All articles
TutorialsJan 27, 2026 · 7 min read

How to Set Up a Self-Hosted GitHub Actions Runner on Your VPS

How to Set Up a Self-Hosted GitHub Actions Runner on Your VPS

GitHub's hosted runners are convenient but come with limits. You get 2,000 minutes per month on free plans, and the machines are shared. If you're running lots of builds or need specific software, those limits get annoying fast.

Self-hosted runners solve this. Your builds run on your own server with no minute caps, faster execution (no cold starts), and whatever environment you want.

Why bother?

The main wins: no minute limits, faster builds (your runner stays warm instead of cold-starting a VM each time), and you can install whatever you need once rather than every build.

A $10/month VPS can handle more CI minutes than GitHub's paid tiers. And if you care about keeping code on your own infrastructure, self-hosted is the only option.

The tradeoff is maintenance. You keep the runner updated and secure.

Prerequisites

You'll need:

  • A VPS with at least 2GB RAM (4GB recommended for larger builds)
  • Ubuntu 22.04 or newer (other distros work too)
  • A GitHub repository or organization

Step 1: Create a runner in GitHub

Go to your repository on GitHub. Click Settings → Actions → Runners → New self-hosted runner.

Select Linux and x64 (or ARM64 if you're on an ARM server). GitHub shows you the installation commands. Keep this page open.

Step 2: Set up the server

SSH into your VPS and create a dedicated user for the runner:

sudo useradd -m -s /bin/bash github-runner sudo usermod -aG sudo github-runner sudo su - github-runner

Create the runner directory:

mkdir actions-runner && cd actions-runner

Download the runner package. Copy the exact commands from GitHub since the version changes:

curl -o actions-runner-linux-x64-2.321.0.tar.gz -L https://github.com/actions/runner/releases/download/v2.321.0/actions-runner-linux-x64-2.321.0.tar.gz tar xzf ./actions-runner-linux-x64-2.321.0.tar.gz

Step 3: Configure the runner

Run the configuration script. You'll need the token from GitHub:

./config.sh --url https://github.com/YOUR_USERNAME/YOUR_REPO --token YOUR_TOKEN

The script asks a few questions:

  • Runner group: Press Enter for default
  • Runner name: Give it something memorable like vps-runner-1
  • Labels: Add labels like self-hosted,linux,x64 (or custom ones like docker,nodejs)
  • Work folder: Press Enter for default _work

Step 4: Install as a service

You want the runner to start automatically and survive reboots:

sudo ./svc.sh install sudo ./svc.sh start

Check the status:

sudo ./svc.sh status

You should see the runner listed as "Idle" in GitHub's runner settings.

Step 5: Use the runner in your workflows

Update your workflow file to target your self-hosted runner:

name: Build on: push: branches: [main] jobs: build: runs-on: self-hosted # This targets your runner steps: - uses: actions/checkout@v4 - name: Build run: | npm install npm run build

You can also target specific labels:

runs-on: [self-hosted, linux, docker]

Installing common dependencies

Your runner starts minimal. Install what your builds need:

# Docker curl -fsSL https://get.docker.com | sudo sh sudo usermod -aG docker github-runner # Node.js curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash - sudo apt install -y nodejs # PHP & Composer sudo apt install -y php php-cli php-mbstring php-xml unzip curl -sS https://getcomposer.org/installer | php sudo mv composer.phar /usr/local/bin/composer

Security considerations

Self-hosted runners execute code from your repository. A few things to keep in mind:

Don't use self-hosted runners on public repos. Anyone who opens a PR could run arbitrary code on your server. GitHub warns about this too.

Use a dedicated server. Don't run the runner on a machine with sensitive data or production workloads.

Keep it updated. The runner auto-updates by default, but verify occasionally:

./config.sh --version

Limit network access. Consider firewall rules that restrict what the runner can reach.

Running multiple runners

One server can run multiple runners. Just create separate directories:

mkdir ~/runner-2 && cd ~/runner-2 # Repeat the download and config steps with a different name

For heavy workloads, you might want runners on separate VPS instances.

Troubleshooting

Runner shows as offline: Check if the service is running: sudo ./svc.sh status Check logs: journalctl -u actions.runner.*

Builds fail with permission errors: Make sure the github-runner user has access to Docker, build directories, etc.

Runner is slow: Check if the server has enough RAM. Builds that swap to disk crawl.


Running lots of CI builds? Our VPS plans include unmetered bandwidth and fast NVMe storage. See pricing.