Deploying with Docker
v1 status: stub. Full Dockerfile walkthrough TBD. The rantaiclaw repo ships a
Dockerfileanddocker-compose.ymlyou can use directly. The default container command israntaiclaw gateway(ENTRYPOINT rantaiclaw,CMD gateway), exposing port3000.
Volume you should mount
The image keeps everything under a single $HOME directory — /rantaiclaw-data — set in the Dockerfile (HOME=/rantaiclaw-data, WORKDIR /rantaiclaw-data). Mount one named volume there and every piece of state survives restarts.
| Container path | Holds | Persist? |
|---|---|---|
/rantaiclaw-data ($HOME) | .rantaiclaw/config.toml, workspace/, memory DB, secret store, audit log, installed skills, and the Knowledge Base database | Yes — mount a named volume |
The shipped docker-compose.yml does exactly this:
volumes:
- rantaiclaw-data:/rantaiclaw-dataInside the container the config lives at /rantaiclaw-data/.rantaiclaw/config.toml and the workspace at /rantaiclaw-data/workspace — the running user is the non-root 65534:65534, so a named volume avoids host-permission friction.
Health check
healthcheck:
test: ["CMD", "rantaiclaw", "status"]
interval: 60s
timeout: 10s
retries: 3
start_period: 10sThe shipped docker-compose.yml uses the lightweight rantaiclaw status rather than the heavier rantaiclaw doctor (which runs full daemon/scheduler/channel diagnostics). For images that include curl, curl -f http://localhost:3000/health is an equivalent target — /health is an always-public gateway route (src/gateway/mod.rs).
Gateway exposure inside Docker
The gateway config keys are host, port, require_pairing, and allow_public_bind, all under [gateway] (defined in src/config/schema.rs). Outside a container the defaults are deliberately locked down: host = "127.0.0.1", allow_public_bind = false, require_pairing = true. The runtime refuses to start if host is a public address (e.g. 0.0.0.0 / [::]) unless a tunnel is configured or allow_public_bind = true is set.
Inside a container that loopback default would make the published port unreachable, so the shipped image sets host = "[::]" and allow_public_bind = true. This is not an open door: require_pairing = true still gates every request, and you control exposure by which host port you publish and to what network.
- Do not treat
allow_public_bind = truein the image as “safe to expose to the internet” — it only relaxes the in-container bind check. - Keep
require_pairing = true(the default). Every gateway client must pair first. - Publish the host port to a trusted network only. For host-only access, bind the published port to loopback (
127.0.0.1:3000:3000); for anything wider, front the gateway with a tunnel or reverse proxy.
Common pitfalls
- Empty channel allowlist. A channel with an empty allowlist is offline, not open (deny-by-default). Run
rantaiclaw doctorin CI and fail on any non-OK result to catch a channel that is enabled but unreachable. - Forgetting
allow_public_bind. If you sethost = "0.0.0.0"(or[::]) in[gateway]you must also setallow_public_bind = true, or the gateway refuses to start. Only do this behind pairing plus a trusted network boundary. - Landlock kernel support. On Linux the Landlock sandbox compiles automatically (the legacy
sandbox-landlockfeature flag is now a no-op) and activates at runtime on kernel 5.13+; on older kernels the runtime falls back to allowlist-only enforcement. Pin a base image with kernel 5.13+ for full sandboxing.