From 3b97d6f5e4071893a141f84807befa334c771dc4 Mon Sep 17 00:00:00 2001 From: Gabriel Kaszewski Date: Fri, 15 May 2026 15:17:05 +0000 Subject: [PATCH] Update wiki page 'Deployment' --- Deployment.md | 92 ++++++++++++++++++++++----------------------------- 1 file changed, 40 insertions(+), 52 deletions(-) diff --git a/Deployment.md b/Deployment.md index e88d179..3977cd7 100644 --- a/Deployment.md +++ b/Deployment.md @@ -1,17 +1,27 @@ # Deployment -Production deployment uses `compose.prod.yml` with Docker Compose. The backend Docker image contains both binaries (`thoughts` API server and `thoughts-worker`); they run as separate containers from the same image. +Production deployment uses `compose.prod.yml`. **Traefik** is used as the reverse proxy (expected to be running as a shared external service on the host). No Nginx is involved. + +API and frontend are exposed on separate subdomains via Traefik labels with automatic Let's Encrypt TLS. ## Services -| Service | Binary / Image | Role | +| Service | Image | Role | |---|---|---| -| `thoughts` | `thoughts` (from `Dockerfile`) | API server on port 8000 | -| `thoughts-worker` | `thoughts-worker` (same image, different entrypoint) | Event consumer — AP fan-out, notifications | -| `thoughts-frontend` | `thoughts-frontend/Dockerfile` | Next.js SSR | -| `database` | `postgres:15` | PostgreSQL with persistent volume | -| `nats` | `nats:latest` | NATS JetStream | -| `proxy` | Nginx (custom) | TLS termination, reverse proxy | +| `api` | `registry.gabrielkaszewski.dev/thoughts:latest` | API server on port 8000 | +| `worker` | Same image, entrypoint `./thoughts-worker` | Event consumer — AP fan-out, notifications | +| `frontend` | `registry.gabrielkaszewski.dev/thoughts-frontend:latest` | Next.js SSR on port 3000 | +| `database` | `postgres:16-alpine` | PostgreSQL with persistent volume | + +NATS runs as a shared external service (`k_nats`) — not managed by this compose file in production. + +## Networks + +| Network | Type | Purpose | +|---|---|---| +| `internal` | bridge | DB ↔ API ↔ worker (isolated) | +| `shared-services` | external | Access to shared infra (NATS, etc.) | +| `traefik` | external | Traefik can reach the containers | ## Steps @@ -21,73 +31,51 @@ Production deployment uses `compose.prod.yml` with Docker Compose. The backend D git clone cd thoughts -# Backend env cp .env.example .env -# Edit: DATABASE_URL, JWT_SECRET, BASE_URL, NATS_URL +# Edit: POSTGRES_USER, POSTGRES_PASSWORD, POSTGRES_DB, +# JWT_SECRET, BASE_URL, CORS_ORIGINS, ALLOW_REGISTRATION ``` -`BASE_URL` **must** be your public domain (e.g. `https://thoughts.example.com`). ActivityPub IDs are derived from it. +`BASE_URL` **must** be the public API domain (e.g. `https://api.thoughts.example.com`). ActivityPub IDs are derived from it. -### 2. Build and start +### 2. Pull images and start ```bash -docker compose -f compose.prod.yml up -d --build +docker compose -f compose.prod.yml pull +docker compose -f compose.prod.yml up -d ``` -Migrations run automatically when the API container starts. No manual migration step needed. +Migrations run automatically when the `api` container starts. -### 3. Nginx / TLS +## Traefik Routing -`nginx/nginx.conf` routes traffic: +Routing is configured via Docker labels on each container. Traefik must be running with: +- A `traefik` Docker network +- A `letsencrypt` cert resolver configured -- `yourdomain.com/api/*` → `thoughts:8000` -- `yourdomain.com/.well-known/*` → `thoughts:8000` (WebFinger, NodeInfo) -- `yourdomain.com/users/*/inbox` → `thoughts:8000` (AP inbox) -- `yourdomain.com/*` → `thoughts-frontend:3000` - -For TLS, add Certbot/Let's Encrypt or put a TLS-terminating reverse proxy in front. - -### 4. Frontend build args - -The frontend image requires build-time env args: - -```bash -docker build -t thoughts-frontend \ - --build-arg NEXT_PUBLIC_API_URL=https://thoughts.example.com \ - --build-arg NEXT_PUBLIC_SERVER_SIDE_API_URL=http://thoughts:8000 \ - thoughts-frontend/ -``` - -`docker compose -f compose.prod.yml` handles this automatically if the args are in the compose file. - -## CI/CD - -Gitea Actions workflows in `.gitea/workflows/`: - -| Workflow | Trigger | Action | -|---|---|---| -| `deploy.yml` | Push to `master` | Build images, push to registry, redeploy | -| `lint.yml` | PR / push | `cargo clippy` + `cargo fmt --check` | -| `test.yml` | PR / push | `cargo test --workspace` | +The `api` container is routed to `api.`, the `frontend` to ``. ## Updating ```bash -git pull -docker compose -f compose.prod.yml up -d --build +docker compose -f compose.prod.yml pull +docker compose -f compose.prod.yml up -d ``` -Migrations run on next API container start. +Migrations run on next API container start — no manual step needed. ## Data Persistence -PostgreSQL data lives in a named Docker volume. Safe across `docker compose down` and rebuilds. Only destroyed with `docker compose down -v` — use with caution. +PostgreSQL data lives in a named Docker volume (`postgres_data`). Safe across `docker compose down` and image updates. Only destroyed with `docker compose down -v`. ## Environment Variables Reference | Variable | Required | Description | |---|---|---| -| `DATABASE_URL` | Yes | PostgreSQL connection string | +| `POSTGRES_USER` | Yes | PostgreSQL username | +| `POSTGRES_PASSWORD` | Yes | PostgreSQL password | +| `POSTGRES_DB` | Yes | PostgreSQL database name | | `JWT_SECRET` | Yes | Secret for JWT signing | -| `BASE_URL` | Yes | Public base URL (used for AP IDs) | -| `NATS_URL` | No | NATS connection (federation/notifications) | +| `BASE_URL` | Yes | Public API base URL (used for AP IDs) | +| `CORS_ORIGINS` | No | Allowed CORS origins (default: `*`) | +| `ALLOW_REGISTRATION` | No | Enable public registration (default: `false`) |