425 lines
12 KiB
Markdown
425 lines
12 KiB
Markdown
|
|
# 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:
|
||
|
|
|
||
|
|
1. **`compose.yml`** - Base configuration for local development
|
||
|
|
2. **`compose.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
|
||
|
|
|
||
|
|
1. **Create environment file:**
|
||
|
|
|
||
|
|
```bash
|
||
|
|
cp .env.example .env
|
||
|
|
# Edit .env with your local settings (defaults work fine)
|
||
|
|
```
|
||
|
|
|
||
|
|
2. **Start all services:**
|
||
|
|
|
||
|
|
```bash
|
||
|
|
docker-compose up -d
|
||
|
|
```
|
||
|
|
|
||
|
|
3. **Access services:**
|
||
|
|
- Frontend: http://localhost:3000 (if enabled)
|
||
|
|
- Directus: http://localhost:8055
|
||
|
|
- Directus Admin: http://localhost:8055/admin
|
||
|
|
|
||
|
|
4. **View logs:**
|
||
|
|
|
||
|
|
```bash
|
||
|
|
docker-compose logs -f
|
||
|
|
```
|
||
|
|
|
||
|
|
5. **Stop services:**
|
||
|
|
|
||
|
|
```bash
|
||
|
|
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
|
||
|
|
|
||
|
|
```bash
|
||
|
|
# 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.
|
||
|
|
|
||
|
|
1. **Create production environment file:**
|
||
|
|
|
||
|
|
```bash
|
||
|
|
cp .env.production.example .env.production
|
||
|
|
```
|
||
|
|
|
||
|
|
2. **Edit `.env.production` with your values:**
|
||
|
|
|
||
|
|
```bash
|
||
|
|
# 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
|
||
|
|
```
|
||
|
|
|
||
|
|
3. **Deploy:**
|
||
|
|
|
||
|
|
```bash
|
||
|
|
# 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 `/api` prefix before forwarding
|
||
|
|
- HTTPS with auto-certificates
|
||
|
|
|
||
|
|
#### 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
|
||
|
|
|
||
|
|
### Traefik Integration
|
||
|
|
|
||
|
|
Both services are configured with Traefik labels for automatic routing:
|
||
|
|
|
||
|
|
**Frontend:**
|
||
|
|
- HTTP → HTTPS redirect
|
||
|
|
- Routes `sexy.pivoine.art` to port 3000
|
||
|
|
- Gzip compression enabled
|
||
|
|
|
||
|
|
**Directus API:**
|
||
|
|
- HTTP → HTTPS redirect
|
||
|
|
- Routes `sexy.pivoine.art/api` to port 8055
|
||
|
|
- Strips `/api` prefix
|
||
|
|
- Gzip compression enabled
|
||
|
|
|
||
|
|
### Production Commands
|
||
|
|
|
||
|
|
```bash
|
||
|
|
# 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 database
|
||
|
|
- `redis-data` - Redis persistence
|
||
|
|
- `directus-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
|
||
|
|
|
||
|
|
```bash
|
||
|
|
# 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
|
||
|
|
|
||
|
|
```bash
|
||
|
|
# Check if postgres is running
|
||
|
|
docker-compose ps postgres
|
||
|
|
|
||
|
|
# Verify health
|
||
|
|
docker-compose exec postgres pg_isready -U sexy
|
||
|
|
```
|
||
|
|
|
||
|
|
**Problem:** Extensions not loading
|
||
|
|
|
||
|
|
```bash
|
||
|
|
# 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
|
||
|
|
|
||
|
|
```bash
|
||
|
|
# 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
|
||
|
|
|
||
|
|
```bash
|
||
|
|
# 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
|
||
|
|
|
||
|
|
```bash
|
||
|
|
# 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`:
|
||
|
|
|
||
|
|
1. **Rename environment variables** according to `.env.production.example`
|
||
|
|
2. **Update command** to use both compose files
|
||
|
|
3. **Verify Traefik labels** match your setup
|
||
|
|
4. **Test** with `docker-compose config` to see merged configuration
|
||
|
|
|
||
|
|
```bash
|
||
|
|
# 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
|
||
|
|
1. Use default credentials (they're fine for local)
|
||
|
|
2. Keep `EXTENSIONS_AUTO_RELOAD=true` for quick iteration
|
||
|
|
3. Run frontend via `pnpm dev` for hot reload
|
||
|
|
4. Restart Directus after bundle changes
|
||
|
|
|
||
|
|
### Production
|
||
|
|
1. Use strong passwords for database and admin
|
||
|
|
2. Set `EXTENSIONS_AUTO_RELOAD=false` for stability
|
||
|
|
3. Use GHCR images for frontend
|
||
|
|
4. Enable Gzip compression via Traefik
|
||
|
|
5. Monitor logs regularly
|
||
|
|
6. Keep backups of uploads and database
|
||
|
|
|
||
|
|
## See Also
|
||
|
|
|
||
|
|
- [DOCKER.md](DOCKER.md) - Docker image documentation
|
||
|
|
- [QUICKSTART.md](QUICKSTART.md) - Quick start guide
|
||
|
|
- [CLAUDE.md](CLAUDE.md) - Development guide
|