Tawa docs

VPS deployment

Run tawa-codex-proxy on a server for a team or CI runners, instead of one developer's laptop.

The Docker page covers running the proxy next to Codex CLI on your own machine. That's the right setup for one developer. It stops being the right setup once you want it to keep running when your laptop is closed, or want every developer/CI runner on a team going through the same relay instead of each running their own.

There's no pre-built docker-compose.yml, Terraform, or one-command deploy script for tawa-cli-proxy yet — same gap as the Claude Code relay. Everything below is the same pattern applied by hand.

Two ways to run it on a server

Both work; pick based on what you'd rather operate.

ContainerNative (systemd)
Host requirementsDocker onlyRust toolchain to build, nothing to run
Image/binary sourceDockerfile.codex, same as localSame cargo build --release as local
Process supervisionDocker's own restart policysystemd
Fits if...You already run other containers on this boxYou'd rather avoid a container runtime entirely

Option A: container

Same image as Docker, built and run directly on the VPS:

docker build -f Dockerfile.codex -t tawa-codex-proxy .
docker run -d \
  --name tawa-codex-proxy \
  --restart unless-stopped \
  -p 127.0.0.1:8788:8788 \
  -e TAWA_API_KEY=<your Tawa API key> \
  tawa-codex-proxy

Binding to 127.0.0.1 here too, even on a server — the reverse proxy in front (below) is what actually terminates external connections.

Option B: native binary under systemd

cargo build --release --locked -p tawa-cli-proxy-codex
sudo cp target/release/tawa-codex-proxy /usr/local/bin/tawa-codex-proxy
/etc/systemd/system/tawa-codex-proxy.service
[Unit]
Description=tawa-codex-proxy
After=network-online.target
Wants=network-online.target

[Service]
ExecStart=/usr/local/bin/tawa-codex-proxy
EnvironmentFile=/etc/tawa-codex-proxy.env
Restart=on-failure
DynamicUser=yes

[Install]
WantedBy=multi-user.target
/etc/tawa-codex-proxy.env
TAWA_API_KEY=...
TAWA_PROXY_LISTEN_ADDR=127.0.0.1:8788
sudo systemctl enable --now tawa-codex-proxy
sudo journalctl -u tawa-codex-proxy -f

Put TLS in front of it

Whichever option you pick, the proxy itself only speaks plain HTTP. Once OPENAI_BASE_URL points at something other than localhost, every request through it — including the real OPENAI_API_KEY Codex CLI sends in the Authorization header — crosses whatever network sits between the developer's machine and this server. Don't point OPENAI_BASE_URL at a plain http:// address on the open internet.

Terminate TLS with a reverse proxy in front instead of exposing :8788 directly — the same Caddy pattern used for the Claude Code relay's VPS deployment:

Caddyfile
proxy.yourdomain.com {
	reverse_proxy 127.0.0.1:8788
}

Then point Codex CLI at the HTTPS address instead of the container port:

export OPENAI_BASE_URL=https://proxy.yourdomain.com/v1
export OPENAI_API_KEY=<your real OpenAI key>

This server can spend your OpenAI key

Anyone who can reach this endpoint can route OpenAI-billed traffic through whatever OPENAI_API_KEY a client sends it — the proxy relays that header verbatim and never checks who's presenting it. Restrict access at the network layer (firewall rule, VPN, IP allowlist, or auth at the reverse proxy) the same way you'd restrict access to any internal service that can spend money on your behalf.

Next

See Configuration for the full environment variable reference, and note the streamed responses this proxy relays can run for minutes on a long agentic turn — size any load balancer/reverse-proxy idle-timeout in front of it accordingly.

On this page