services: postgres: image: postgres:16-alpine environment: POSTGRES_USER: guestguard POSTGRES_PASSWORD: guestguard POSTGRES_DB: guestguard ports: - "5432:5432" volumes: - postgres-data:/var/lib/postgresql/data healthcheck: test: ["CMD-SHELL", "pg_isready -U guestguard -d guestguard"] interval: 5s timeout: 3s retries: 10 mailpit: image: axllent/mailpit:latest ports: - "1025:1025" # SMTP - "8025:8025" # Web UI healthcheck: test: ["CMD", "wget", "-qO-", "http://localhost:8025/api/v1/info"] interval: 5s timeout: 3s retries: 10 redis: image: redis:7-alpine command: ["redis-server", "--save", "", "--appendonly", "no"] ports: - "6379:6379" healthcheck: test: ["CMD", "redis-cli", "ping"] interval: 5s timeout: 3s retries: 10 nats: image: nats:2.10-alpine command: - "-js" - "-sd" - "/data" - "-m" - "8222" ports: - "4222:4222" - "8222:8222" volumes: - nats-data:/data healthcheck: test: ["CMD", "wget", "-qO-", "http://localhost:8222/healthz"] interval: 5s timeout: 3s retries: 10 api: build: context: . dockerfile: cmd/api/Dockerfile environment: GG_ENV: development GG_HTTP_ADDR: :8080 GG_DATABASE_URL: postgres://guestguard:guestguard@postgres:5432/guestguard?sslmode=disable GG_NATS_URL: nats://nats:4222 GG_FRAUD_GRPC_ADDR: fraud-engine:9091 GG_FRAUD_GRPC_TIMEOUT: 250ms GG_REDIS_ADDR: redis:6379 GG_SMTP_HOST: mailpit GG_SMTP_PORT: "1025" GG_SMTP_FROM_EMAIL: noreply@guestguard.local GG_SMTP_FROM_NAME: GuestGuard (dev) # Resend overrides SMTP when GG_RESEND_API_KEY is set in .env at the # project root. Leave unset and Mailpit (above) handles delivery. GG_RESEND_API_KEY: ${GG_RESEND_API_KEY:-} GG_RESEND_FROM_EMAIL: ${GG_RESEND_FROM_EMAIL:-} GG_RESEND_FROM_NAME: ${GG_RESEND_FROM_NAME:-GuestGuard} GG_TOKEN_SECRET: dev-only-insecure-secret-change-me GG_JWT_SECRET: dev-only-insecure-jwt-secret-change-me-32+bytes GG_PUBLIC_BASE_URL: http://localhost:3000 # Stripe billing — empty values leave billing disabled. Set in # .env at the project root to enable. GG_STRIPE_SECRET_KEY: ${GG_STRIPE_SECRET_KEY:-} GG_STRIPE_WEBHOOK_SECRET: ${GG_STRIPE_WEBHOOK_SECRET:-} GG_STRIPE_PRICE_PRO: ${GG_STRIPE_PRICE_PRO:-} GG_STRIPE_PRICE_BUSINESS: ${GG_STRIPE_PRICE_BUSINESS:-} # Tier 2 Block D — branding image uploads. Stored on the named # volume below so they survive container restarts; production # would back this with S3 + CDN instead. GG_UPLOADS_DIR: /var/lib/guestguard/uploads GG_UPLOADS_PUBLIC_URL: http://localhost:8080/uploads volumes: - uploads-data:/var/lib/guestguard/uploads ports: - "8080:8080" depends_on: postgres: condition: service_healthy nats: condition: service_healthy redis: condition: service_healthy restart: unless-stopped fraud-engine: build: context: ./fraud-engine dockerfile: Dockerfile environment: GG_ENV: development GG_HTTP_ADDR: 0.0.0.0:8081 GG_GRPC_ADDR: 0.0.0.0:9091 GG_NATS_URL: nats://nats:4222 ports: - "8081:8081" - "9091:9091" depends_on: nats: condition: service_healthy restart: unless-stopped notifier: build: context: . dockerfile: cmd/notifier/Dockerfile environment: GG_ENV: development GG_DATABASE_URL: postgres://guestguard:guestguard@postgres:5432/guestguard?sslmode=disable GG_NATS_URL: nats://nats:4222 GG_PUBLIC_BASE_URL: http://localhost:3000 GG_SMTP_HOST: mailpit GG_SMTP_PORT: "1025" GG_SMTP_FROM_EMAIL: noreply@guestguard.local GG_SMTP_FROM_NAME: GuestGuard (dev) GG_RESEND_API_KEY: ${GG_RESEND_API_KEY:-} GG_RESEND_FROM_EMAIL: ${GG_RESEND_FROM_EMAIL:-} GG_RESEND_FROM_NAME: ${GG_RESEND_FROM_NAME:-GuestGuard} depends_on: postgres: condition: service_healthy nats: condition: service_healthy restart: unless-stopped frontend: build: context: ./frontend dockerfile: Dockerfile environment: NODE_ENV: production NUXT_PUBLIC_API_BASE: http://localhost:8080 NUXT_PUBLIC_WS_BASE: ws://localhost:8080 ports: - "3000:3000" depends_on: - api restart: unless-stopped volumes: postgres-data: nats-data: uploads-data: