Extending Takuto Core

You will often need tools that aren’t baked into the official image — kubectl, terraform, an internal vendor CLI, or a pinned version of claude. Takuto Core has a three-tier extension model, plus a fourth path for runtime-only tweaks. Pick the one that matches your need.

Your needMechanismWhere to write it
Add a single binary CLI (kubectl, terraform, vendor CLI)Provisioning[provisioning].install_commands in config.toml
Pin a baked tool to a specific versionProvisioning (PATH shadowing)[provisioning].install_commands
Add system packages (apt) or librariesCustom DockerfileNew Dockerfile FROM ghcr.io/takuto-team/takuto-core:latest
Add an environment variable everywhereCompose overridedocker-compose.override.yml
Mount an extra host directoryCompose overridedocker-compose.override.yml

The guiding principle: Baked = required for advertised features, Provisioning = admin preferences, Custom image = specialized OS-level needs.

Mechanism 1 — [provisioning].install_commands

The most common case. At every Takuto startup, the entrypoint runs a SHA-gated install pass:

  1. It hashes the install_commands list.
  2. If the hash matches the last successful run → skip (the fast path; runs every boot when nothing changed).
  3. If it differs → it exports TAKUTO_TOOLS_BIN=/opt/takuto-tools/bin and runs each command via bash -c as root. Per-command failures log a warning but don’t abort. The new hash is recorded only on full success, so partial failures retry next boot.

The tools land in the takuto-tools Docker volume, bind-mounted read-only into every worker, editor, and run-command container, with /opt/takuto-tools/bin first on $PATH — so anything you drop there is available everywhere and shadows baked tools of the same name.

Idempotency is your responsibility

Re-runs happen whenever the list changes — including when you add a command (the existing ones re-run too). Guard each command with an existence check:

[provisioning]
install_commands = [
  '[ -f "$TAKUTO_TOOLS_BIN/kubectl" ] || (curl -fsSLo "$TAKUTO_TOOLS_BIN/kubectl" https://dl.k8s.io/release/v1.31.0/bin/linux/amd64/kubectl && chmod +x "$TAKUTO_TOOLS_BIN/kubectl")',
]

After a restart, every workflow can call kubectl directly.

Pin claude to a specific version

[provisioning]
install_commands = [
  '[ -f "$TAKUTO_TOOLS_BIN/claude" ] || (npm install -g --prefix "$TAKUTO_TOOLS_BIN/.npm" @anthropic-ai/claude-code@2.1.140 && ln -sf "$TAKUTO_TOOLS_BIN/.npm/bin/claude" "$TAKUTO_TOOLS_BIN/claude")',
]

PATH precedence makes the pinned version win over the runtime-installed @latest. To revert: remove the line, restart, and rm /opt/takuto-tools/bin/claude.

Install a private vendor CLI

install_commands = [
  '[ -f "$TAKUTO_TOOLS_BIN/mycli" ] || curl -fsSL -H "Authorization: token $MYCO_TOKEN" https://internal.example.com/cli/mycli-v2.tgz | tar -xz -C "$TAKUTO_TOOLS_BIN"',
]

Set MYCO_TOKEN in takuto.env so the install command can see it.

Force a re-install without changing config

The SHA gate skips re-runs when the list is unchanged. To force one (e.g. you suspect a broken binary):

docker exec --user root takuto-core-takuto-1 \
    rm /opt/takuto-tools/.provisioning-sha
docker compose restart takuto

Wipe all customizations

docker compose down
docker volume rm takuto-core_takuto-tools
docker compose up -d

Mechanism 2 — Custom Dockerfile

Use this for system packages (apt libraries, daemons) or anything that touches multiple OS directories (/etc, /usr/share, /var/lib). The takuto-tools volume only writes a single bin/ directory, so provisioning is the wrong tool for system-wide installs.

# my-takuto.Dockerfile
FROM ghcr.io/takuto-team/takuto-core:latest
USER root
RUN apt-get update && apt-get install -y --no-install-recommends \
        awscli postgresql-client \
    && rm -rf /var/lib/apt/lists/*
USER takuto

Wire it into docker-compose.yml:

services:
  takuto:
    build:
      context: .
      dockerfile: my-takuto.Dockerfile

Then docker compose build takuto && docker compose up -d.

Mechanism 3 — docker-compose.override.yml

Compose merges docker-compose.override.yml automatically. Use it for runtime-only tweaks that don’t need a rebuild.

Extra environment variables:

services:
  takuto:
    environment:
      - GH_HOST=github.example.com   # internal GHE instance
      - MY_API_TOKEN=...

Extra bind mount:

services:
  takuto:
    volumes:
      - /host/shared-cache:/home/takuto/.cache/shared:ro

Compose overrides affect the takuto service only. To propagate a mount or env var to spawned worker containers, the worker volume / passthrough-env lists in the engine must include them — see the Takuto Core source for WORKER_VOLUMES and PASSTHROUGH_ENV.

Tool inventory at a glance

  • Baked (required for advertised features): node, npm, the Rust toolchain (cargo/rustfmt/clippy), git, gh, jq, docker CLI, iptables, ttyd, openvscode-server, mise, and the Playwright/Chromium libraries.
  • Installed at first start (not baked into the image): the agent CLIs claude, codex, opencode, cursor-agent, and the Atlassian CLI acli — fetched into the shared takuto-tools volume, versioned per config.toml ([agent.providers.*].version, [jira].acli_version). See Configuration.
  • Provisioning defaults (admin can pin/replace/disable): single-binary tools like the Figma and Lokalise CLIs.
  • Removed (handle via custom image): tools like AWS CLI v2 that only a minority of deployments need.

Troubleshooting provisioning

  • “My command isn’t running on restart.” The SHA gate skips unchanged lists — edit something (even a comment) to change the hash, or rm /opt/takuto-tools/.provisioning-sha and restart.
  • “Workers don’t see my tool.” Check it landed in the volume (docker exec takuto-core-takuto-1 ls /opt/takuto-tools/bin/) and that /opt/takuto-tools/bin is first on the worker’s $PATH. Look for [provisioning] … WARN lines in the boot log for install failures.

For more, see troubleshooting.