# Portainer

> Deploy AI Gateway on Portainer as a Compose stack.

URL: https://boelabs.com/en/docs/ai-gateway/deploy-portainer
Language: English

[Portainer](https://www.portainer.io) deploys the repository's `docker/compose.yaml` as a **Stack**.

1. **Stacks → Add stack**, and either paste the `docker/compose.yaml` or use the **Git repository**
   option pointing at this repo.
2. Under **Environment variables**, set:
	- `MASTER_KEY` — `openssl rand -base64 48`
	- `ENCRYPTION_KEYRING` — e.g. `{"primary":"<openssl rand -hex 32>"}`
	- `ACTIVE_ENCRYPTION_KEY_ID` — `primary`
3. **Deploy the stack.** Postgres, Redis and the one-off `migrate` job run inside the stack; the
   gateway listens on internal port `4000`, and the optional dashboard on `3001`.

The production Compose base publishes no host ports. Attach your reverse proxy to the stack network.
If the proxy runs directly on the same Docker host instead, add loopback-only mappings for gateway
(`127.0.0.1:4000:4000`) and the dashboard (`127.0.0.1:3001:3001`) in Portainer's stack editor.

Route the dashboard on its own hostname — it is an independent app at the root of its domain, not a
path on the gateway's. It needs `GATEWAY_URL` pointing at the gateway on the stack network; the
gateway itself needs no domain unless you want the API published. See
[Operator dashboard → Serving it](https://boelabs.com/docs/ai-gateway/dashboard#serving-it).
Never publish Postgres, Redis, or `migrate`.

The gateway reaches Postgres/Redis over the internal stack network in plaintext, so there is no TLS
handshake and no [Bun TLS issue](https://boelabs.com/docs/ai-gateway/troubleshooting).

If you point at **external** instances instead, set `DATABASE_URL`/`REDIS_URL` in the stack
environment and prefer a [managed provider with a public-CA certificate](https://boelabs.com/docs/ai-gateway/setup#managed-database-and-redis);
a self-signed database over a public port will not work on Bun.

