{
  "manifest_version": "1",
  "slug": "claude-code-runner",
  "title": "Claude Code self-hosted runner",
  "headline": "Run a Claude Code self-hosted runner",
  "description": "Turn an exe.dev VM into a Claude Code self-hosted runner: sessions you start from claude.ai, the Claude apps, or claude --cloud run on your VM.",
  "category": "ai-agents",
  "tags": [
    "claude code",
    "claude self-hosted-runner",
    "self-hosted runner",
    "self-hosted environments",
    "claude code on the web",
    "claude --cloud",
    "cloud sessions",
    "anthropic"
  ],
  "url": "https://exe.dev/deploy/claude-code-runner",
  "markdown_url": "https://exe-staging.dev/deploy/claude-code-runner.md",
  "upstream": {
    "website": "https://claude.com/product/claude-code",
    "docs": "https://code.claude.com/docs/en/self-hosted-environments",
    "license": "Proprietary (Anthropic Commercial Terms)"
  },
  "example_vm_name": "my-claude-code-runner",
  "time": "10 minutes",
  "stack": [
    {
      "name": "Claude Code runner",
      "detail": "`claude self-hosted-runner`, the claude-runner systemd service, as the claude-runner user",
      "port": 8080,
      "data": "/home/claude-runner/work"
    },
    {
      "name": "Runner settings",
      "detail": "the environment key, and the account the runner serves",
      "data": "/etc/claude"
    }
  ],
  "access": [
    {
      "url": "ssh my-claude-code-runner.exe.xyz",
      "who": "private",
      "what": "a shell on the machine your sessions run on"
    },
    {
      "url": "https://my-claude-code-runner.exe.xyz:8080/healthz",
      "who": "private",
      "what": "the runner's health check"
    },
    {
      "url": "https://my-claude-code-runner.exe.xyz:3000/",
      "who": "private",
      "what": "anything a session serves on a port from 3000 to 9999"
    }
  ],
  "inputs": [
    {
      "name": "CLAUDE_ENVIRONMENT_SECRET",
      "description": "the environment key claude.ai showed once when you created the self-hosted environment"
    },
    {
      "name": "CLAUDE_RUNNER_ACCOUNT",
      "description": "the email of the claude.ai account whose sessions this runner serves, or `none` for a runner anyone in your organization can use"
    }
  ],
  "delivers": [
    "Claude Code 2.1.224 or later installed for a dedicated `claude-runner` user",
    "The environment key in /etc/claude/environment-secret, readable only by `claude-runner`",
    "The claude-runner systemd service, enabled so it comes back after a reboot",
    "`Registered: runner_id=` in `journalctl -u claude-runner`, with no `[runner:fatal]` line after it"
  ],
  "deployable": true,
  "deploy": {
    "new_vm_url": "https://exe-staging.dev/new?prompt=CLAUDE_ENVIRONMENT_SECRET%3D%3Cfill-this-in%3E%0ACLAUDE_RUNNER_ACCOUNT%3D%3Cfill-this-in%3E%0A%0ASet%20up%20Claude%20Code%20self-hosted%20runner%20on%20this%20VM%20by%20following%20the%20exe.dev%20recipe%20at%20https%3A%2F%2Fexe-staging.dev%2Fdeploy%2Fclaude-code-runner.md%20from%20top%20to%20bottom.%0A%0AThe%20values%20at%20the%20top%20of%20this%20message%20are%20mine%3A%0A-%20CLAUDE_ENVIRONMENT_SECRET%3A%20the%20environment%20key%20claude.ai%20showed%20once%20when%20you%20created%20the%20self-hosted%20environment%0A-%20CLAUDE_RUNNER_ACCOUNT%3A%20the%20email%20of%20the%20claude.ai%20account%20whose%20sessions%20this%20runner%20serves%2C%20or%20%60none%60%20for%20a%20runner%20anyone%20in%20your%20organization%20can%20use%0AAsk%20me%20for%20any%20that%20still%20say%20%3Cfill-this-in%3E%20before%20you%20need%20them.%0A%0AThe%20recipe%27s%20example%20VM%20name%20is%20my-claude-code-runner.%20This%20VM%27s%20name%20is%20the%20output%20of%20%60hostname%60%3A%20use%20it%20wherever%20the%20recipe%20writes%20my-claude-code-runner.%20Skip%20the%20step%20that%20creates%20the%20VM%3B%20you%20are%20on%20it.%0A%0AYou%20are%20done%20when%20all%20of%20these%20are%20true.%20Check%20each%20one%20yourself%3A%0A-%20Claude%20Code%202.1.224%20or%20later%20installed%20for%20a%20dedicated%20%60claude-runner%60%20user%0A-%20The%20environment%20key%20in%20%2Fetc%2Fclaude%2Fenvironment-secret%2C%20readable%20only%20by%20%60claude-runner%60%0A-%20The%20claude-runner%20systemd%20service%2C%20enabled%20so%20it%20comes%20back%20after%20a%20reboot%0A-%20%60Registered%3A%20runner_id%3D%60%20in%20%60journalctl%20-u%20claude-runner%60%2C%20with%20no%20%60%5Brunner%3Afatal%5D%60%20line%20after%20it%0A%0ARun%20%22Set%20up%20the%20runner%20by%20hand%22%20from%20%22On%20the%20VM%22%2C%20skipping%20VM%20creation%0Aand%20SSH.%20CLAUDE_ENVIRONMENT_SECRET%20is%20a%20secret%3A%20write%20it%20to%0A%2Fetc%2Fclaude%2Fenvironment-secret%20with%0A%60printf%20%27%25s%5Cn%27%20%22%24value%22%20%7C%20sudo%20-u%20claude-runner%20sh%20-c%20%27cat%20%3E%20%2Fetc%2Fclaude%2Fenvironment-secret%27%60%0Ainstead%20of%20the%20interactive%20paste%2C%20and%20never%20print%20it.%20If%0ACLAUDE_RUNNER_ACCOUNT%20is%20%60none%60%2C%20skip%20%2Fetc%2Fclaude%2Frunner.env.%20Don%27t%20start%0Aa%20session%3A%20that%20happens%20on%20claude.ai.%20If%20the%20log%20says%20%60RegisterRunner%0Aauth%20failed%60%2C%20tell%20the%20owner%20the%20environment%20key%20is%20wrong%20or%20revoked.%0AFinish%20by%20pointing%20the%20owner%20to%20%22Start%20a%20session%20on%20your%20VM%22.%0A%0AFinish%20with%20the%20URLs%20I%20should%20open.%0A",
    "terminal": "curl -fsSL https://exe-staging.dev/deploy/claude-code-runner/prompt.txt | ssh exe.dev new --prompt=/dev/stdin",
    "prompt": "CLAUDE_ENVIRONMENT_SECRET=\u003cfill-this-in\u003e\nCLAUDE_RUNNER_ACCOUNT=\u003cfill-this-in\u003e\n\nSet up Claude Code self-hosted runner on this VM by following the exe.dev recipe at https://exe-staging.dev/deploy/claude-code-runner.md from top to bottom.\n\nThe values at the top of this message are mine:\n- CLAUDE_ENVIRONMENT_SECRET: the environment key claude.ai showed once when you created the self-hosted environment\n- CLAUDE_RUNNER_ACCOUNT: the email of the claude.ai account whose sessions this runner serves, or `none` for a runner anyone in your organization can use\nAsk me for any that still say \u003cfill-this-in\u003e before you need them.\n\nThe recipe's example VM name is my-claude-code-runner. This VM's name is the output of `hostname`: use it wherever the recipe writes my-claude-code-runner. Skip the step that creates the VM; you are on it.\n\nYou are done when all of these are true. Check each one yourself:\n- Claude Code 2.1.224 or later installed for a dedicated `claude-runner` user\n- The environment key in /etc/claude/environment-secret, readable only by `claude-runner`\n- The claude-runner systemd service, enabled so it comes back after a reboot\n- `Registered: runner_id=` in `journalctl -u claude-runner`, with no `[runner:fatal]` line after it\n\nRun \"Set up the runner by hand\" from \"On the VM\", skipping VM creation\nand SSH. CLAUDE_ENVIRONMENT_SECRET is a secret: write it to\n/etc/claude/environment-secret with\n`printf '%s\\n' \"$value\" | sudo -u claude-runner sh -c 'cat \u003e /etc/claude/environment-secret'`\ninstead of the interactive paste, and never print it. If\nCLAUDE_RUNNER_ACCOUNT is `none`, skip /etc/claude/runner.env. Don't start\na session: that happens on claude.ai. If the log says `RegisterRunner\nauth failed`, tell the owner the environment key is wrong or revoked.\nFinish by pointing the owner to \"Start a session on your VM\".\n\nFinish with the URLs I should open.\n",
    "prompt_url": "https://exe-staging.dev/deploy/claude-code-runner/prompt.txt"
  },
  "updated": "2026-10-30",
  "verified": "2026-10-30",
  "related": [
    "claude-code",
    "claude-managed-agents",
    "github-actions-runner",
    "openclaw",
    "codex",
    "codex-remote-control"
  ]
}
