Production Deployment
For production use docker-compose.prod.yml. It adds:
- Caddy as a reverse proxy with automatic TLS via Let's Encrypt
- Security headers — HSTS, X-Frame-Options, X-Content-Type-Options, Referrer-Policy
- PostgreSQL and Ollama are not exposed to the host — only reachable inside the Docker network
- App healthcheck (
GET /api/health) wired to Docker restart policy
Requirements
- A server with Docker and Docker Compose
- A domain name pointing to your server (A record)
- Ports 80 and 443 open on your firewall
Step by step
1. Configure .env
bash
cp .env.example .envEdit .env and fill in all CHANGE_ME values:
env
# PostgreSQL — use strong random values
POSTGRES_USER=rag
POSTGRES_PASSWORD=a_strong_random_password
POSTGRES_DB=rag_db
# Caddy — your public domain
DOMAIN=rag.yourdomain.com
# LLM — llama3.1:8b supports tool calling
OLLAMA_LLM_MODEL=llama3.1:8b
# Embeddings — Google is free, recommended
GOOGLE_API_KEY=your_google_api_key
# Admin API — protects /api/admin/* endpoints
ADMIN_API_KEY=a_different_random_secret2. Start
bash
docker compose -f docker-compose.prod.yml up -d --buildCaddy will automatically obtain a TLS certificate for your domain. The first start takes a couple of minutes while Caddy negotiates with Let's Encrypt and Ollama downloads its models.
3. Verify
bash
# App is up and database is reachable
curl https://rag.yourdomain.com/api/health
# → {"status":"ok","checks":{"db":true,"embedding":true},"ts":"..."}
# Container health
docker compose -f docker-compose.prod.yml psArchitecture in production
Internet
│ :443 / :80
▼
┌──────────┐
│ Caddy │ Automatic TLS, gzip, security headers
└──────────┘
│ :3000 (internal only)
▼
┌──────────┐ ┌──────────────┐ ┌────────┐
│ app │──▶│ postgres │ │ ollama │
└──────────┘ └──────────────┘ └────────┘
No host ports No host portsLogs
bash
docker compose -f docker-compose.prod.yml logs -f app
docker compose -f docker-compose.prod.yml logs -f caddyBackup
bash
# Dump the database to a local file
docker compose -f docker-compose.prod.yml exec postgres \
pg_dump -U $POSTGRES_USER $POSTGRES_DB > backup_$(date +%Y%m%d).sqlUpdate
bash
git pull
docker compose -f docker-compose.prod.yml up -d --buildPrisma migrations run automatically on startup.
Using a managed database
To use a managed PostgreSQL service (Railway, Supabase, Neon) instead of the Docker container:
- Remove the
postgresservice fromdocker-compose.prod.yml - Set
DATABASE_URLin.envto the connection string provided by your service - The
appservice will migrate and connect on startup
Customising Caddy
Edit Caddyfile to add rate limiting, basic auth, or a custom error page:
{$DOMAIN} {
reverse_proxy app:3000
encode gzip
# Example: basic auth on admin endpoints
# basicauth /api/admin/* {
# admin JDJhJDE0...
# }
}After editing, reload Caddy without restarting:
bash
docker compose -f docker-compose.prod.yml exec caddy caddy reload --config /etc/caddy/Caddyfile