Docker
Run tawa-claude-proxy as a local container — no Rust toolchain on the host, and an easy way to keep it always-on.
tawa-cli-proxy ships a Dockerfile at the repo root. Building it gives you
a self-contained image with no Rust toolchain, cmake, nasm, or perl
needed on the host — only Docker.
No image is published to a registry yet — there's no docker pull to run.
You build it locally from source. Publishing a versioned image (GitHub
Container Registry, matching how tawa-gateway and the Tawa Guard workers ship
today) is the natural next step here, not yet done.
Build the image
From the tawa-cli-proxy repo root:
docker build -t tawa-claude-proxy .This is a multi-stage build: a rust:1.90-slim-bookworm builder stage
compiles a release binary with cargo build --release --locked (--locked
so the build fails fast if Cargo.lock ever drifts from Cargo.toml,
rather than silently re-resolving dependency versions), then the binary is
copied into a minimal debian:bookworm-slim runtime image running as a
non-root user. Nothing from the builder stage — the Rust toolchain, cached
crates, intermediate object files — ships in the final image.
Run it
docker run -d \
--name tawa-claude-proxy \
-p 127.0.0.1:8787:8787 \
-e TAWA_API_KEY=<your Tawa API key> \
tawa-claude-proxyBind to loopback
-p 127.0.0.1:8787:8787 (not just -p 8787:8787) publishes the port to
the host's loopback interface only. Omitting the 127.0.0.1: prefix
publishes it on every network interface, including anything reachable from
outside your machine — this proxy has no authentication of its own beyond
whatever key Claude Code hands it, so exposing it beyond localhost on a
laptop is a real risk, not a theoretical one.
The image sets TAWA_PROXY_LISTEN_ADDR=0.0.0.0:8787 by default (rather than
the binary's own 127.0.0.1:8787 default — see
Configuration). This has to be 0.0.0.0
inside the container, or Docker's own port-forwarding can't reach it — the
-p 127.0.0.1:8787:8787 flag above is what actually restricts external
access, not this setting.
Check it's up:
docker ps --filter name=tawa-claude-proxy
curl http://127.0.0.1:8787/healthz
# okThe image also defines a HEALTHCHECK against /healthz, so
docker ps/docker inspect report container health directly — useful if
you run this under a supervisor that restarts unhealthy containers.
Point Claude Code at it
Same as the source-build path — Claude Code doesn't know or care whether
what's listening on 127.0.0.1:8787 is a container or a native process:
export ANTHROPIC_BASE_URL=http://127.0.0.1:8787
export ANTHROPIC_API_KEY=<your real Anthropic key>
claudeConfiguration and logs
Pass any of the environment variables
with -e:
docker run -d \
--name tawa-claude-proxy \
-p 127.0.0.1:8787:8787 \
-e TAWA_API_KEY=<your Tawa API key> \
-e TAWA_PROXY_FAIL_MODE=closed \
-e RUST_LOG=debug \
tawa-claude-proxydocker logs -f tawa-claude-proxyRUST_LOG isn't Tawa-specific — it's the standard tracing-subscriber
env filter the binary uses for its structured logs (info by default).
Keep it running
For a single laptop, --restart unless-stopped is usually enough so the
proxy survives a reboot without a supervisor:
docker run -d \
--name tawa-claude-proxy \
--restart unless-stopped \
-p 127.0.0.1:8787:8787 \
-e TAWA_API_KEY=<your Tawa API key> \
tawa-claude-proxyFor a shared, always-on setup instead of a single developer's machine, see VPS deployment.