version: '3.8' services: backend: build: ./backend ports: - "127.0.0.1:8020:8000" volumes: - ./backend:/app - backend_data:/app/data environment: - DATABASE_URL=postgresql://elmeg:elmeg_password@db:5432/elmeg_db - SECRET_KEY=${SECRET_KEY:-demo-secret-change-in-production} - AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID} - AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY} - AWS_SES_REGION=${AWS_SES_REGION} - EMAIL_FROM=${EMAIL_FROM} - FRONTEND_URL=${FRONTEND_URL:-https://elmeg.xyz} command: sh start.sh depends_on: - db restart: unless-stopped healthcheck: test: [ "CMD", "python", "-c", "import httpx; httpx.get('http://localhost:8000/docs').raise_for_status()" ] interval: 30s timeout: 10s retries: 3 networks: - elmeg - traefik-public labels: - "traefik.enable=true" - "traefik.http.routers.elmeg-backend.rule=(Host(`elmeg.runfoo.run`) || Host(`elmeg.xyz`)) && PathPrefix(`/api`)" - "traefik.http.routers.elmeg-backend.entrypoints=websecure" - "traefik.http.routers.elmeg-backend.tls.certresolver=letsencrypt" - "traefik.http.routers.elmeg-backend.priority=100" - "traefik.http.middlewares.elmeg-strip.stripprefix.prefixes=/api" - "traefik.http.routers.elmeg-backend.middlewares=elmeg-strip" - "traefik.http.routers.elmeg-backend.service=elmeg-backend-svc" - "traefik.http.services.elmeg-backend-svc.loadbalancer.server.port=8000" - "traefik.docker.network=traefik-public" # Direct routes for docs (no strip) - "traefik.http.routers.elmeg-backend-docs.rule=(Host(`elmeg.runfoo.run`) || Host(`elmeg.xyz`)) && PathPrefix(`/docs`, `/openapi.json`)" - "traefik.http.routers.elmeg-backend-docs.entrypoints=websecure" - "traefik.http.routers.elmeg-backend-docs.tls.certresolver=letsencrypt" - "traefik.http.routers.elmeg-backend-docs.priority=100" - "traefik.http.routers.elmeg-backend-docs.service=elmeg-backend-svc" frontend: build: context: ./frontend dockerfile: Dockerfile ports: - "127.0.0.1:3020:3000" volumes: - ./frontend:/app - /app/node_modules environment: - NEXT_PUBLIC_API_URL=/api - INTERNAL_API_URL=http://backend:8000 depends_on: - backend restart: unless-stopped networks: - elmeg - traefik-public labels: - "traefik.enable=true" - "traefik.http.routers.elmeg-frontend.rule=(Host(`elmeg.runfoo.run`) || Host(`elmeg.xyz`)) && !PathPrefix(`/api`, `/docs`, `/openapi.json`)" - "traefik.http.routers.elmeg-frontend.entrypoints=websecure" - "traefik.http.routers.elmeg-frontend.tls.certresolver=letsencrypt" - "traefik.http.routers.elmeg-frontend.priority=50" - "traefik.http.services.elmeg-frontend.loadbalancer.server.port=3000" - "traefik.docker.network=traefik-public" db: image: postgres:15-alpine environment: - POSTGRES_USER=elmeg - POSTGRES_PASSWORD=elmeg_password - POSTGRES_DB=elmeg volumes: - postgres_data:/var/lib/postgresql/data restart: unless-stopped healthcheck: test: [ "CMD-SHELL", "pg_isready -U elmeg -d elmeg" ] interval: 10s timeout: 5s retries: 5 networks: - elmeg volumes: postgres_data: backend_data: networks: elmeg: traefik-public: name: ${TRAEFIK_NETWORK:-traefik} external: true