12 KiB
Docker Compose Guide
This guide explains the Docker Compose setup for sexy.pivoine.art with local development and production configurations.
Architecture Overview
The application uses a multi-file compose setup with two configurations:
compose.yml- Base configuration for local developmentcompose.production.yml- Production overrides with Traefik integration
Service Architecture
┌─────────────────────────────────────────────────────────────┐
│ 🌐 Traefik Reverse Proxy (Production Only) │
│ ├─ HTTPS Termination │
│ ├─ Automatic Let's Encrypt │
│ └─ Routes traffic to frontend & Directus API │
├─────────────────────────────────────────────────────────────┤
│ 💄 Frontend (SvelteKit) │
│ ├─ Port 3000 (internal) │
│ ├─ Serves on https://sexy.pivoine.art │
│ └─ Proxies /api to Directus │
├─────────────────────────────────────────────────────────────┤
│ 🎭 Directus CMS │
│ ├─ Port 8055 (internal) │
│ ├─ Serves on https://sexy.pivoine.art/api │
│ ├─ Custom bundle extensions mounted │
│ └─ Uploads volume │
├─────────────────────────────────────────────────────────────┤
│ 🗄️ PostgreSQL (Local) / External (Production) │
│ └─ Database for Directus │
├─────────────────────────────────────────────────────────────┤
│ 💾 Redis (Local) / External (Production) │
│ └─ Cache & session storage │
└─────────────────────────────────────────────────────────────┘
Local Development Setup
Prerequisites
- Docker 20.10+
- Docker Compose 2.0+
Quick Start
- Create environment file:
cp .env.example .env
# Edit .env with your local settings (defaults work fine)
- Start all services:
docker-compose up -d
-
Access services:
- Frontend: http://localhost:3000 (if enabled)
- Directus: http://localhost:8055
- Directus Admin: http://localhost:8055/admin
-
View logs:
docker-compose logs -f
- Stop services:
docker-compose down
Local Services
PostgreSQL
- Image:
postgres:16-alpine - Port: 5432 (internal only)
- Volume:
postgres-data - Database:
sexy
Redis
- Image:
redis:7-alpine - Port: 6379 (internal only)
- Volume:
redis-data - Persistence: AOF enabled
Directus
- Image:
directus/directus:11 - Port: 8055 (exposed)
- Volumes:
directus-uploads- File uploads./packages/bundle/dist- Custom extensions
- Features:
- Auto-reload extensions
- WebSockets enabled
- CORS enabled for localhost
Local Development Workflow
# Start infrastructure (Postgres, Redis, Directus)
docker-compose up -d
# Develop frontend locally with hot reload
cd packages/frontend
pnpm dev
# Build Directus bundle
pnpm --filter @sexy.pivoine.art/bundle build
# Restart Directus to load new bundle
docker-compose restart directus
Production Deployment
Prerequisites
- External PostgreSQL database
- External Redis instance
- Traefik reverse proxy configured
- External network:
compose_network
Setup
The production compose file now uses the include directive to automatically extend compose.yml, making deployment simpler.
- Create production environment file:
cp .env.production.example .env.production
- Edit
.env.productionwith your values:
# Database (external)
CORE_DB_HOST=your-postgres-host
SEXY_DB_NAME=sexy_production
DB_USER=sexy
DB_PASSWORD=your-secure-password
# Redis (external)
CORE_REDIS_HOST=your-redis-host
# Directus
SEXY_DIRECTUS_SECRET=your-32-char-random-secret
ADMIN_PASSWORD=your-secure-admin-password
# Traefik
SEXY_TRAEFIK_HOST=sexy.pivoine.art
# Frontend
PUBLIC_API_URL=https://sexy.pivoine.art/api
PUBLIC_URL=https://sexy.pivoine.art
# Email (SMTP)
EMAIL_SMTP_HOST=smtp.your-provider.com
EMAIL_SMTP_USER=your-email@domain.com
EMAIL_SMTP_PASSWORD=your-smtp-password
- Deploy:
# Simple deployment - compose.production.yml includes compose.yml automatically
docker-compose -f compose.production.yml --env-file .env.production up -d
# Or use the traditional multi-file approach (same result)
docker-compose -f compose.yml -f compose.production.yml --env-file .env.production up -d
Production Services
Directus
- Image:
directus/directus:11(configurable) - Network:
compose_network(external) - Volumes:
/var/www/sexy.pivoine.art/uploads- Persistent uploads/var/www/sexy.pivoine.art/packages/bundle/dist- Extensions
- Traefik routing:
- Domain:
sexy.pivoine.art/api - Strips
/apiprefix before forwarding - HTTPS with auto-certificates
- Domain:
Frontend
- Image:
ghcr.io/valknarxxx/sexy:latest(from GHCR) - Network:
compose_network(external) - Volume:
/var/www/sexy.pivoine.art- Application code - Traefik routing:
- Domain:
sexy.pivoine.art - HTTPS with auto-certificates
- Domain:
Traefik Integration
Both services are configured with Traefik labels for automatic routing:
Frontend:
- HTTP → HTTPS redirect
- Routes
sexy.pivoine.artto port 3000 - Gzip compression enabled
Directus API:
- HTTP → HTTPS redirect
- Routes
sexy.pivoine.art/apito port 8055 - Strips
/apiprefix - Gzip compression enabled
Production Commands
# Deploy/update (simplified - uses include)
docker-compose -f compose.production.yml --env-file .env.production up -d
# View logs
docker-compose -f compose.production.yml logs -f
# Restart specific service
docker-compose -f compose.production.yml restart frontend
# Stop all services
docker-compose -f compose.production.yml down
# Update images
docker-compose -f compose.production.yml pull
docker-compose -f compose.production.yml up -d
Environment Variables
Local Development (.env)
| Variable | Default | Description |
|---|---|---|
DB_DATABASE |
sexy |
Database name |
DB_USER |
sexy |
Database user |
DB_PASSWORD |
sexy |
Database password |
DIRECTUS_SECRET |
- | Secret for Directus (min 32 chars) |
ADMIN_EMAIL |
admin@sexy.pivoine.art |
Admin email |
ADMIN_PASSWORD |
admin |
Admin password |
CORS_ORIGIN |
http://localhost:3000 |
CORS allowed origins |
See .env.example for full list.
Production (.env.production)
| Variable | Description | Required |
|---|---|---|
CORE_DB_HOST |
External PostgreSQL host | ✅ |
SEXY_DB_NAME |
Database name | ✅ |
DB_PASSWORD |
Database password | ✅ |
CORE_REDIS_HOST |
External Redis host | ✅ |
SEXY_DIRECTUS_SECRET |
Directus secret key | ✅ |
SEXY_TRAEFIK_HOST |
Domain name | ✅ |
EMAIL_SMTP_HOST |
SMTP server | ✅ |
EMAIL_SMTP_PASSWORD |
SMTP password | ✅ |
SEXY_FRONTEND_PUBLIC_API_URL |
Frontend API URL | ✅ |
SEXY_FRONTEND_PUBLIC_URL |
Frontend public URL | ✅ |
See .env.production.example for full list.
Note: All frontend-specific variables are prefixed with SEXY_FRONTEND_ for clarity.
Volumes
Local Development
postgres-data- PostgreSQL databaseredis-data- Redis persistencedirectus-uploads- Uploaded files
Production
/var/www/sexy.pivoine.art/uploads- Directus uploads/var/www/sexy.pivoine.art- Application code (frontend)
Networks
Local: sexy-network
- Bridge network
- Internal communication only
- Directus exposed on 8055
Production: compose_network
- External network (pre-existing)
- Connects to Traefik
- No exposed ports (Traefik handles routing)
Health Checks
All services include health checks:
PostgreSQL:
- Command:
pg_isready - Interval: 10s
Redis:
- Command:
redis-cli ping - Interval: 10s
Directus:
- Endpoint:
/server/health - Interval: 30s
Frontend:
- HTTP GET:
localhost:3000 - Interval: 30s
Troubleshooting
Local Development
Problem: Directus won't start
# Check logs
docker-compose logs directus
# Common issues:
# 1. Database not ready - wait for postgres to be healthy
# 2. Wrong secret - check DIRECTUS_SECRET is at least 32 chars
Problem: Can't connect to database
# Check if postgres is running
docker-compose ps postgres
# Verify health
docker-compose exec postgres pg_isready -U sexy
Problem: Extensions not loading
# Rebuild bundle
pnpm --filter @sexy.pivoine.art/bundle build
# Verify volume mount
docker-compose exec directus ls -la /directus/extensions/
# Restart Directus
docker-compose restart directus
Production
Problem: Services not accessible via domain
# Check Traefik labels
docker inspect sexy_frontend | grep traefik
# Verify compose_network exists
docker network ls | grep compose_network
# Check Traefik is running
docker ps | grep traefik
Problem: Can't connect to external database
# Test connection from Directus container
docker-compose exec directus sh
apk add postgresql-client
psql -h $CORE_DB_HOST -U $DB_USER -d $SEXY_DB_NAME
Problem: Frontend can't reach Directus API
# Check Directus is accessible
curl https://sexy.pivoine.art/api/server/health
# Verify CORS settings
# PUBLIC_API_URL should match the public Directus URL
Migration from Old Setup
If migrating from docker-compose.production.yml:
- Rename environment variables according to
.env.production.example - Update command to use both compose files
- Verify Traefik labels match your setup
- Test with
docker-compose configto see merged configuration
# Validate configuration
docker-compose -f compose.yml -f compose.production.yml --env-file .env.production config
# Deploy
docker-compose -f compose.yml -f compose.production.yml --env-file .env.production up -d
Best Practices
Local Development
- Use default credentials (they're fine for local)
- Keep
EXTENSIONS_AUTO_RELOAD=truefor quick iteration - Run frontend via
pnpm devfor hot reload - Restart Directus after bundle changes
Production
- Use strong passwords for database and admin
- Set
EXTENSIONS_AUTO_RELOAD=falsefor stability - Use GHCR images for frontend
- Enable Gzip compression via Traefik
- Monitor logs regularly
- Keep backups of uploads and database
See Also
- DOCKER.md - Docker image documentation
- QUICKSTART.md - Quick start guide
- CLAUDE.md - Development guide