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.
| Container | Native (systemd) | |
|---|---|---|
| Host requirements | Docker only | Rust toolchain to build, nothing to run |
| Image/binary source | Dockerfile.codex, same as local | Same cargo build --release as local |
| Process supervision | Docker's own restart policy | systemd |
| Fits if... | You already run other containers on this box | You'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-proxyBinding 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[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.targetTAWA_API_KEY=...
TAWA_PROXY_LISTEN_ADDR=127.0.0.1:8788sudo systemctl enable --now tawa-codex-proxy
sudo journalctl -u tawa-codex-proxy -fPut 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:
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.