Update wiki page 'Deployment'

2026-05-15 15:17:05 +00:00
parent 5b28d527cc
commit 3b97d6f5e4

@@ -1,17 +1,27 @@
# Deployment # 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 ## Services
| Service | Binary / Image | Role | | Service | Image | Role |
|---|---|---| |---|---|---|
| `thoughts` | `thoughts` (from `Dockerfile`) | API server on port 8000 | | `api` | `registry.gabrielkaszewski.dev/thoughts:latest` | API server on port 8000 |
| `thoughts-worker` | `thoughts-worker` (same image, different entrypoint) | Event consumer — AP fan-out, notifications | | `worker` | Same image, entrypoint `./thoughts-worker` | Event consumer — AP fan-out, notifications |
| `thoughts-frontend` | `thoughts-frontend/Dockerfile` | Next.js SSR | | `frontend` | `registry.gabrielkaszewski.dev/thoughts-frontend:latest` | Next.js SSR on port 3000 |
| `database` | `postgres:15` | PostgreSQL with persistent volume | | `database` | `postgres:16-alpine` | PostgreSQL with persistent volume |
| `nats` | `nats:latest` | NATS JetStream |
| `proxy` | Nginx (custom) | TLS termination, reverse proxy | 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 ## Steps
@@ -21,73 +31,51 @@ Production deployment uses `compose.prod.yml` with Docker Compose. The backend D
git clone <repo-url> git clone <repo-url>
cd thoughts cd thoughts
# Backend env
cp .env.example .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 ```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` The `api` container is routed to `api.<your-domain>`, the `frontend` to `<your-domain>`.
- `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` |
## Updating ## Updating
```bash ```bash
git pull docker compose -f compose.prod.yml pull
docker compose -f compose.prod.yml up -d --build 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 ## 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 ## Environment Variables Reference
| Variable | Required | Description | | 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 | | `JWT_SECRET` | Yes | Secret for JWT signing |
| `BASE_URL` | Yes | Public base URL (used for AP IDs) | | `BASE_URL` | Yes | Public API base URL (used for AP IDs) |
| `NATS_URL` | No | NATS connection (federation/notifications) | | `CORS_ORIGINS` | No | Allowed CORS origins (default: `*`) |
| `ALLOW_REGISTRATION` | No | Enable public registration (default: `false`) |