# Tabungin - full stack (web + api + postgres) in one command: # cp .env.docker.example .env.docker (then edit secrets) # docker compose --env-file .env.docker up -d --build # # Web (UI): http://localhost:${WEB_PORT:-8080} # API (Swagger): http://localhost:${API_PORT:-3001}/api # Postgres: internal only (not published by default) services: db: image: postgres:16-alpine restart: unless-stopped environment: POSTGRES_USER: ${POSTGRES_USER:-tabungin} POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-tabungin} POSTGRES_DB: ${POSTGRES_DB:-tabungin} volumes: - db_data:/var/lib/postgresql/data healthcheck: test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-tabungin} -d ${POSTGRES_DB:-tabungin}"] interval: 5s timeout: 5s retries: 10 # Uncomment to access Postgres from the host: # ports: # - "5432:5432" api: build: context: ./apps/api restart: unless-stopped depends_on: db: condition: service_healthy environment: NODE_ENV: production PORT: 3001 DATABASE_URL: postgresql://${POSTGRES_USER:-tabungin}:${POSTGRES_PASSWORD:-tabungin}@db:5432/${POSTGRES_DB:-tabungin}?schema=public DATABASE_URL_SHADOW: postgresql://${POSTGRES_USER:-tabungin}:${POSTGRES_PASSWORD:-tabungin}@db:5432/${POSTGRES_DB:-tabungin}_shadow?schema=public JWT_SECRET: ${JWT_SECRET:?JWT_SECRET is required} WEB_APP_URL: ${WEB_APP_URL:-http://localhost:8080} # Optional integrations (leave blank to disable) EXCHANGE_RATE_URL: ${EXCHANGE_RATE_URL:-} GOOGLE_CLIENT_ID: ${GOOGLE_CLIENT_ID:-} GOOGLE_CLIENT_SECRET: ${GOOGLE_CLIENT_SECRET:-} GOOGLE_CALLBACK_URL: ${GOOGLE_CALLBACK_URL:-http://localhost:8080/api/auth/google/callback} OTP_SEND_WEBHOOK_URL: ${OTP_SEND_WEBHOOK_URL:-} OTP_SEND_WEBHOOK_URL_TEST: ${OTP_SEND_WEBHOOK_URL_TEST:-} volumes: # Persist uploaded avatars across container rebuilds - api_uploads:/app/public ports: - "${API_PORT:-3001}:3001" web: build: context: ./apps/web args: # Empty => requests are same-origin relative; nginx proxies /api & /avatars VITE_API_URL: "" restart: unless-stopped depends_on: - api ports: - "${WEB_PORT:-8080}:80" volumes: db_data: api_uploads: