services: keygate: image: ghcr.io/tabloy/keygate:latest # build: . # uncomment to build from source ports: - "9000:9000" # Everything in .env reaches the container (storage, release keys, # rate limits, Redis...). The values under `environment` take # precedence over .env and pin what is specific to this compose setup. # (`required: false` needs Docker Compose 2.24 or later.) env_file: - path: .env required: false environment: PORT: "9000" ENVIRONMENT: production BASE_URL: ${BASE_URL:-http://localhost:9000} # The bundled postgres service, not the localhost URL in .env.example. DATABASE_URL: postgres://keygate:keygate@postgres:5432/keygate?sslmode=disable JWT_SECRET: ${JWT_SECRET:?Set JWT_SECRET in .env} LICENSE_SIGNING_KEY: ${LICENSE_SIGNING_KEY:?Set LICENSE_SIGNING_KEY in .env} # Also taken from the shell or `--env-file`, for deployments that # inject secrets as environment variables rather than a .env file. ADMIN_EMAILS: ${ADMIN_EMAILS:-} STRIPE_SECRET_KEY: ${STRIPE_SECRET_KEY:-} SMTP_HOST: ${SMTP_HOST:-} SMTP_PORT: ${SMTP_PORT:-587} SMTP_USERNAME: ${SMTP_USERNAME:-} SMTP_PASSWORD: ${SMTP_PASSWORD:-} SMTP_FROM: ${SMTP_FROM:-} REDIS_URL: ${REDIS_URL:-} depends_on: postgres: condition: service_healthy restart: unless-stopped healthcheck: test: ["CMD", "curl", "-sf", "http://localhost:9000/health"] interval: 30s timeout: 5s retries: 3 start_period: 10s postgres: image: postgres:18-alpine environment: POSTGRES_USER: keygate POSTGRES_PASSWORD: keygate POSTGRES_DB: keygate volumes: # postgres:18+ images expect the volume mounted at /var/lib/postgresql # (data lives in a versioned subdirectory); mounting .../data makes # initdb fail on a fresh volume. https://github.com/docker-library/postgres/issues/37 - pgdata:/var/lib/postgresql healthcheck: test: ["CMD-SHELL", "pg_isready -U keygate"] interval: 10s timeout: 5s retries: 5 restart: unless-stopped volumes: pgdata: