Tawa docs

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-proxy

Bind 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
# ok

The 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>
claude

Configuration 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-proxy
docker logs -f tawa-claude-proxy

RUST_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-proxy

For a shared, always-on setup instead of a single developer's machine, see VPS deployment.

On this page