Update wiki page 'Deployment'

2026-05-15 15:05:42 +00:00
parent ef3fae91bf
commit 1694995df2

@@ -1,15 +1,17 @@
# Deployment
Production deployment uses Docker Compose (`compose.prod.yml`) with four services behind Nginx.
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.
## Services
| Service | Image | Description |
| Service | Binary / 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 |
| `thoughts-frontend` | Built from `thoughts-frontend/Dockerfile` | Next.js SSR frontend |
| `thoughts-backend` | Built from `thoughts-backend/Dockerfile` | Rust API server |
| `database` | `postgres:latest` | PostgreSQL with persistent volume |
## Steps
@@ -20,49 +22,72 @@ git clone <repo-url>
cd thoughts
# Backend env
cp thoughts-backend/.env.example thoughts-backend/.env
# Edit: DATABASE_URL, JWT_SECRET, etc.
# Root env (shared by compose)
cp .env.example .env
# Edit: POSTGRES_USER, POSTGRES_PASSWORD, POSTGRES_DB, domain settings
# Edit: DATABASE_URL, JWT_SECRET, BASE_URL, NATS_URL
```
`BASE_URL` **must** be your public domain (e.g. `https://thoughts.example.com`). ActivityPub IDs are derived from it.
### 2. Build and start
```bash
docker compose -f compose.prod.yml up -d --build
```
### 3. Run migrations
Migrations run automatically when the API container starts. No manual migration step needed.
```bash
docker compose -f compose.prod.yml exec thoughts-backend \
./migration
```
### 3. Nginx / TLS
### 4. Nginx / TLS
`nginx/nginx.conf` routes traffic:
The `nginx/nginx.conf` routes traffic:
- `yourdomain.com/api/*``thoughts-backend:3001`
- `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 a Certbot/Let's Encrypt step or use an external TLS terminator in front of the Nginx container.
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
A Gitea Actions workflow lives at `.gitea/workflows/deploy.yml`. It builds and pushes Docker images on push to `master` and triggers a redeploy on your server.
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
```bash
git pull
docker compose -f compose.prod.yml up -d --build
# Run migrations if schema changed
docker compose -f compose.prod.yml exec thoughts-backend ./migration
```
Migrations run on next API container start.
## Data Persistence
PostgreSQL data is stored in a named Docker volume. It survives container restarts and `docker compose down`. Use `docker compose down -v` only if you intentionally want to wipe the database.
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.
## Environment Variables Reference
| Variable | Required | Description |
|---|---|---|
| `DATABASE_URL` | Yes | PostgreSQL connection string |
| `JWT_SECRET` | Yes | Secret for JWT signing |
| `BASE_URL` | Yes | Public base URL (used for AP IDs) |
| `NATS_URL` | No | NATS connection (federation/notifications) |