2026-10-09 15:38:29 +03:30
# Phase 1 — Deployment: Ledgerline on a server behind Traefik
2026-09-05 08:11:00 +03:30
*Part of `docs/roadmap/` — see [`intro.md`](intro.md) for platform/background context and [`summary.md`](summary.md) for how every phase fits together. Prev: [`phase-0.md`](phase-0.md) · Next: [`phase-2.md`](phase-2.md)*
2026-10-09 15:38:29 +03:30
**Status:** Done
2026-09-05 08:11:00 +03:30
2026-10-09 15:38:29 +03:30
**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).
2026-09-05 08:11:00 +03:30
2026-10-09 15:38:29 +03:30
- **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` ) —
nine containers do not fit in 1 GB of RAM without it, and steady state still
uses roughly 700 MB plus swap.
- **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 command** (always both files — a bare ` docker compose up`
would use the base file alone and publish every port):
2026-09-05 08:11:00 +03:30
2026-10-09 15:38:29 +03:30
` ``bash
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).
**Time-box:** 1 evening.
**Deliverable:** the unmodified-behaviour Ledgerline system reachable at a real
domain over HTTPS, reproducible with one compose command.
2026-09-05 08:11:00 +03:30
### Concepts
2026-10-09 15:38:29 +03:30
- **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.
2026-09-05 08:11:00 +03:30
2026-10-09 15:38:29 +03:30
**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.