Update wiki page 'Deployment'
@@ -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`) |
|
||||||
|
|||||||
Reference in New Issue
Block a user