Files
LedgerLine/docs/roadmap/phase-1.md
T
Movses AslanyanandClaude Opus 5.5 34a11da848 Document deploy.sh and where the observability platform lives
phase-1: deploy via deploy.sh, correct the container count, and note
that stripped host ports leave no path for the Phase 5 scrape.
phase-2: explain why Grafana is not on the Ledgerline server and why
Cloudflare's proxy never forwards :3000 or the other platform ports.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 11:01:49 +03:30

5.0 KiB
Raw Blame History

Phase 1 — Deployment: Ledgerline on a server behind Traefik

Part of docs/roadmap/ — see intro.md for platform/background context and summary.md for how every phase fits together. Prev: phase-0.md · Next: phase-2.md

Status: Done

Message: Ledgerline is running on a remote server and served over HTTPS at https://ledgerline.vione.cloud. Verified against the live deployment, not just the config: the frontend loads, /api/health/all reports every service ok, and the origin serves a real Let's Encrypt certificate (valid until 2027-01-07, auto-renewed by Traefik).

  • Target: the throwaway server czy (Ubuntu 24.04, 1 vCPU, 1 GB RAM), repo at /opt/apps/LedgerLine. Docker 29.9 and Compose v5.6 installed from Docker's apt repo. A 2 GB swapfile was added (persisted in /etc/fstab) — ten containers (nine Ledgerline + Traefik) do not fit in 1 GB of RAM without it, and steady state still uses roughly 600–700 MB plus swap. There is no headroom for the observability platform on this host.

  • How it's wired: docker-compose.traefik.yml is an override, not an edit of docker-compose.yml, so local development is unchanged. It adds Traefik v3.6 on ports 80/443 (Docker provider, exposedByDefault=false, HTTP→HTTPS redirect, Let's Encrypt via HTTP-01), routes Host(`ledgerline.vione.cloud`) to the gateway on port 8000, and strips the published host ports from every other service (ports: !reset []) so Postgres, Redis and the services are reachable only on the Compose network.

  • No application or tests/ changes. The Dockerfile and base compose file are untouched; schema applied and data seeded on the server with the same steps as make seed (800 accounts, 250,000 transactions).

  • Deploy / redeploy: run ./deploy.sh from your own machine. It SSHes to czy (override with DEPLOY_HOST / DEPLOY_DIR), does git pull --ff-only in /opt/apps/LedgerLine, and runs the compose command below. The server pulls from the remote, so push first; the script warns about unpushed commits. The underlying command, if run by hand on the server (always both files — a bare docker compose up would use the base file alone and publish every port):

    docker compose -f docker-compose.yml -f docker-compose.traefik.yml up -d --build
    
  • Cloudflare gotchas hit on the way:

    • The first certificate attempt failed with an ACME 404 because the Cloudflare DNS record did not yet point at the server — a packet capture on the server showed no inbound traffic from Cloudflare at all. Traefik only retries issuance on restart, so after fixing the record it needed docker compose ... restart traefik.
    • With the orange-cloud proxy on, the SSL/TLS mode must be Full — Flexible combined with Traefik's HTTPS redirect causes a redirect loop.
  • Caveats to remember: the deployment is public, including the /api/admin/chaos endpoints (anyone who finds them can flip flags); internet scanners probed the site within seconds of it going live. The server is disposable by design, and Traefik's state lives in the letsencrypt volume, which is lost on an OS reinstall (a new certificate is simply issued on the next run).

  • Consequence for later phases: because !reset strips every service's host port, nothing outside czy can reach a service's /metrics directly. The Phase 5 scrape (Prometheus on the observability host → prometheus_extra_scrape_jobs) needs a deliberate path in — e.g. a firewalled port or a Traefik route — decided before Phase 5.

Time-box: 1 evening.

Deliverable: the unmodified-behaviour Ledgerline system reachable at a real domain over HTTPS, reproducible with one compose command.

Concepts

  • Traefik's model: entrypoints (the ports it listens on), routers (rule → service), services (where to send traffic), middlewares, and providers (where config comes from — here, Docker labels on containers). Compare with nginx, where the same information lives in one static config file.
  • Compose override files. -f base -f override merges the files; !reset clears an inherited key (here, ports) instead of appending to it.
  • ACME HTTP-01 behind a CDN. Let's Encrypt must be able to reach port 80 on the hostname's actual origin. With Cloudflare proxying, that only works once the DNS record points at the right server.
  • Why this phase exists before observability. The telemetry work in Phases 2–11 is more convincing when the system under observation is a real deployment rather than a laptop, and the platform (observability-ansible) is already a remote deployment — Ledgerline now sits on the same footing.

Done when: https://ledgerline.vione.cloud shows the frontend with a valid certificate; /api/health/all is all ok; only ports 22, 80 and 443 are reachable on the server; and the deploy is one documented compose command.