---
title: "Deploy with Docker"
subtitle: "Run the gateway image on any container host, with your keys in the environment."
---

The image is `ghcr.io/respanai/respan-gateway`, for `linux/amd64` and `linux/arm64`. It holds the `respan-gateway` binary with the web UI built in, on a distroless Debian 12 base, running as a non-root user. The binary is the entrypoint, so anything after the image name is passed to it as arguments.

<Note>
Only the `:main` tag (the latest `main` branch) and `:sha-<commit>` tags are published for now. `:latest` and version tags such as `:0.2.0` arrive with the next release. The examples use `:main`; pin a `:sha-<commit>` tag if you need a fixed build.
</Note>

## Run it

The image listens on `0.0.0.0:8080` (its `PORT` is `8080`). The gateway refuses to start on a non-loopback address without a client key, so anyone who can reach it can't spend your provider keys. Pass one in `RESPAN_CLIENT_KEYS` (comma separated for several):

```bash
export RESPAN_CLIENT_KEYS="sk-gw-$(openssl rand -hex 24)"

docker run --rm -p 8080:8080 \
  -e OPENAI_API_KEY \
  -e ANTHROPIC_API_KEY \
  -e RESPAN_CLIENT_KEYS \
  ghcr.io/respanai/respan-gateway:main
```

With no config file, every vendor whose key is set becomes a provider. To pass the keys in a `.env` file instead, use `--env-file .env`; the container can't see the `.env` files on your machine.

Call it with the client key as the API key:

```bash
curl http://localhost:8080/v1/chat/completions \
  -H "Authorization: Bearer $RESPAN_CLIENT_KEYS" \
  -H "Content-Type: application/json" \
  -d '{"model": "claude-haiku-5-5", "messages": [{"role": "user", "content": "Hello!"}]}'
```

See [Connect your app](/docs/oss-gateway/connect) for the SDKs, and [Client keys](/docs/oss-gateway/client-keys) for named and admin keys.

## Web UI

Open [localhost:8080/ui/](http://localhost:8080/ui/). Because the gateway requires a client key, the UI asks for one. If you name keys and mark some `admin = true`, only those reach the admin API the UI reads; otherwise every key does.

## Use a config file

For routes, tools or plugins, mount a directory holding `gateway.toml` and point the gateway at it with `--config` (or the `RESPAN_CONFIG` variable):

```bash
docker run --rm -p 8080:8080 \
  -e OPENAI_API_KEY -e ANTHROPIC_API_KEY -e APP_KEY \
  -v "$PWD/config:/config:ro" \
  ghcr.io/respanai/respan-gateway:main --config /config/gateway.toml
```

<Warning>
A `listen` in the config wins over `PORT`. `respan-gateway init` and the example config write `listen = "127.0.0.1:8080"`, which can't be reached from outside the container. In Docker, remove `listen` or set it to `"0.0.0.0:8080"`.
</Warning>

Keep secrets out of the file with `env:NAME`, and pass the variables to the container. This `config/gateway.toml` matches the command above:

```toml
client_keys = [{ name = "app", key = "env:APP_KEY" }]

[providers.openai]
vendor = "openai"
key = "env:OPENAI_API_KEY"

[providers.anthropic]
vendor = "anthropic"
key = "env:ANTHROPIC_API_KEY"
```

Keys in `RESPAN_CLIENT_KEYS` are added to the config's `client_keys`. Check a config before you deploy it:

```bash
docker run --rm -v "$PWD/config:/config:ro" \
  -e OPENAI_API_KEY -e ANTHROPIC_API_KEY -e APP_KEY \
  ghcr.io/respanai/respan-gateway:main check --config /config/gateway.toml
```

See [Config file](/docs/oss-gateway/config) for every setting.

## Docker Compose

```yaml
services:
  gateway:
    image: ghcr.io/respanai/respan-gateway:main
    command: ["--config", "/config/gateway.toml", "--watch"]
    ports:
      - "8080:8080"
    environment:
      OPENAI_API_KEY: ${OPENAI_API_KEY}
      ANTHROPIC_API_KEY: ${ANTHROPIC_API_KEY}
      APP_KEY: ${APP_KEY}
    volumes:
      - ./config:/config:ro
    restart: unless-stopped
```

Compose reads `${...}` values from your shell or a `.env` file next to `compose.yaml`.

## Reload the config

The gateway reloads its config file without a restart. Open requests finish on the old config, and an invalid config is refused while the old one keeps serving. A changed `listen` needs a restart.

- **`--watch`**: checks the file every 2 seconds, as in the Compose example. Mount the directory, not the single file, so edits show up inside the container.
- **SIGHUP**: `docker kill --signal HUP <container>`.
- **HTTP**: `POST /ui/api/reload` with an admin client key.

```bash
curl -X POST http://localhost:8080/ui/api/reload \
  -H "Authorization: Bearer $APP_KEY"
```

A reload reads the config file again. Environment variables are fixed for the container's life, so changing a key in the environment needs a restart.

## Build the image yourself

The repository's `Dockerfile` builds the web UI, compiles the gateway, and copies the binary into the distroless image:

```bash
git clone https://github.com/respanai/respan-gateway
cd respan-gateway
docker build -t respan-gateway .
```
