You only need this page to run your own installation. On ManyLayers Cloud the gateway is already running at
https://app.manylayers.io/v1 and the console at https://app.manylayers.io. In a self-hosted install those addresses are your own: expose the gateway (port 8180) and the workspace (port 8190) on hostnames you control, and use them wherever the docs show the hosted ones.How it works
| Service | Binary | Default port | Serves |
|---|---|---|---|
| Gateway | /gateway | 8180 | /v1/* — the data plane. Stateless; scale it. |
| Workspace | /workspace | 8190 | /admin, /api/v1/gateway, /auth, /scim, /w, /ui (console). Runs background workers once; do not scale it for traffic. |
| Deployer | /deployer | 8200 | /admin/deployments, /admin/library, /admin/training-jobs. Optional. |
| — | /dbtool | — | Schema migrations and maintenance. |
/health, /healthz, /ready, /readyz, /version and /metrics. All three read the same config file and the same PostgreSQL database.
| Dependency | Required | Used for |
|---|---|---|
| PostgreSQL | Yes | Configuration, keys, policies, audit log, usage. Readiness fails without it. |
| Redis | For more than one gateway replica | Shared rate-limit windows, budget counters, the exact-match cache and event fan-out. Without it each replica keeps its own in-memory state. |
| Qdrant | Optional | Shared semantic cache (cache.semantic.store: qdrant) and knowledge bases. |
Run it
- Docker Compose
- Docker image
- Binaries
The repository has three stacks in
infra/docker/, each one compose file plus one env file:| File | Use |
|---|---|
docker-compose.dev.yml | Built from source, ports on localhost, debug logs. |
docker-compose.demo.yml | Prebuilt :demo images. |
docker-compose.prod.yml | Registry images (ghcr.io/tigergate/manylayers, set by IMAGE_PREFIX and IMAGE_TAG), read-only root filesystem, resource limits. Postgres, Redis, Workspace and Deployer bind 127.0.0.1; only the gateway is published on all interfaces. |
make prod-deploy runs pull, migrate, start and health check in one step. Pin IMAGE_TAG to a real tag rather than latest. Put the gateway behind your TLS ingress or load balancer; keep Workspace and Deployer on a private network or behind a VPN. For local development run make dev-migrate then make dev-up.No service migrates the schema on startup. Run
dbtool migrate (the compose migrate job) before starting or upgrading, so several gateway replicas never race on a migration.Health and readiness
| Endpoint | Use for | Checks |
|---|---|---|
/health, /healthz | Liveness | Nothing — always 200 {"status":"ok"} while the process runs. |
/ready, /readyz | Load balancer / readiness | Postgres ping, Redis ping when configured, and drain state; 503 not_ready on any failure. Bounded to 2 seconds. |
/version | Build stamp | Build version, commit and time, plus the environment name. |
/ready: a database blip would restart healthy replicas. The image has no shell or curl, so container health checks run the binary itself — /gateway -readycheck (or -healthcheck) probes its own port over loopback and exits 0 or 1. /workspace and /deployer take the same flags.
Graceful shutdown
OnSIGTERM each service:
- Marks itself draining —
/readyreturns503,/healthstays200. - Keeps serving for
SHUTDOWN_DRAIN_DELAY(default0s) so load balancers stop sending new requests. - Stops accepting connections and lets in-flight requests finish for up to
SHUTDOWN_TIMEOUT(default20s; YAMLserver.shutdown_timeout). If the deadline passes it logsshutdown deadline exceededand exits non-zero.
SHUTDOWN_DRAIN_DELAY to 5–10s behind a real load balancer or in Kubernetes, and keep your orchestrator’s grace period above SHUTDOWN_DRAIN_DELAY + SHUTDOWN_TIMEOUT (the production compose file uses stop_grace_period: 40s).
Scaling
- Gateway: add replicas behind a load balancer; they hold no local state. Configure Redis so rate limits, budgets and the exact cache are shared — without it each replica enforces limits independently.
- One Redis: run a single Redis (or one logical cluster). Several independent Redis instances would each keep their own limiter state, multiplying every caller’s limit.
- Workspace and Deployer: one instance each; Workspace runs the workflow, connector, billing and sweep workers.
- Credential key: set
MANYLAYERS_ENCRYPTION_KEYexplicitly. Without it each service generatesdata/credentials.keylocally, and replicas cannot read each other’s stored provider keys.
Environment variables
Environment variables override the config file. Inside the file,${VAR}, ${VAR:-default} and ${vault:secret/data/path#key} are expanded at load time.
| Variable | Used by | Purpose |
|---|---|---|
GATEWAY_PORT | gateway | Listen port (default 8180; overrides server.listen). |
WORKSPACE_PORT | workspace | Listen port (default 8190). |
DEPLOYER_PORT | deployer | Listen port (default 8200). |
DATABASE_URL | all | PostgreSQL URL; overrides database.url. |
REDIS_URL | all | redis://[:password@]host:port/db; overrides redis.*. |
SHUTDOWN_TIMEOUT | all | In-flight drain deadline, e.g. 20s. |
SHUTDOWN_DRAIN_DELAY | all | Time to stay up after turning unready, e.g. 10s. |
LOG_LEVEL | all | debug, info (default), warn, error. |
ENVIRONMENT | all | Empty, dev, development, local or test give text logs; anything else gives JSON. |
MANYLAYERS_ENCRYPTION_KEY | all | Seals provider keys and trace-destination headers. Overrides connectors.encryption_key. |
MANYLAYERS_ENCRYPTION_KEY_FILE | all | Where a generated key is kept when none is set (default data/credentials.key). |
MANYLAYERS_PII_VAULT_KEYS | all | v1:<base64>,v2:<base64> keys for reversible PII redaction. Set the same value on every service. |
MANYLAYERS_PII_VAULT_KEY_VERSION | all | Version used to seal new values. |
PUBLIC_WORKSPACE_URL | all | Console origin(s) allowed by CORS, comma-separated. Unset = same-origin only. |
PUBLIC_GATEWAY_URL, PUBLIC_DEPLOYER_URL | workspace | Where the console sends /v1 and deployment calls in a split-origin setup. |
MANYLAYERS_OTLP_DEBUG | gateway | 1 prints every OTLP export to stderr. Debug only. |
VAULT_ADDR, VAULT_TOKEN | all | HashiCorp Vault (KV v2) for ${vault:…} references. |
SLACK_WEBHOOK_URL | all | Internal notifications webhook when notifications.slack.webhook_url is empty. |
STAFF_PASSWORD | dbtool | Password for dbtool staff-create (at least 12 characters; read from stdin if unset). |
MANYLAYERS_UI_DIR, MANYLAYERS_UI_DEV_SERVER | workspace | Development only: serve the console from disk or a Vite dev server. |
configs/gateway.container.yaml) also reads these through ${…} references:
| Variable | Purpose |
|---|---|
MANYLAYERS_AUDIT_LOG_BODIES | true stores request and response bodies (after PII redaction) in the audit log. Default false. |
MANYLAYERS_ACCESS_TOKEN_TTL, MANYLAYERS_REFRESH_TOKEN_TTL | Console access-token lifetime (default 15m) and login lifetime (default 720h). |
RESEND_API_KEY, RESEND_FROM | Email for sign-in codes, invitations and budget alerts through Resend. |
MANYLAYERS_PUBLIC_URL | Public console origin that invitation links point at. |
Provider keys such as OPENAI_API_KEY | Referenced from models[].upstream_api_key or from a console provider credential reference. |
Next steps
Metrics
Scrape
/metrics on every replica.gateway.yaml reference
Every config key the services read.
Caching
Share the cache across replicas with Redis.
Request Logging & Audit
Retention and body capture for your deployment.