Skip to content

Run Claude Code in a kukeon cell

This guide walks a freshly-kuke init-ed host from zero to a live Claude Code prompt running inside a kukeon cell — first as a long-lived Attachable Cell, then as a parametrized CellProfile you can drive with kuke run -p for one-shot prompts.

Claude Code is named in the Vision as the canonical agent workload kukeon's isolation primitives are measured against. The companion artifacts under docs/examples/claude-code/ are the smallest end-to-end shape: an image, a Cell, and a CellProfile.

The example does not bake API keys into the image and does not configure auth — bring your own per the upstream setup docs. For the full agent-runner shape (SSH bind, signed commits, agents/project-repo clones), see eminwux/crew — this guide is derived from crew/images/claude/Dockerfile and crew/agents/pm/profiles/pm.yaml with that plumbing stripped out.

Prerequisites

  • A host that has been bootstrapped with kuke init. kuke get realms shows both default and kuke-system Ready.
  • A local Docker (or nerdctl build) install for building the image.
  • A copy of the three files under docs/examples/claude-code/:
  • Dockerfile
  • cell.yaml
  • profile.yaml

Step 1 — Build the image

The example Dockerfile is the upstream crew image verbatim. It installs nodejs + npm + the @anthropic-ai/claude-code package as a non-root claude user on debian:trixie-slim:

cd docs/examples/claude-code
docker build -t claude-code:latest .

nerdctl build -t claude-code:latest . works the same way if you don't run Docker.

What's in the upstream image that the smoke path doesn't need

The crew Dockerfile carries a handful of fleet-runner extras the Claude Code smoke does not exercise; they are kept verbatim so this Dockerfile remains drop-in usable on a crew-style host:

  • golang, gh, pre-commit, gnupg, ssh, jq, yq, make, gettext-base — the toolchain crew's /dispatch agents need to clone repos, sign commits, and run pre-commit. The smoke in this guide does not invoke any of them; trim them if you want a leaner image.
  • groupadd -g 988 kukeon / usermod -aG kukeon claude — fleet-specific. On crew's hosts gid 988 matches the host containerd socket group, so the claude user inside the cell can talk to a host-mounted socket. On a freshly-kuke init-ed host you do not bind-mount the containerd socket into the cell, so the supplementary group is inert. Leave it in if you ever expect to bind the socket; drop it for a strictly-Claude-Code image.

The smoke path needs only nodejs, npm, the @anthropic-ai/claude-code package, and the non-root claude user.

Step 2 — Load the image into the default realm

Every realm maps to its own containerd namespace (Containerd namespaces). User workloads live in default.kukeon.io, so the cell's image has to be loaded into that namespace before kuke apply / kuke run can pull it:

sudo kuke image load --from-docker claude-code:latest

--realm default is the default, so it can be omitted. Verify:

sudo kuke image get | grep claude-code

If you don't run Docker, use the nerdctl save … | sudo kuke image load - form documented in kuke image.

Step 3 — Apply and attach the Cell

cell.yaml is a single Attachable Cell with two containers: a busybox root keeping the cell alive (sleep infinity) and a work container running the Claude Code image, marked attachable: true so kuke attach can connect a TTY to it.

sudo kuke apply -f docs/examples/claude-code/cell.yaml

kuke apply creates the cell. If you hit a daemon issue, retry in-process.

Then connect a terminal to the work container:

sudo kuke attach claude-code

You should land at a claude> prompt inside the cell. From there, run claude to enter the upstream Claude Code REPL (you'll need to complete the upstream auth flow the first time — the example does not pre-bake an API key).

Press ^]^] (two consecutive Ctrl-] keystrokes) to detach cleanly. The cell keeps running; re-attach later with the same command.

Tear-down

sudo kuke delete cell claude-code --cascade

--cascade removes the cell's containers in the same call. If kuke delete fails because the daemon is down, fall back to in-process mode the same way kuke apply does — see Applying manifests.

Optional: persist ~/.claude across restarts

The smoke path above keeps Claude Code's per-user state inside the cell's overlay. To survive kuke delete cell + re-apply, bind-mount a host directory under the work container:

- id: work
  attachable: true
  image: docker.io/library/claude-code:latest
  command: /bin/bash
  workingDir: /home/claude
  volumes:
    - source: /var/lib/claude-code/state
      target: /home/claude/.claude
  tty:
    prompt: "claude> "

This is optional — the smoke flow above does not need it.

Step 4 — One-shot prompts via a CellProfile

For "fire one prompt, tear the cell down on exit" jobs, install the example profile and drive it with kuke run -p. A CellProfile is a per-user reusable cell template loaded from $HOME/.kuke/profiles.d/<name>.yaml (or $KUKE_PROFILES_DIR).

mkdir -p ~/.kuke/profiles.d
cp docs/examples/claude-code/profile.yaml ~/.kuke/profiles.d/claude-code.yaml

The profile declares two parameters: IMAGE (defaults to docker.io/library/claude-code:latest) and PROMPT (required). Its tty.onInit runs claude --print "${PROMPT}" && exit, so the attach loop closes after the prompt's reply prints. spec.cell.autoDelete: true then deletes the materialized cell — no --rm flag required.

Run it:

sudo kuke run -p claude-code \
    --param PROMPT="explain the kukeon architecture in one sentence"

kuke run -p materializes a cell named claude-code-<6hex>, attaches to the work container, runs the onInit script, prints Claude Code's reply, and exits. Override IMAGE to pin a tag:

sudo kuke run -p claude-code \
    --param IMAGE=docker.io/library/claude-code:2026-05-14 \
    --param PROMPT="…"

See kuke run for the full flag surface, including --name, --param-file, and the -d/--detach mode.

Where to go next

  • The full crew agent-runner shape. eminwux/crew — SSH bind, GPG-signed commits, agents-repo clone, project-repo clone, the orchestrator that dispatches per-call cells. Two layers up from this guide.
  • Cell teardown verbs. docs/cli-use-cases.md — the full operator workflow reference, including stop / kill / delete / purge --cascade for cells.
  • Manifest reference. Applying manifests — multi-doc manifests, the in-process escape hatch, profile parameter resolution order.