deployment/production/docker-compose.production.yml
Rama main · réplica de lectura del repositorio original.
version: "3.8"
services:
# API Service
janua-api:
image: janua/api:latest
container_name: janua-api
restart: unless-stopped
environment:
- ENVIRONMENT=production
- DATABASE_URL=${DATABASE_URL}
- REDIS_URL=${REDIS_URL}
- JWT_SECRET_KEY=${JWT_SECRET_KEY}
- SECRET_KEY=${SECRET_KEY}
- SENDGRID_API_KEY=${SENDGRID_API_KEY}
- SENTRY_DSN=${SENTRY_DSN}
# Database SSL Configuration
- DATABASE_SSL_MODE=${DATABASE_SSL_MODE:-require}
- DATABASE_SSL_CA_FILE=/etc/ssl/certs/db-ca.crt
# Email (Resend) - Centralized for all MADFAM apps
- RESEND_API_KEY=${RESEND_API_KEY}
# Internal API - Service-to-service communication for MADFAM apps
- INTERNAL_API_KEY=${INTERNAL_API_KEY}
volumes:
# Mount database CA certificate for SSL verification
- ./ssl/db/ca.crt:/etc/ssl/certs/db-ca.crt:ro
ports:
- "4100:4100" # MADFAM standard: 4100 = Janua API
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:4100/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy
networks:
- janua-network
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
deploy:
resources:
limits:
cpus: "2.0"
memory: 2G
reservations:
cpus: "1.0"
memory: 1G
# Database with SSL enabled
postgres:
image: postgres:15-alpine
container_name: janua-postgres
restart: unless-stopped
environment:
POSTGRES_DB: ${POSTGRES_DB:-janua}
POSTGRES_USER: ${POSTGRES_USER:-janua}
POSTGRES_PASSWORD: "${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD in an untracked local env file}"
POSTGRES_INITDB_ARGS: "--auth-host=scram-sha-256 --auth-local=scram-sha-256"
ports:
- "5432:5432"
volumes:
- postgres_data:/var/lib/postgresql/data
- ./postgresql.conf:/etc/postgresql/postgresql.conf
- ./pg_hba.conf:/etc/postgresql/pg_hba.conf
# SSL certificates for encrypted connections
- ./ssl/db/server.crt:/var/lib/postgresql/server.crt:ro
- ./ssl/db/server.key:/var/lib/postgresql/server.key:ro
- ./ssl/db/ca.crt:/var/lib/postgresql/ca.crt:ro
command: >
postgres
-c config_file=/etc/postgresql/postgresql.conf
-c ssl=on
-c ssl_cert_file=/var/lib/postgresql/server.crt
-c ssl_key_file=/var/lib/postgresql/server.key
-c ssl_ca_file=/var/lib/postgresql/ca.crt
healthcheck:
test:
[
"CMD-SHELL",
"pg_isready -U ${POSTGRES_USER:-janua} -d ${POSTGRES_DB:-janua}",
]
interval: 30s
timeout: 10s
retries: 3
start_period: 60s
networks:
- janua-network
deploy:
resources:
limits:
cpus: "1.5"
memory: 1.5G
reservations:
cpus: "0.5"
memory: 512M
# Redis Cache
redis:
image: redis:7-alpine
container_name: janua-redis
restart: unless-stopped
command: redis-server --requirepass ${REDIS_PASSWORD} --appendonly yes --maxmemory 512mb --maxmemory-policy allkeys-lru
ports:
- "6379:6379"
volumes:
- redis_data:/data
healthcheck:
test: ["CMD", "redis-cli", "--raw", "incr", "ping"]
interval: 30s
timeout: 3s
retries: 3
start_period: 30s
networks:
- janua-network
deploy:
resources:
limits:
cpus: "0.5"
memory: 512M
reservations:
cpus: "0.25"
memory: 256M
# Nginx Load Balancer
nginx:
image: nginx:alpine
container_name: janua-nginx
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx-ssl.conf:/etc/nginx/conf.d/default.conf
- ./ssl/certs:/etc/ssl/certs
- ./ssl/private:/etc/ssl/private
- ./nginx.conf:/etc/nginx/nginx.conf
depends_on:
- janua-api
- janua-dashboard
- janua-website
- janua-docs
networks:
- janua-network
healthcheck:
test:
[
"CMD",
"wget",
"--no-verbose",
"--tries=1",
"--spider",
"http://localhost/health",
]
interval: 30s
timeout: 10s
retries: 3
deploy:
resources:
limits:
cpus: "0.5"
memory: 256M
# Dashboard Frontend (app.janua.dev)
janua-dashboard:
image: janua/dashboard:latest
container_name: janua-dashboard
restart: unless-stopped
environment:
- NODE_ENV=production
# INTERNAL_API_URL: Server-side API route calls (runtime, internal network)
# Uses Docker network hostname for container-to-container communication
- INTERNAL_API_URL=http://janua-api:4100
# NEXT_PUBLIC_API_URL: Client-side calls (build-time, external URL)
- NEXT_PUBLIC_API_URL=https://api.janua.dev
- NEXT_PUBLIC_APP_URL=https://app.janua.dev
ports:
- "4101:3000" # MADFAM standard: 4101 = Janua Dashboard
healthcheck:
test:
[
"CMD",
"wget",
"--no-verbose",
"--tries=1",
"--spider",
"http://localhost:3000/health",
]
interval: 30s
timeout: 10s
retries: 3
networks:
- janua-network
# Public Website (janua.dev)
janua-website:
image: janua/website:latest
container_name: janua-website
restart: unless-stopped
environment:
- NODE_ENV=production
- NEXT_PUBLIC_API_URL=https://api.janua.dev
- NEXT_PUBLIC_APP_URL=https://app.janua.dev
ports:
- "4104:3000" # MADFAM standard: 4104 = Janua Website
healthcheck:
test:
[
"CMD",
"wget",
"--no-verbose",
"--tries=1",
"--spider",
"http://localhost:3000/health",
]
interval: 30s
timeout: 10s
retries: 3
networks:
- janua-network
# Documentation Site (docs.janua.dev)
janua-docs:
image: janua/docs:latest
container_name: janua-docs
restart: unless-stopped
environment:
- NODE_ENV=production
ports:
- "4103:3000" # MADFAM standard: 4103 = Janua Docs
healthcheck:
test:
[
"CMD",
"wget",
"--no-verbose",
"--tries=1",
"--spider",
"http://localhost:3000/health",
]
interval: 30s
timeout: 10s
retries: 3
networks:
- janua-network
# Monitoring Stack
prometheus:
image: prom/prometheus:v2.45.0
container_name: janua-prometheus
restart: unless-stopped
ports:
- "9090:9090"
volumes:
- ./monitoring/prometheus.yml:/etc/prometheus/prometheus.yml
- ./monitoring/rules:/etc/prometheus/rules
- prometheus_data:/prometheus
command:
- "--config.file=/etc/prometheus/prometheus.yml"
- "--storage.tsdb.path=/prometheus"
- "--web.console.libraries=/etc/prometheus/console_libraries"
- "--web.console.templates=/etc/prometheus/consoles"
- "--storage.tsdb.retention.time=30d"
- "--web.enable-lifecycle"
networks:
- janua-network
healthcheck:
test:
[
"CMD",
"wget",
"--no-verbose",
"--tries=1",
"--spider",
"http://localhost:9090/-/healthy",
]
interval: 30s
timeout: 10s
retries: 3
grafana:
image: grafana/grafana:10.0.0
container_name: janua-grafana
restart: unless-stopped
ports:
- "3003:3000"
environment:
- GF_SECURITY_ADMIN_PASSWORD=${GRAFANA_PASSWORD}
- GF_USERS_ALLOW_SIGN_UP=false
- GF_INSTALL_PLUGINS=grafana-piechart-panel
volumes:
- grafana_data:/var/lib/grafana
- ./monitoring/grafana-dashboard.json:/etc/grafana/provisioning/dashboards/janua.json
networks:
- janua-network
healthcheck:
test:
[
"CMD-SHELL",
"wget --no-verbose --tries=1 --spider http://localhost:3000/api/health || exit 1",
]
interval: 30s
timeout: 10s
retries: 3
# PostgreSQL Exporter for Prometheus
postgres-exporter:
image: prometheuscommunity/postgres-exporter
container_name: janua-postgres-exporter
restart: unless-stopped
ports:
- "9187:9187"
environment:
# SSL enabled: verify-full requires CA cert, require just needs encryption
DATA_SOURCE_NAME: "postgresql://${POSTGRES_USER:-janua}:${POSTGRES_PASSWORD}@postgres:5432/${POSTGRES_DB:-janua}?sslmode=require"
volumes:
# Mount CA certificate for SSL verification (optional, for sslmode=verify-full)
- ./ssl/db/ca.crt:/etc/ssl/certs/ca.crt:ro
depends_on:
- postgres
networks:
- janua-network
# Redis Exporter for Prometheus
redis-exporter:
image: oliver006/redis_exporter
container_name: janua-redis-exporter
restart: unless-stopped
ports:
- "9121:9121"
environment:
- REDIS_ADDR=redis://redis:6379
- REDIS_PASSWORD=${REDIS_PASSWORD}
depends_on:
- redis
networks:
- janua-network
# Node Exporter for System Metrics
node-exporter:
image: prom/node-exporter:v1.6.0
container_name: janua-node-exporter
restart: unless-stopped
ports:
- "9100:9100"
volumes:
- /proc:/host/proc:ro
- /sys:/host/sys:ro
- /:/rootfs:ro
command:
- "--path.procfs=/host/proc"
- "--path.rootfs=/rootfs"
- "--path.sysfs=/host/sys"
- "--collector.filesystem.mount-points-exclude=^/(sys|proc|dev|host|etc)($$|/)"
networks:
- janua-network
# ============================================
# Automated PostgreSQL Backup Service
# ============================================
# Runs daily backups at 2 AM UTC with 30-day retention
# Backups are stored locally and can be synced to S3/GCS
postgres-backup:
image: prodrigestivill/postgres-backup-local:15
container_name: janua-postgres-backup
restart: unless-stopped
environment:
- POSTGRES_HOST=postgres
- POSTGRES_DB=${POSTGRES_DB:-janua}
- POSTGRES_USER=${POSTGRES_USER:-janua}
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
- POSTGRES_EXTRA_OPTS=--blobs --format=custom --compress=9
# Schedule: Daily at 2:00 AM UTC
- SCHEDULE=0 2 * * *
# Keep 30 days of daily backups
- BACKUP_KEEP_DAYS=30
# Keep 4 weekly backups (Sundays)
- BACKUP_KEEP_WEEKS=4
# Keep 6 monthly backups (1st of month)
- BACKUP_KEEP_MONTHS=6
# Naming convention
- BACKUP_SUFFIX=.sql.gz
# Health check interval
- HEALTHCHECK_PORT=8080
# Optional: SSL mode for backup connection
- POSTGRES_SSL_MODE=require
volumes:
- postgres_backups:/backups
# Mount CA certificate for SSL verification
- ./ssl/db/ca.crt:/etc/ssl/certs/ca.crt:ro
depends_on:
postgres:
condition: service_healthy
networks:
- janua-network
healthcheck:
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:8080/"]
interval: 60s
timeout: 10s
retries: 3
deploy:
resources:
limits:
cpus: "0.5"
memory: 512M
reservations:
cpus: "0.1"
memory: 128M
labels:
- "com.janua.description=Automated PostgreSQL backup service"
- "com.janua.backup.schedule=Daily at 2:00 AM UTC"
- "com.janua.backup.retention=30 days daily, 4 weekly, 6 monthly"
networks:
janua-network:
driver: bridge
ipam:
driver: default
config:
- subnet: 172.20.0.0/16
volumes:
postgres_data:
driver: local
postgres_backups:
driver: local
driver_opts:
type: none
o: bind
device: /var/backups/janua/postgres
redis_data:
driver: local
prometheus_data:
driver: local
grafana_data:
driver: local