Skip to content

Deployment

Docker Compose (local / small deployments)

Section titled “Docker Compose (local / small deployments)”

The root docker-compose.yml is the zero-config deployment for end users. It pulls the pre-built image from GHCR and bundles a PostgreSQL 16 database:

Terminal window
curl -O https://raw.githubusercontent.com/gatesuite/mcpgate/main/docker-compose.yml

Set required environment variables:

Terminal window
export ADMIN_API_KEY=$(python3 -c "import secrets; print(secrets.token_hex(32))")
export DATABASE_URL=postgresql+asyncpg://mcpgate:mcpgate@postgres:5432/mcpgate

Start:

Terminal window
docker compose up -d

MCPGate will be available at http://localhost:8000.

Terminal window
MCPGATE_VERSION=1.2.3 docker compose up -d

Or in .env:

MCPGATE_VERSION=1.2.3
ADMIN_API_KEY=your-secret-here
DATABASE_URL=postgresql+asyncpg://mcpgate:mcpgate@postgres:5432/mcpgate
KEY_PREFIX=mcp_sk_

To build and run from the local source (useful when developing MCPGate itself):

Terminal window
make app-up # builds production image, starts MCPGate + Postgres
make app-logs # tail logs
make app-down # stop (volumes preserved)

This uses deployments/docker-compose/docker-compose.yml which builds the production Dockerfile target.


MCPGate ships a Helm chart published to GHCR as an OCI artifact.

Terminal window
helm install mcpgate oci://ghcr.io/gatesuite/charts/mcpgate \
--namespace mcpgate \
--create-namespace \
--set secret.adminApiKey="your-strong-secret-here" \
--set secret.databaseUrl="postgresql+asyncpg://user:pass@postgres:5432/mcpgate"
ValueDefaultDescription
replicaCount1Number of pods
image.repositoryghcr.io/gatesuite/mcpgateContainer image
image.taglatestImage tag — pin to a release in production
secret.adminApiKey""Master admin key (required)
secret.databaseUrl""PostgreSQL connection string (required)
secret.keyPrefixmcp_sk_Key prefix
service.port8000Service port
autoscaling.enabledtrueEnable HPA
autoscaling.minReplicas1Minimum replicas
autoscaling.maxReplicas10Maximum replicas
autoscaling.targetCPUUtilizationPercentage70CPU scale trigger
pdb.enabledfalseEnable PodDisruptionBudget
ingress.enabledfalseEnable ingress

MCPGate expects an async-compatible PostgreSQL connection string (postgresql+asyncpg://). It auto-creates the api_keys table on startup.

secret:
adminApiKey: "your-strong-secret"
databaseUrl: "postgresql+asyncpg://mcpgate:password@my-postgres-host:5432/mcpgate"
ingress:
enabled: true
className: nginx
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
hosts:
- host: keys.yourdomain.com
paths:
- path: /
pathType: Prefix
tls:
- secretName: mcpgate-tls
hosts:
- keys.yourdomain.com

  • ADMIN_API_KEY is at least 32 random bytes, stored in a secret manager (not in source control)
  • Image is pinned to a specific version tag, not latest
  • MCPGate is not exposed publicly — behind a private load balancer or accessed only by your backend services
  • PostgreSQL has a dedicated user with minimal privileges (SELECT, INSERT, UPDATE, DELETE on api_keys only)
  • TLS is terminated at the ingress/load balancer
  • replicaCount ≥ 2 in production, with pdb.enabled: true to prevent simultaneous pod eviction
  • Resource limits are set (defaults: 250m CPU / 128Mi memory)
  • Liveness, readiness, and startup probes are enabled (on by default in the Helm chart)
  • Log shipping is configured — MCPGate logs to stdout at INFO level

VariableRequiredDefaultDescription
DATABASE_URL✅—PostgreSQL async connection string (postgresql+asyncpg://...)
ADMIN_API_KEY✅—Master token for admin endpoints
KEY_PREFIX—mcp_sk_Prefix for generated keys
PROJECT_NAME—MCPGateService name in API responses
VERSION—1.0.0Version string
API_V1_STR—/api/v1API route prefix