Skip to content

Running a self-hosted GitHub Actions Runner

Log in easily into your CI environment

Deploy on exe.dev

Shelley handles the setup.

Prefer a terminal?
curl -fsSL https://exe-staging.dev/deploy/github-actions-runner/prompt.txt | ssh exe.dev new --prompt=/dev/stdin

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.

First, create a new VM with ssh exe.dev new. This will create a new VM. SSH into it with ssh vmname.exe.xyz. The trickiest bit is to find the GitHub URLs. Replace the placeholders in the following:

Copy and paste the instructions from GitHub's instructions into your shell session. It's pretty quick and easy until "run.sh".

To make sure the runner restarts after a reboot, we can create a systemd service:

Create the service file at /home/exedev/actions-runner/gh-actions-runner.service:

cat > /home/exedev/actions-runner/gh-actions-runner.service << 'EOF'
[Unit]
Description=GitHub Actions Runner
After=network.target

[Service]
Type=simple
User=exedev
WorkingDirectory=/home/exedev/actions-runner
ExecStart=/home/exedev/actions-runner/run.sh
Restart=always
RestartSec=10

[Install]
WantedBy=multi-user.target
EOF

Then copy the service file to systemd directory

sudo cp /home/exedev/actions-runner/gh-actions-runner.service /etc/systemd/system/

And start the service:

sudo systemctl daemon-reload
sudo systemctl enable --now gh-actions-runner.service

Verify the service is running

sudo systemctl status gh-actions-runner.service

gh-actions-runner.service - GitHub Actions Runner
     Loaded: loaded (/etc/systemd/system/gh-actions-runner.service; enabled; preset: enabled)
     Active: active (running) since Sun 2025-11-09 04:33:28 UTC; 41s ago
   Main PID: 1151 (run.sh)
      Tasks: 15 (limit: 2384)
     Memory: 93.2M (peak: 101.6M)
        CPU: 1.447s
     CGroup: /system.slice/gh-actions-runner.service
             /bin/bash /home/exedev/actions-runner/run.sh
             /bin/bash /home/exedev/actions-runner/run-helper.sh
             /home/exedev/actions-runner/bin/Runner.Listener run

Nov 09 04:33:30 ...exe.dev run.sh[1159]: Connected to GitHub
Nov 09 04:33:31 ...exe.dev run.sh[1159]: Current runner version: '2.329.0'
Nov 09 04:33:31 ...exe.dev run.sh[1159]: 2025-11-09 04:33:31Z: Listening for Jobs

You're all set!

What you get#

  • 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
What's running, and where
my-github-actions-runner.exe.xyz exe.dev VM
GitHub Actions runner gh-actions-runner.service, managed by systemd ~/actions-runner

Deploy with Shelley#

Click Deploy and Shelley will set up GitHub Actions runner on a new VM, then check that it works. No config-file wrangling required.

Have these ready; Shelley asks for them:

  • GITHUB_RUNNER_URL: the --url in the ./config.sh command on GitHub's new self-hosted runner page, for your organization or repository
  • GITHUB_RUNNER_TOKEN: the --token in the same command; it expires after an hour
Prefer a terminal?
curl -fsSL \
  https://exe-staging.dev/deploy/github-actions-runner/prompt.txt \
  | ssh exe.dev new --prompt=/dev/stdin
What Shelley is told
GITHUB_RUNNER_URL=<fill-this-in>
GITHUB_RUNNER_TOKEN=<fill-this-in>

Set up GitHub Actions runner on this VM by following the exe.dev recipe at https://exe-staging.dev/deploy/github-actions-runner.md from top to bottom.

The values at the top of this message are mine:
- GITHUB_RUNNER_URL: the `--url` in the `./config.sh` command on GitHub's new self-hosted runner page, for your organization or repository
- GITHUB_RUNNER_TOKEN: the `--token` in the same command; it expires after an hour
Ask me for any that still say <fill-this-in> before you need them.

The 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.

You are done when all of these are true. Check each one yourself:
- 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`

GitHub's instructions are on the page I open in my browser; reproduce
them here. Download the latest Linux x64 runner from
https://github.com/actions/runner/releases into ~/actions-runner, check
its SHA-256, and run ./config.sh --unattended with my URL and token and
--name "$(hostname)". Don't start run.sh by hand: the systemd service
runs it. Never echo the token back.

Finish with the URLs I should open.

Also at /deploy/github-actions-runner/prompt.txt. The recipe it follows is /deploy/github-actions-runner.md.

Updated Oct 17, 2026 · Markdown · manifest.json · Something wrong? Tell us