Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ Container image monorepo. Each subdirectory containing a `Containerfile` is buil
| `agent-pipe-uploader/` | `python:3.13-alpine` | Internal MCP artifact-transfer service using caller-supplied signed URLs |
| `tfroot-runner/` | `ghcr.io/actions/actions-runner:2.336.0` (Ubuntu) | gha-runner-scale-set runner with the OpenTofu IaC toolchain (kubectl, kustomize, sops, ansible, pre-commit, tflint, terraform-docs, infracost, checkov) |
| `gh-cli/` | `alpine:3.24` | Minimal `gh` image for automation Jobs |
| `claude-runner/` | `debian:bookworm-slim` | Claude Code self-hosted environments runner (`claude self-hosted-runner`) built per Anthropic's official image recipe |

## How It Works

Expand Down
32 changes: 32 additions & 0 deletions claude-runner/Containerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Claude Code self-hosted environments runner image.
#
# Anthropic does not publish a pre-built runner image; this mirrors their
# minimal runner Dockerfile from the official deploy docs (keep aligned):
# https://code.claude.com/docs/en/self-hosted-environments-deploy#build-the-runner-image
#
# Upgrade the fleet by bumping CLAUDE_CODE_VERSION and restarting the runners:
# every session runs the binary baked into this image (the runner disables
# auto-update inside the sessions it spawns).
FROM debian:bookworm-slim

# Upstream guards this ARG with :? and passes it via --build-arg; we carry a
# pinned default so this repo's buildah workflow builds without extra
# arguments. 2.1.224 is the minimum that recognizes `self-hosted-runner`.
ARG CLAUDE_CODE_VERSION=2.1.224

LABEL description="Claude Code self-hosted environments runner"
LABEL org.opencontainers.image.source="https://github.com/makeitworkcloud/images"

RUN apt-get update && apt-get install -y --no-install-recommends git curl ca-certificates openssh-client \
&& rm -rf /var/lib/apt/lists/*

# Standard Claude Code release location; verify against the release's signed
# manifest per https://code.claude.com/docs/en/setup#binary-integrity-and-code-signing
RUN curl -fsSL "https://downloads.claude.ai/claude-code-releases/${CLAUDE_CODE_VERSION}/linux-x64/claude" \
-o /usr/local/bin/claude && chmod +x /usr/local/bin/claude

RUN git config --system user.name "Claude" \
&& git config --system user.email "noreply@anthropic.com" \
&& git config --system --add safe.directory '*'

ENTRYPOINT ["claude"]
97 changes: 97 additions & 0 deletions claude-runner/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
# Claude Code self-hosted runner image

Runs Anthropic's Claude Code **self-hosted environments** runner (`claude
self-hosted-runner`) — the Claude-native execution fleet for
claude.makeitwork.cloud cloud sessions.

Anthropic does not publish a pre-built runner image. Per their instructions,
every organization builds its own image around the `claude` binary; this is
ours, kept as close to upstream as possible.

## Upstream instructions — authoritative

Operational truth for this fleet lives in Anthropic's documentation. This
image and any deployment of it defer to these pages:

- [Self-hosted environments](https://code.claude.com/docs/en/self-hosted-environments) —
how environments, runners, and sessions work; plan availability (public
beta, Team and Enterprise) and limitations
- [Quickstart](https://code.claude.com/docs/en/self-hosted-environments-quickstart) —
create the environment in the claude.ai admin UI, copy the environment key,
start a runner, route a session to it
- [Deploy to production](https://code.claude.com/docs/en/self-hosted-environments-deploy) —
**the page this Containerfile mirrors** ([Build the runner image](https://code.claude.com/docs/en/self-hosted-environments-deploy#build-the-runner-image)):
hardening, network requirements, git credentials, and the official
Kubernetes and Compose recipes
- [Customize sessions](https://code.claude.com/docs/en/self-hosted-environments-configuration) —
lifecycle hooks, wrapper scripts, on-demand runners, MCP servers
- [Test end to end](https://code.claude.com/docs/en/self-hosted-environments-testing) —
CI smoke test to run before promoting a new image
- [Reference](https://code.claude.com/docs/en/self-hosted-environments-reference) —
every runner CLI flag, env var, metric, and the health endpoint
- [Verify session identity](https://code.claude.com/docs/en/self-hosted-environments-identity) —
validating session tokens from adjacent services
- [Binary integrity and code signing](https://code.claude.com/docs/en/setup#binary-integrity-and-code-signing) —
verifying the downloaded binary against the release's signed manifest

## What the image contains — and deliberately omits

Contains (upstream's minimal set):

- `debian:bookworm-slim` with `git`, `curl`, `ca-certificates`, `openssh-client`
- The `claude` binary at `/usr/local/bin/claude`, pinned via
`CLAUDE_CODE_VERSION` from the standard release location
(`downloads.claude.ai/claude-code-releases`)
- System-wide git identity and `safe.directory '*'`, per upstream

Omits, per upstream's hardening guidance (["No broad credentials in the
image"](https://code.claude.com/docs/en/self-hosted-environments-deploy#harden-your-deployment)):

- **No environment secret** — mounted at runtime as a Kubernetes Secret and
passed via `--environment-secret-file` (their recipe)
- **No SSH keys, tokens, or git credentials** — configured per their
[Configure git](https://code.claude.com/docs/en/self-hosted-environments-deploy#configure-git)
options at deploy time

## Version pinning

Each session's child process runs the runner's own binary, and the runner
disables auto-update inside the sessions it spawns. Upgrade the fleet by
bumping `CLAUDE_CODE_VERSION`, rebuilding, and restarting the runners — see
[Pin the version](https://code.claude.com/docs/en/self-hosted-environments-deploy#pin-the-version).
`2.1.224` is the minimum release that recognizes the `self-hosted-runner`
subcommand.

## Deviations from upstream

Recorded here so drift stays visible:

1. Upstream guards `ARG CLAUDE_CODE_VERSION` with `:?` and passes it via
`--build-arg`; this Containerfile carries a pinned default so the repo's
buildah workflow builds without extra arguments.
2. House `LABEL` lines added per repo convention.

Everything else mirrors the docs' Dockerfile verbatim. Do not add further
drift without recording it in this section.

## Requirements from Anthropic's docs

- Plan: public beta on Team and Enterprise; an Owner must enable **Allow
self-hosted environments** on the Cloud environments admin page
- Claude Code ≥ 2.1.224 and git ≥ 2.24; `linux-x64` build (arm64/musl
variants documented upstream)
- Outbound HTTPS to `api.anthropic.com` and the hosts in their
[network requirements table](https://code.claude.com/docs/en/self-hosted-environments-deploy#network-requirements);
Anthropic makes no inbound connections
- NTP-synced clock — authentication fails when the clock is more than five
minutes off
- Billing: sessions consume the organization's Claude Code usage, the same as
Anthropic-hosted environments (subscription path, not per-token API)

## Ownership and delivery

`makeitworkcloud/images` owns this source image; its `main` workflow publishes
`ghcr.io/makeitworkcloud/claude-runner` after merge. Deployment wiring —
chart/Deployment, environment Secret, egress policy for the fleet — is not
yet authored; `charts` and `kustomize-cluster` ownership follows separately.
Authored, published, selected, and healthy remain distinct delivery stages.
Loading