Deployment
Deploy Enclavr to production.
Quick Start
Deploy Enclavr using Docker Compose, or use Neon (serverless PostgreSQL) for the database.
Option 1: Neon PostgreSQL (Recommended)
Neon is the default database - create a project at neon.tech:
- Create a Neon project at neon.tech
- Get your connection string from the Neon dashboard
- Set
NEON_CONNECTION_STRINGenvironment variable:NEON_CONNECTION_STRING=postgres://user:password@host.neon.tech/neondb?sslmode=require - Run Redis with Docker:
docker run -d --name enclavr-redis -p 6379:6379 redis:8
Option 2: Docker Compose
# Clone with submodules
git clone --recurse-submodules https://github.com/enclavr/enclavr.git
cd enclavr/infra
# Copy environment file
cp .env.example .env
# Edit .env with your values
# Start services
docker compose up -d
Production Checklist
- Change
JWT_SECRETto a secure random string - Set strong
DB_PASSWORD - Configure
REDIS_PASSWORD - Set up SSL/TLS with Caddy (automatic HTTPS) or reverse proxy
- Configure TURN server credentials
- Set up regular database backups
- Enable monitoring profile for observability
- PostgreSQL 18+: Set
PGDATA: /var/lib/postgresql/18/dockerenvironment variable (volume path changed from older versions)
Docker Compose Profiles
Enclavr uses Docker Compose profiles for flexible deployment. Use the Makefile shortcuts:
# Core services only (default)
make up
# With monitoring (Prometheus, Grafana, Loki, Alertmanager)
make monitoring
# With voice (Coturn TURN server)
make voice
# With automated backups
make backup
# With automatic HTTPS (Caddy)
make tls
# With object storage (MinIO)
make storage
# Full stack (all profiles)
make full
# Production (full + TLS)
make prod
# Development mode (hot reload)
make dev
All Makefile Targets
| Target | Description |
|---|---|
make help | Show all available commands |
make up | Start core services |
make down | Stop all services |
make restart | Restart all services |
make build | Build all images (no-cache) |
make dev | Start in development mode (hot reload) |
make prod | Start in production mode (full + TLS) |
make full | Start with all profiles |
make monitoring | Start with monitoring stack |
make voice | Start with Coturn TURN server |
make backup | Start with automated backups |
make tls | Start with Caddy automatic HTTPS |
make storage | Start with MinIO object storage |
make validate | Validate docker-compose configuration |
make status | Show service status |
make logs | Follow logs for core services |
make health | Check health of all services |
make env-check | Validate environment variables |
make db-shell | Open PostgreSQL shell |
make redis-shell | Open Redis CLI |
make clean | Remove stopped containers and unused volumes |
make update-images | Pull latest images and recreate |
make pg-backup | Create manual database backup |
make pg-restore | Restore database from backup file |
Development Mode
The docker-compose.override.yml file is automatically loaded by Docker Compose when present. Use make dev to start in development mode.
Production Overrides
A docker-compose.prod.yml file provides production-specific overrides. It is not automatically loaded — you must explicitly include it:
# Start with production overrides
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d
# Or use the Makefile (make prod uses --profile full --profile tls)
make prod
Production Override Settings
| Setting | Value | Description |
|---|---|---|
SENTRY_ENVIRONMENT | production | Sentry environment tag |
DB_SSLMODE | require | Enforce SSL for database connections |
NODE_ENV | production | Node.js production mode |
| Frontend port | 3000:80 | Maps container port 80 to host port 3000 |
Development Features
| Feature | Details |
|---|---|
| Server hot reload | Source code mounted from ../server, runs with go run ./cmd/server |
| Frontend hot reload | Source code mounted from ../frontend, runs with bun run dev |
| Go module cache | Persistent server_go_cache volume for faster rebuilds |
| Node modules cache | Persistent frontend_node_modules volume |
| Next.js cache | Persistent frontend_next volume |
| Delve debugger | Server debugger on port 2345 (localhost only) |
| PostgreSQL access | Port 5432 exposed to localhost |
| Redis access | Port 6379 exposed to localhost |
| NODE_ENV | Set to development |
| DB_SSLMODE | Set to disable |
| Sentry | Disabled (empty DSN) |
Automatic HTTPS with Caddy
Caddy provides automatic HTTPS via Let's Encrypt:
# In .env, set:
DOMAIN=enclavr.example.com
ACME_EMAIL=admin@example.com
MONITORING_DOMAIN=monitoring.example.com
GRAFANA_BASIC_AUTH_USER=admin
GRAFANA_BASIC_AUTH_HASH=... # htpasswd hash
# Start with TLS profile
make tls
Caddy features:
- Automatic Let's Encrypt certificate management
- API subdomain routing (
api.{DOMAIN}) with separate reverse proxy to server:8080 - Monitoring subdomain (
{MONITORING_DOMAIN}) with basic auth protection, proxying to Grafana - WebSocket upgrade support for voice signaling (automatic with
reverse_proxy) - API rate limiting (requires custom Caddy build with
caddy-ratelimitmodule; disabled by default) - Security headers (HSTS, X-Content-Type-Options, X-Frame-Options, Referrer-Policy, Permissions-Policy)
- Path blocking for sensitive files (
/.git*,/.env*,/.docker*,/docker-compose*) - Gzip compression
- QUIC/HTTP3 support (port 443/udp)
- ACME staging CA available (commented out) for testing
Caddy Subdomain Routing
| Subdomain | Target | Features |
|---|---|---|
{DOMAIN} | frontend:80 | Static files, security headers, hidden file blocking |
api.{DOMAIN} | server:8080 | REST API, WebSocket upgrade, rate limiting |
{MONITORING_DOMAIN} | grafana:3000 | Basic auth protected, monitoring dashboard |
Automatic Container Updates
Watchtower automatically updates running containers:
# Enable the maintenance profile
docker compose --profile maintenance up -d
| Variable | Default | Description |
|---|---|---|
WATCHTOWER_POLL_INTERVAL | 86400 (24h) | Check interval in seconds |
WATCHTOWER_LOG_LEVEL | info | Log verbosity |
Containers with the com.centurylinklabs.watchtower.enable=true label are eligible for auto-updates.
Database Backups
Automated backups run via the backup Docker Compose profile:
# Enable automated backups (daily at 2 AM by default)
make backup
# Manual backup
make pg-backup
# Restore from backup
make pg-restore FILE=backup.sql.gz
| Variable | Default | Description |
|---|---|---|
BACKUP_CRON | 0 2 * * * | Backup schedule (cron format) |
BACKUP_RETENTION_DAYS | 7 | Days to keep backups |
Volumes marked for backup: postgres_data, server_uploads, grafana_data, backup_data.
Alternative Reverse Proxy
If you prefer nginx or another reverse proxy instead of Caddy:
server {
listen 443 ssl;
server_name enclavr.example.com;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
location / {
proxy_pass http://frontend:3000;
}
location /api/ {
proxy_pass http://server:8080;
}
location /ws {
proxy_pass http://server:8080;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}