Skip to main content
Self-hosted environments are in public beta on Team and Enterprise plans; Availability and limitations covers the enablement path. This page covers running the fleet in production; see the quickstart for your first runner and session.
A self-hosted environment runs Claude Code cloud sessions on runners you deploy inside your network, and in production those sessions execute model-directed code on behalf of anyone in your organization. This page is for the operator taking a working environment to production. It works through the deployment in order: what to lock down before connecting real systems, the egress the fleet needs, how sessions authenticate to your git host, the deployment recipes themselves, and what to check when sessions misbehave.

Harden your deployment

A self-hosted runner executes arbitrary, model-directed code on your infrastructure on behalf of any member of your Anthropic organization. Work through each item before you connect an environment to production systems:
  • Ephemeral, per-session containers: run each runner process in a fresh container or VM that’s destroyed when the process exits, with --capacity 1 and the default --drain-grace-sec 0 so each container serves exactly one session. At a higher capacity, or with a positive drain grace, one container serves multiple sessions from the same locked account; see Runner lifecycle. Don’t reuse a filesystem between runner restarts, except in the deliberate pre-warmed checkout setup, and never across accounts.
  • No broad credentials in the image: don’t include long-lived SSH keys, cloud-provider credentials, or personal access tokens that grant more than a session needs. Mint credentials used during a session, such as push or API tokens, per session from your wrapper script. For the initial clone, which happens before the wrapper runs, use a checkout lifecycle hook or --use-anthropic-git-proxy; see Configure git.
  • Keep the environment secret off session-running hosts: the environment secret can register runners and pick up any org member’s queued sessions. On a fixed fleet it lives on every runner host, where any session’s code can read the secret file. Prefer on-demand runners, where the secret stays on the orchestrator host, which never runs user code, and each runner receives a single-use work order that registers exactly one runner. On a fixed fleet, treat the environment-secret file as readable by every session and rotate the secret after any suspected session compromise.
  • Default-deny network egress: restrict runner and session container outbound traffic at your own network boundary on every environment; Default-deny egress covers what to allow and why.
  • Least-privilege host IAM: the compute identity attached to the runner host, such as an instance profile or node service account, should grant only what the runner itself needs. Sessions should obtain their own credentials through your wrapper script rather than inheriting the host’s.
  • Block the cloud metadata endpoint from sessions: keeping sessions off the host identity requires blocking their access to the cloud metadata endpoint, and subnet-level egress policies don’t intercept link-local metadata traffic, so block it in the container itself:
    • IMDSv2 with a hop limit of one
    • GKE Workload Identity with metadata concealment
    • An explicit deny for 169.254.169.254 in the session container’s network namespace
    The block applies to your wrapper script and lifecycle hooks too, since they share the container. Authenticate any token exchange with the session JWT against your own token service over allowlisted egress, or use a file-based web identity such as IAM Roles for Service Accounts (IRSA) on Amazon EKS.
  • Per-runner filesystem isolation: each runner process gets its own working directory that no other process on the host can read or write. Make --hooks-dir, the wrapper script, and the host’s ~/.claude/ read-only to the session, either built into the image or mounted read-only.
  • Dispatch is organization-wide: any member of your Anthropic organization can dispatch a session to any of its environments, and there’s no per-environment access control on dispatch. Treat every runner host as reachable for code execution by every org member, and place data or credentials on a runner host only if every org member is allowed to read them. --lock-to-account bounds which account’s sessions a given host executes, but dispatch into the environment itself stays organization-wide. To make self-hosted environments the only picker option, an Owner or admin can hide Anthropic-hosted environments for the whole organization from the Cloud environments page.
  • Enforce the repo-settings guard: choose the guard mode with --confine-repo-settings. The default warn logs a violation and still spawns the session, enforce refuses the session, and off disables the scan. The runner scans each repository’s committed settings for:
    • A grant that resolves outside that session’s own workspace: an additionalDirectories entry, an Edit, Write, or NotebookEdit rule in permissions.allow, or a sandbox.filesystem.allowWrite or allowRead entry
    • A non-empty env block
    • An operator-posture override such as sandbox.enabled: false
    The guard runs regardless of --trust-workspace, and doesn’t cover repository hooks, .mcp.json, or Bash rules; see Permissions and tool approval for where those grants belong.
Your organization’s IP allowlist doesn’t cover self-hosted runner traffic by default. Don’t rely on it as a network control for runner or session traffic; apply default-deny egress at your own network boundary instead, and contact your Anthropic account team if you want IP-allowlist enforcement for your organization.

Network requirements

The runner and the session children it spawns make outbound connections to the hosts below. Restrict session-container egress to these hosts and the specific internal services sessions need to reach; Default-deny egress covers how and why. These hosts are always required: Whether these hosts are needed depends on your configuration: The runner doesn’t reach statsig.anthropic.com, *.sentry.io, claude.ai, or platform.claude.com. These hosts appear in some older enterprise network checklists, but you don’t need to allowlist them for runner or session traffic: feature-flag fetches go to api.anthropic.com, and the runner authenticates with the environment secret rather than interactive OAuth. Two host-side flows do reach claude.ai, so run them from a host whose egress allows it rather than widening session-container egress: the one-line installer fetches install.sh from claude.ai at install time, and interactive claude auth login, which the guided setup, doctor’s signed-in mode, and CI dispatch use, signs in through claude.ai, claude.com, and platform.claude.com. mcp-proxy.anthropic.com isn’t required either: self-hosted sessions don’t use it, and delivery of your organization’s claude.ai connectors to sessions, when enabled for your organization, routes through api.anthropic.com. See MCP servers.

Default-deny egress

Deploy runner and session containers in a network segment or namespace whose outbound traffic is limited to the hosts in the network requirements table, your git host, and the specific internal services sessions need to reach. The product can’t verify or enforce this, so apply it at your own network boundary on every environment. Session code is model-directed and can attempt connections to arbitrary hosts; default-deny egress at the network layer bounds where those attempts can land. This applies regardless of permission mode: the default pre-approved tool set already includes Bash, so shell egress runs without a prompt even without auto mode. For details on which telemetry each session emits and how to turn it off, see Telemetry.

Configure git

The runner manages repository checkouts but doesn’t configure git identity or credentials by default. You control the runner’s image and process environment, so you control the git config. Choose one of two approaches:
  • Let the runner configure git: start the runner with --configure-git to have it write the same identity and commit-signing config that Anthropic-hosted sessions use
  • Ship git config in your image: set identity and push credentials yourself, for example to commit under your own bot identity
Git version floors on the runner host: --configure-git SSH commit signing requires Git 2.34 or newer, --use-anthropic-git-proxy requires 2.32 or newer, and resuming sessions from branches pushed by --push-outcome-on-release requires 2.29 or newer. Git 2.24 is sufficient if you omit all three and manage git identity yourself.

Let the runner configure git

Start the runner with --configure-git, or set SELF_HOSTED_RUNNER_CONFIGURE_GIT=1, to have it write global git config at startup:
  • user.name = Claude and user.email = noreply@anthropic.com, matching Anthropic-hosted sessions
  • SSH-format commit and tag signing, routed through a runner-managed shim that signs each commit via Anthropic’s signing service using the session’s own credentials. Signatures are verifiable on GitHub against Anthropic’s published SSH signing key.
Commit signing requires git 2.34 or newer; the runner checks at startup and exits with an error if your git is older. This flag doesn’t configure push credentials, which you still provide in the image.

Ship git config in your image

Git identity is required for any commit. Set it system-wide in your Dockerfile so the config applies regardless of which user the runner process runs as: