services: # ── PostgreSQL ────────────────────────────────────────────── db: image: postgres:16-alpine environment: POSTGRES_DB: notesfrais POSTGRES_USER: notesfrais POSTGRES_PASSWORD: ${DB_PASSWORD} volumes: - pgdata2:/var/lib/postgresql/data healthcheck: test: ["CMD-SHELL", "pg_isready -U notesfrais -d notesfrais"] interval: 5s timeout: 5s retries: 12 restart: unless-stopped networks: - internal # ── Backend Express ───────────────────────────────────────── backend: build: context: ./backend dockerfile: Dockerfile environment: NODE_ENV: production PORT: 3001 DATABASE_URL: postgresql://notesfrais:${DB_PASSWORD}@db:5432/notesfrais JWT_SECRET: ${JWT_SECRET} APP_SECRET: ${APP_SECRET} UPLOADS_DIR: /app/uploads FRONTEND_URL: https://${DOMAIN:-frais.domench.fr} volumes: - uploads:/app/uploads depends_on: db: condition: service_healthy restart: unless-stopped networks: - internal - coolify # ── Frontend nginx ────────────────────────────────────────── frontend: build: context: ./frontend dockerfile: Dockerfile depends_on: - backend restart: unless-stopped expose: - "80" networks: - internal - coolify labels: - "traefik.enable=true" - "traefik.http.routers.notesfrais.rule=Host(`frais.domench.fr`)" - "traefik.http.routers.notesfrais.entrypoints=https" - "traefik.http.routers.notesfrais.tls=true" - "traefik.http.routers.notesfrais.tls.certresolver=letsencrypt" - "traefik.http.services.notesfrais.loadbalancer.server.port=80" - "traefik.http.routers.notesfrais-http.rule=Host(`frais.domench.fr`)" - "traefik.http.routers.notesfrais-http.entrypoints=http" - "traefik.http.routers.notesfrais-http.middlewares=notesfrais-https-redirect" - "traefik.http.middlewares.notesfrais-https-redirect.redirectscheme.scheme=https" - "traefik.http.middlewares.notesfrais-https-redirect.redirectscheme.permanent=true" networks: internal: driver: bridge coolify: external: true volumes: pgdata2: uploads: