{
  "manifest_version": "1",
  "slug": "github-actions-runner",
  "title": "GitHub Actions runner",
  "headline": "Running a self-hosted GitHub Actions Runner",
  "description": "There's very little to it; you're following the GitHub instructions, but then doing a little bit of systemd work to make sure the runner keeps running.",
  "category": "dev-tools",
  "tags": [
    "github actions",
    "self-hosted runner",
    "ci",
    "continuous integration"
  ],
  "url": "https://exe.dev/deploy/github-actions-runner",
  "markdown_url": "https://exe.dev/deploy/github-actions-runner.md",
  "upstream": {
    "website": "https://docs.github.com/en/actions/hosting-your-own-runners",
    "source": "https://github.com/actions/runner",
    "license": "MIT"
  },
  "example_vm_name": "my-github-actions-runner",
  "stack": [
    {
      "name": "GitHub Actions runner",
      "detail": "`gh-actions-runner.service`, managed by systemd",
      "data": "~/actions-runner"
    }
  ],
  "inputs": [
    {
      "name": "GITHUB_RUNNER_URL",
      "description": "the `--url` in the `./config.sh` command on GitHub's new self-hosted runner page, for your organization or repository"
    },
    {
      "name": "GITHUB_RUNNER_TOKEN",
      "description": "the `--token` in the same command; it expires after an hour"
    }
  ],
  "delivers": [
    "A self-hosted runner, registered with your GitHub organization or repository",
    "The `gh-actions-runner` systemd service, which restarts the runner after a reboot",
    "`Listening for Jobs` in `sudo systemctl status gh-actions-runner.service`"
  ],
  "deployable": true,
  "deploy": {
    "new_vm_url": "https://exe.dev/new?prompt=GITHUB_RUNNER_URL%3D%3Cfill-this-in%3E%0AGITHUB_RUNNER_TOKEN%3D%3Cfill-this-in%3E%0A%0ASet%20up%20GitHub%20Actions%20runner%20on%20this%20VM%20by%20following%20the%20exe.dev%20recipe%20at%20https%3A%2F%2Fexe.dev%2Fdeploy%2Fgithub-actions-runner.md%20from%20top%20to%20bottom.%0A%0AThe%20values%20at%20the%20top%20of%20this%20message%20are%20mine%3A%0A-%20GITHUB_RUNNER_URL%3A%20the%20%60--url%60%20in%20the%20%60.%2Fconfig.sh%60%20command%20on%20GitHub%27s%20new%20self-hosted%20runner%20page%2C%20for%20your%20organization%20or%20repository%0A-%20GITHUB_RUNNER_TOKEN%3A%20the%20%60--token%60%20in%20the%20same%20command%3B%20it%20expires%20after%20an%20hour%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-github-actions-runner.%20This%20VM%27s%20name%20is%20the%20output%20of%20%60hostname%60%3A%20use%20it%20wherever%20the%20recipe%20writes%20my-github-actions-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-%20A%20self-hosted%20runner%2C%20registered%20with%20your%20GitHub%20organization%20or%20repository%0A-%20The%20%60gh-actions-runner%60%20systemd%20service%2C%20which%20restarts%20the%20runner%20after%20a%20reboot%0A-%20%60Listening%20for%20Jobs%60%20in%20%60sudo%20systemctl%20status%20gh-actions-runner.service%60%0A%0AGitHub%27s%20instructions%20are%20on%20the%20page%20I%20open%20in%20my%20browser%3B%20reproduce%0Athem%20here.%20Download%20the%20latest%20Linux%20x64%20runner%20from%0Ahttps%3A%2F%2Fgithub.com%2Factions%2Frunner%2Freleases%20into%20~%2Factions-runner%2C%20check%0Aits%20SHA-256%2C%20and%20run%20.%2Fconfig.sh%20--unattended%20with%20my%20URL%20and%20token%20and%0A--name%20%22%24%28hostname%29%22.%20Don%27t%20start%20run.sh%20by%20hand%3A%20the%20systemd%20service%0Aruns%20it.%20Never%20echo%20the%20token%20back.%0A%0AFinish%20with%20the%20URLs%20I%20should%20open.%0A",
    "terminal": "curl -fsSL https://exe.dev/deploy/github-actions-runner/prompt.txt | ssh exe.dev new --prompt=/dev/stdin",
    "prompt": "GITHUB_RUNNER_URL=\u003cfill-this-in\u003e\nGITHUB_RUNNER_TOKEN=\u003cfill-this-in\u003e\n\nSet up GitHub Actions runner on this VM by following the exe.dev recipe at https://exe.dev/deploy/github-actions-runner.md from top to bottom.\n\nThe values at the top of this message are mine:\n- GITHUB_RUNNER_URL: the `--url` in the `./config.sh` command on GitHub's new self-hosted runner page, for your organization or repository\n- GITHUB_RUNNER_TOKEN: the `--token` in the same command; it expires after an hour\nAsk me for any that still say \u003cfill-this-in\u003e before you need them.\n\nThe recipe's example VM name is my-github-actions-runner. This VM's name is the output of `hostname`: use it wherever the recipe writes my-github-actions-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- A self-hosted runner, registered with your GitHub organization or repository\n- The `gh-actions-runner` systemd service, which restarts the runner after a reboot\n- `Listening for Jobs` in `sudo systemctl status gh-actions-runner.service`\n\nGitHub's instructions are on the page I open in my browser; reproduce\nthem here. Download the latest Linux x64 runner from\nhttps://github.com/actions/runner/releases into ~/actions-runner, check\nits SHA-256, and run ./config.sh --unattended with my URL and token and\n--name \"$(hostname)\". Don't start run.sh by hand: the systemd service\nruns it. Never echo the token back.\n\nFinish with the URLs I should open.\n",
    "prompt_url": "https://exe.dev/deploy/github-actions-runner/prompt.txt"
  },
  "updated": "2026-10-17",
  "related": [
    "forgejo",
    "gitea",
    "code-server",
    "dev-test-prod",
    "marimo",
    "n8n"
  ]
}
