# Deployment

> Deploy AI Gateway: topologies, the Docker Compose stack, and per-platform guides.

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

Run the gateway with Postgres and Redis. The repository provides a Docker Compose stack
for local hosts, Linux servers, Coolify, Portainer, and Dokploy. The dashboard is a separate
service that can be deployed independently.

## Pick your platform

- [Local](https://boelabs.com/docs/ai-gateway/deploy-local) — run it on your machine for development.
- [Coolify](https://boelabs.com/docs/ai-gateway/deploy-coolify) — deploy the Compose stack on Coolify.
- [Portainer](https://boelabs.com/docs/ai-gateway/deploy-portainer) — deploy the Compose stack on Portainer.
- [Dokploy](https://boelabs.com/docs/ai-gateway/deploy-dokploy) — deploy the Compose stack on Dokploy.
- [Docker on a Linux VPS](https://boelabs.com/docs/ai-gateway/deploy-docker-linux) — plain `docker compose` on any host.

## Topologies

How the gateway reaches Postgres/Redis decides whether you need TLS — and, because the gateway runs on
**Bun**, whether you hit the [self-signed TLS issue](https://boelabs.com/docs/ai-gateway/troubleshooting).

| Topology | Connection | TLS | Works on Bun |
|---|---|---|---|
| **Compose (all-in-one)** — gateway + Postgres + Redis together | internal Docker network | none (plaintext) | ✅ recommended |
| **Managed database** (Neon / Aiven / Upstash …) | internet | public-CA | ✅ |
| **Self-signed database exposed publicly** (raw Coolify/Dokploy port) | internet | self-signed | ⚠️ may fail — see [Troubleshooting](https://boelabs.com/docs/ai-gateway/troubleshooting) |

The first row is the default for every recipe here: the gateway talks to Postgres/Redis over the
private Compose network in plaintext, which is both safe (network isolation) and immune to the Bun TLS
issue.

## Compose files and naming

- `docker/compose.yaml` is the production base. It exposes services to the private network without publishing host ports.
- `docker/compose.local.yaml` adds loopback-only host mappings for local development or a host reverse proxy. Load it explicitly; do not add it to a production PaaS deployment.

## The Compose stack

The repository ships a `docker/compose.yaml` that brings up the whole stack:

- **postgres** (`postgres:18-alpine`) and **redis** (`redis:8-alpine`), with health checks and named
  volumes (`pgdata`, `redisdata`).
- **migrate** — a one-off job that applies SQL migrations and exits; the gateway waits for it to
  finish. PaaS dashboards may list it as a domain candidate; leave its domain empty.
- **gateway** (`:4000` internally) — built from `apps/api/Dockerfile`.
- **dashboard** (`:3001` internally) — the optional operator UI, off unless `DASH_ENABLED` is true.
  It is an independent deployment with its own domain, and it needs one setting — `GATEWAY_URL`.
  The gateway needs nothing, and does not have to be public. See
  [Operator dashboard → Serving it](https://boelabs.com/docs/ai-gateway/dashboard#serving-it).

Coolify and Dokploy deploy only `docker/compose.yaml` and attach their proxy to the private network.
For a local machine or a host-level reverse proxy, merge the local override explicitly:

```bash
git clone https://github.com/boelabs/ai-gateway.git && cd ai-gateway

MASTER_KEY=$(openssl rand -base64 48) \
ENCRYPTION_KEY_HEX=$(openssl rand -hex 32) \
docker compose -f docker/compose.yaml -f docker/compose.local.yaml up -d
```

`docker/compose.local.yaml` supplies known development-only defaults. The production base intentionally
leaves both secrets empty, and the gateway refuses to start until they are configured. In
Coolify/Portainer/Dokploy, set them in the UI. `DATABASE_URL` and `REDIS_URL` default to the internal
`postgres`/`redis` services over plaintext and only need overriding if you point at managed instances
— see [Setup](https://boelabs.com/docs/ai-gateway/setup).

<Callout>

Assign domains only to the **gateway** and **dashboard** services you want to expose. Keep
Postgres, Redis, and the one-off `migrate` job internal. The production Compose base enforces this
by publishing no host ports.

</Callout>

