FASHIONISTAR CI/CD
🔄 Celery Queues Deploy: 4aae6106b1530c3cc7d7d64f5e3c3e1d5015691d [GitHub Actions]
27c799c | # ============================================================================ | |
| # FASHIONISTAR — Production Docker Compose (Oracle Cloud Always Free VM) | |
| # ============================================================================ | |
| # Deploys on: Oracle Cloud VM.Standard.A1.Flex (2 OCPUs / 12 GB RAM / ARM64) | |
| # | |
| # Services on Oracle (THIS FILE): | |
| # nginx — Reverse proxy + SSL termination (Let's Encrypt) | |
| # api — Django ASGI via Gunicorn + UvicornWorker (separate API logs) | |
| # keepalive — Oracle idle-prevention pinger (every 4 minutes) | |
| # | |
| # ❌ NO postgres container — uses Neon PostgreSQL (DATABASE_URL in .env.production) | |
| # ❌ NO redis container — uses Aiven Redis (REDIS_URL=rediss://... in .env.production) | |
| # | |
| # Services on Northflank (northflank.json): | |
| # celery_worker — All background task queues (separate Celery logs) | |
| # celery_beat — Celery Beat scheduler (cron job) | |
| # | |
| # Both API (Oracle) and Celery (Northflank) share the SAME Aiven Redis | |
| # for broker/cache — no cross-cloud Redis setup needed! | |
| # | |
| # Usage: | |
| # docker compose -f docker-compose.production.yml up -d | |
| # docker compose -f docker-compose.production.yml pull && \ | |
| # docker compose -f docker-compose.production.yml up -d --no-deps api | |
| # ============================================================================ | |
| version: '3.9' | |
| services: | |
| # ── Nginx: Reverse proxy + SSL + Static files ─────────────────────────────── | |
| nginx: | |
| image: nginx:1.27-alpine | |
| container_name: fashionistar_nginx | |
| ports: | |
| - "80:80" | |
| - "443:443" | |
| volumes: | |
| - ./deploy/nginx/conf.d:/etc/nginx/conf.d:ro | |
| - /etc/letsencrypt:/etc/letsencrypt:ro | |
| - static_files:/app/staticfiles:ro | |
| - media_files:/app/mediafiles:ro | |
| - nginx_logs:/var/log/nginx | |
| depends_on: | |
| api: | |
| condition: service_healthy | |
| restart: always | |
| networks: | |
| - fashionistar_net | |
| logging: | |
| driver: "json-file" | |
| options: | |
| max-size: "50m" | |
| max-file: "5" | |
| healthcheck: | |
| test: ["CMD", "nginx", "-t"] | |
| interval: 30s | |
| timeout: 10s | |
| retries: 3 | |
| # ── Django ASGI API Gateway ───────────────────────────────────────────────── | |
| # PORT=10000 (matches fashionistar_backend_production.env) | |
| # Uses Aiven Redis (TLS rediss://) from .env.production — NO local Redis needed | |
| # Uses Neon PostgreSQL (DATABASE_URL) from .env.production — NO local DB needed | |
| # Separate from Celery — has its own log volume (api_logs) | |
| api: | |
| image: ghcr.io/${GITHUB_REPOSITORY:-fashionistar-clothings/fashionistar_backend}/api:${IMAGE_TAG:-latest} | |
| container_name: fashionistar_api | |
| pull_policy: always | |
| # Load ALL production secrets from .env.production (mirrors fashionistar_backend_production.env) | |
| env_file: | |
| - .env.production | |
| environment: | |
| # ── Core Settings ─────────────────────────────────────────────────────── | |
| - DJANGO_SETTINGS_MODULE=backend.config.production | |
| # PORT must be 10000 to match your fashionistar_backend_production.env | |
| - PORT=10000 | |
| # ── Oracle Cloud VM: 2 OCPUs → 4 Gunicorn workers (2×CPU, I/O-bound) ── | |
| - GUNICORN_WORKERS=4 | |
| - GUNICORN_TIMEOUT=300 | |
| - GUNICORN_KEEPALIVE=75 | |
| - GUNICORN_LOG_LEVEL=info | |
| # ── Updated Hosts — include Oracle domain ─────────────────────────────── | |
| - ALLOWED_HOSTS=api.fashionistar.net,fashionistar.net,fashionistar-frontend.vercel.app,localhost,127.0.0.1 | |
| - BACKEND_URL=https://api.fashionistar.net | |
| - BACKEND_TUNNEL_URL=https://api.fashionistar.net | |
| - SITE_URL=https://api.fashionistar.net | |
| - HEALTH_URL_ENDPOINT=https://api.fashionistar.net/api/v1/health | |
| # ── CORS / CSRF — include Oracle domain ──────────────────────────────── | |
| - CORS_ALLOWED_ORIGINS=https://fashionistar.net,https://fashionistar-frontend.vercel.app,https://api.fashionistar.net | |
| - CSRF_TRUSTED_ORIGINS=https://fashionistar.net,https://fashionistar-frontend.vercel.app,https://api.fashionistar.net | |
| # ── Log directory — separate from Celery ─────────────────────────────── | |
| - LOG_DIR=/app/logs/api | |
| volumes: | |
| - static_files:/app/staticfiles | |
| - media_files:/app/mediafiles | |
| # Dedicated API log volume — Celery logs are separate (on Northflank) | |
| - api_logs:/app/logs/api | |
| # No depends_on redis or postgres — both are external managed services | |
| restart: always | |
| deploy: | |
| resources: | |
| limits: | |
| # Oracle 2 OCPU / 12 GB RAM — reserve headroom for OS + logging | |
| cpus: '1.8' | |
| memory: 9G | |
| reservations: | |
| cpus: '0.5' | |
| memory: 2G | |
| networks: | |
| - fashionistar_net | |
| logging: | |
| driver: "json-file" | |
| options: | |
| max-size: "100m" | |
| max-file: "10" | |
| tag: "fashionistar-api" | |
| healthcheck: | |
| # PORT=10000 (matches production env) | |
| test: ["CMD", "curl", "-fsS", "http://127.0.0.1:10000/api/v1/health/"] | |
| interval: 30s | |
| timeout: 10s | |
| start_period: 90s | |
| retries: 5 | |
| # ── Oracle Keep-Alive (Idle Prevention) ───────────────────────────────────── | |
| # Oracle reclaims Always Free instances if CPU < 10% for 7 consecutive days. | |
| # This lightweight pinger on PORT=10000 prevents idle reclamation. | |
| keepalive: | |
| image: curlimages/curl:8-alpine | |
| container_name: fashionistar_keepalive | |
| command: > | |
| sh -c "while true; do | |
| curl -fsS http://api:10000/api/v1/health/ > /dev/null 2>&1 || true; | |
| sleep 240; | |
| done" | |
| depends_on: | |
| - api | |
| restart: always | |
| deploy: | |
| resources: | |
| limits: | |
| cpus: '0.05' | |
| memory: 32M | |
| networks: | |
| - fashionistar_net | |
| logging: | |
| driver: "json-file" | |
| options: | |
| max-size: "5m" | |
| max-file: "2" | |
| # ── Volumes ────────────────────────────────────────────────────────────────── | |
| volumes: | |
| static_files: | |
| name: fashionistar_static_files | |
| media_files: | |
| name: fashionistar_media_files | |
| # Dedicated API log volume (Django ASGI gateway logs only — Celery logs on Northflank) | |
| api_logs: | |
| name: fashionistar_api_logs | |
| driver: local | |
| driver_opts: | |
| type: none | |
| o: bind | |
| device: /var/log/fashionistar/api | |
| # Nginx access/error logs | |
| nginx_logs: | |
| name: fashionistar_nginx_logs | |
| driver: local | |
| driver_opts: | |
| type: none | |
| o: bind | |
| device: /var/log/fashionistar/nginx | |
| # ── Networks ───────────────────────────────────────────────────────────────── | |
| networks: | |
| fashionistar_net: | |
| name: fashionistar_production_net | |
| driver: bridge | |
| ipam: | |
| driver: default | |
| config: | |
| - subnet: 172.20.0.0/16 | |