version: '3.8' # Production Docker Compose Configuration # Usage: docker compose -f docker-compose.prod.yml up -d services: # ========================================== # Gitea - Production Configuration # ========================================== gitea: image: gitea/gitea:latest-rootless container_name: nanaxe-gitea restart: unless-stopped networks: - nanaxe-network volumes: - gitea-data:/data - /etc/timezone:/etc/timezone:ro - /etc/localtime:/etc/localtime:ro environment: - USER_UID=1000 - USER_GID=1000 - GITEA__database__DB_TYPE=postgres - GITEA__database__HOST=postgres:5432 - GITEA__database__NAME=gitea - GITEA__database__USER=gitea - GITEA__database__PASSWD=${POSTGRES_PASSWORD} - GITEA__server__DOMAIN=${GITEA_DOMAIN} - GITEA__server__ROOT_URL=https://${GITEA_DOMAIN} - GITEA__server__SSH_DOMAIN=${GITEA_DOMAIN} - GITEA__server__SSH_PORT=222 - GITEA__server__SSH_LISTEN_PORT=22 - GITEA__security__INSTALL_LOCK=true - GITEA__security__SECRET_KEY=${GITEA_SECRET_KEY} - GITEA__service__DISABLE_REGISTRATION=false - GITEA__service__REQUIRE_SIGNIN_VIEW=false - GITEA__log__MODE=console - GITEA__log__LEVEL=Info healthcheck: test: ["CMD", "curl", "-f", "http://localhost:3000/api/healthz"] interval: 30s timeout: 10s retries: 3 start_period: 40s deploy: resources: limits: cpus: '1.0' memory: 512M reservations: cpus: '0.25' memory: 128M # ========================================== # PostgreSQL - Production # ========================================== postgres: image: postgres:16-alpine container_name: nanaxe-postgres restart: unless-stopped networks: - nanaxe-network volumes: - postgres-data:/var/lib/postgresql/data - ./backups:/backups environment: - POSTGRES_USER=gitea - POSTGRES_PASSWORD=${POSTGRES_PASSWORD} - POSTGRES_DB=gitea - PGDATA=/var/lib/postgresql/data/pgdata healthcheck: test: ["CMD-SHELL", "pg_isready -U gitea"] interval: 10s timeout: 5s retries: 5 deploy: resources: limits: cpus: '0.5' memory: 256M reservations: cpus: '0.1' memory: 64M # ========================================== # Nanaxe API - Production # ========================================== nanaxe-api: image: ghcr.io/${GITHUB_REPOSITORY:-nanaxe}/nanaxe-api:latest container_name: nanaxe-api restart: unless-stopped networks: - nanaxe-network volumes: - mindmap-data:/app/mindmaps - api-logs:/app/logs environment: - NODE_ENV=production - PORT=8080 - GITEA_URL=http://gitea:3000 - GITEA_TOKEN=${GITEA_TOKEN} - JWT_SECRET=${JWT_SECRET} - MINDMAP_STORAGE_PATH=/app/mindmaps - LOG_LEVEL=info healthcheck: test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost:8080/health"] interval: 30s timeout: 10s retries: 3 start_period: 10s deploy: resources: limits: cpus: '0.5' memory: 256M reservations: cpus: '0.1' memory: 64M replicas: 1 update_config: parallelism: 1 delay: 10s failure_action: rollback order: start-first rollback_config: parallelism: 1 delay: 10s failure_action: pause depends_on: postgres: condition: service_healthy gitea: condition: service_healthy # ========================================== # Nginx - Production with SSL # ========================================== nginx: image: nginx:alpine container_name: nanaxe-nginx restart: unless-stopped networks: - nanaxe-network ports: - "80:80" - "443:443" volumes: - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro - ./nginx/html:/usr/share/nginx/html:ro - ./nginx/ssl:/etc/nginx/ssl:ro - certbot-data:/etc/letsencrypt:ro - certbot-www:/var/www/certbot:ro healthcheck: test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost/health"] interval: 30s timeout: 10s retries: 3 deploy: resources: limits: cpus: '0.25' memory: 128M depends_on: - gitea - nanaxe-api # ========================================== # Certbot - SSL Certificates # ========================================== certbot: image: certbot/certbot container_name: nanaxe-certbot restart: "no" networks: - nanaxe-network volumes: - certbot-data:/etc/letsencrypt - certbot-www:/var/www/certbot entrypoint: "/bin/sh -c 'trap exit TERM; while :; do certbot renew; sleep 12h & wait $${!}; done;'" profiles: - ssl # ========================================== # Watchtower - Auto-update containers # ========================================== watchtower: image: containrrr/watchtower container_name: nanaxe-watchtower restart: unless-stopped networks: - nanaxe-network volumes: - /var/run/docker.sock:/var/run/docker.sock environment: - WATCHTOWER_CLEANUP=true - WATCHTOWER_POLL_INTERVAL=3600 - WATCHTOWER_INCLUDE_STOPPED=true - WATCHTOWER_REVIVE_STOPPED=false - WATCHTOWER_ROLLING_RESTART=true - WATCHTOWER_LABEL_ENABLE=true command: --interval 3600 --cleanup profiles: - auto-update # ========================================== # Redis - Caching (optional) # ========================================== redis: image: redis:7-alpine container_name: nanaxe-redis restart: unless-stopped networks: - nanaxe-network volumes: - redis-data:/data command: redis-server --appendonly yes --maxmemory 64mb --maxmemory-policy allkeys-lru healthcheck: test: ["CMD", "redis-cli", "ping"] interval: 10s timeout: 3s retries: 3 deploy: resources: limits: cpus: '0.25' memory: 128M profiles: - cache networks: nanaxe-network: driver: bridge ipam: config: - subnet: 172.20.0.0/16 volumes: gitea-data: driver: local postgres-data: driver: local mindmap-data: driver: local api-logs: driver: local certbot-data: driver: local certbot-www: driver: local redis-data: driver: local