Back to blog
TutorialDecember 29, 2024• 8 min read

Deploy Self-Hosted GitHub Actions Runners on Vectorlay

Tired of waiting in GitHub's runner queue? Want GPU access in your CI/CD pipeline? This guide shows you how to run self-hosted GitHub Actions runners on Vectorlay with automatic failover and scaling.

Faster Builds
Dedicated resources
No Queue
Zero wait times
GPU Access
ML in your CI
Auto Failover
Node goes down? No problem
No Limits
Your infra, your rules
$
Cost Control
Predictable pricing

Why Self-Hosted Runners?

GitHub's hosted runners are convenient, but they come with limitations:

  • Queue times: Popular repos can wait minutes for a runner
  • Resource limits: 2 vCPUs, 7GB RAM on free tier
  • No GPUs: Can't run ML training or inference in CI
  • Minutes caps: Free tier limits, expensive overages
  • No customization: Can't pre-install your toolchain

Self-hosted runners on Vectorlay solve all of these. You get dedicated resources, instant job pickup, GPU access, and Vectorlay's automatic failover if a node goes offline.

Prerequisites

What You'll Need

  • A Vectorlay account (we handle the infrastructure)
  • A GitHub Classic PAT (Personal Access Token)
    • • For single repo: repo scope
    • • For org-wide: admin:org + repo scopes

Important: Fine-grained PATs don't work for organization runners. You must use a Classic PAT. Go to GitHub → Settings → Developer settings → Personal access tokens → Tokens (classic) → Generate new token.

Step 1: Create the Cluster

We'll use the popular myoung34/github-runner Docker image, which handles runner registration, deregistration, and auto-updates.

Option A: Single Repository Runner

For runners that only serve one repository:

Cluster Configuration
Name:github-runner-myrepo
Image:myoung34/github-runner:latest
Port:80(unused, but required)
Replicas:2(for parallel jobs)
Environment Variables
REPO_URL=https://github.com/your-org/your-repo
ACCESS_TOKEN=ghp_xxxxxxxxxxxxxxxxxxxx

Option B: Organization-Wide Runner

For runners that can serve any repository in your organization:

Cluster Configuration
Name:github-runner-org
Image:myoung34/github-runner:latest
Port:80
Replicas:4(scale based on job volume)
Environment Variables
ORG_NAME=your-org
ACCESS_TOKEN=ghp_xxxxxxxxxxxxxxxxxxxx

Pro tip: Don't set RUNNER_NAME. The image auto-generates unique names for each replica, which is required when running multiple instances.

Step 2: Deploy and Verify

Click Deploy and wait for your cluster to reach the "Running" state. This typically takes 1-2 minutes as the runner image is pulled and containers start.

Once running, verify your runners are registered:

  1. Go to your repository (or organization) on GitHub
  2. Navigate to Settings → Actions → Runners
  3. You should see your runners listed as "Idle"
Example: Runners in GitHub Settings
vectorlay-runner-a1b2c3Idle
vectorlay-runner-d4e5f6Idle

Step 3: Use the Runners in Your Workflows

Update your workflow files to use runs-on: self-hosted:

# .github/workflows/build.yml
name: Build and Test

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

jobs:
  build:
    runs-on: self-hosted
    
    steps:
      - uses: actions/checkout@v4
      
      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '20'
          
      - name: Install dependencies
        run: npm ci
        
      - name: Run tests
        run: npm test
        
      - name: Build
        run: npm run build

Using Labels for GPU Runners

If you have GPU nodes and want to route ML jobs specifically to them, add labels:

Add to Environment Variables
LABELS=self-hosted,linux,gpu,cuda
jobs:
  train-model:
    runs-on: [self-hosted, gpu]
    
    steps:
      - uses: actions/checkout@v4
      
      - name: Train model
        run: python train.py --epochs 100

How It Works

  • Outbound connection: Runners connect outbound to GitHub over HTTPS. No public IP or inbound ports needed.
  • Auto-registration: On container start, the runner registers itself with GitHub using your PAT.
  • Job pickup: Runners long-poll GitHub for jobs. When a workflow triggers, an idle runner picks it up instantly.
  • Clean environment: Each job runs in a fresh environment. The container persists but the workspace is cleaned.
  • Graceful shutdown: On container stop, the runner deregisters from GitHub automatically.

Scaling and Failover

Each replica is an independent runner. With 4 replicas, you can run 4 jobs in parallel.

Scaling Up

Need more parallelism? Just increase the replica count in your cluster settings. New runners spin up and register within seconds.

Automatic Failover

If a Vectorlay node goes offline:

  1. Vectorlay detects the failure via heartbeat
  2. The runner on that node becomes unresponsive to GitHub
  3. GitHub marks the runner as offline after 30 seconds
  4. Vectorlay schedules a replacement replica on a healthy node
  5. New runner registers and becomes available

Any in-progress job on the failed node will be marked as failed by GitHub and can be re-run. Queued jobs will be picked up by remaining healthy runners.

Advanced Configuration

Runner Groups (Enterprise)

If you're on GitHub Enterprise, you can assign runners to groups:

RUNNER_GROUP=production

Ephemeral Runners

For maximum isolation, use ephemeral mode. The runner handles one job then exits (Vectorlay will automatically restart it):

EPHEMERAL=true

Docker-in-Docker

Need to build Docker images in your CI? The runner image supports Docker-in-Docker. Make sure to use a Vectorlay node with Docker installed and add:

DOCKER_ENABLED=true

Troubleshooting

Runner not appearing in GitHub

  • Check the container logs in Vectorlay dashboard for registration errors
  • Verify your PAT has the correct scopes
  • For org runners, confirm you used a Classic PAT (not fine-grained)
  • Check that REPO_URL or ORG_NAME is correct

Jobs stuck in "Queued"

  • Verify at least one runner shows "Idle" in GitHub Settings
  • Check that your workflow's runs-on labels match the runner's labels
  • For org runners, ensure the repository has access to the runner group

"PAT does not have admin:org scope"

  • You're trying to register an org-wide runner
  • Regenerate your PAT with admin:org scope
  • Or switch to REPO_URL for a single-repo runner

Cost Comparison

Running 4 concurrent runners on Vectorlay vs GitHub's hosted runners:

ProviderSpecsMonthly Cost
GitHub Free2 vCPU, 7GB RAM2,000 mins then $0.008/min
GitHub Team2 vCPU, 7GB RAM3,000 mins then $0.008/min
Larger Runners8 vCPU, 32GB RAM$0.032/min (~$46/1000 mins)
Vectorlay8+ vCPU, 32GB+ RAM, GPU optional~$0.20-0.40/hr per runner

If you run 10,000+ minutes of CI per month, self-hosted runners on Vectorlay can save 50-70% compared to GitHub's hosted runners—with better specs and GPU access.

Wrapping Up

Self-hosted GitHub Actions runners on Vectorlay give you the best of both worlds: the convenience of GitHub Actions with the power and control of your own infrastructure.

  • No more queue wait times
  • Run GPU workloads in CI
  • Automatic failover and scaling
  • Predictable costs

Questions? Join our Discord or check out the docs.

Ready to speed up your CI/CD?

Deploy self-hosted runners in minutes. Get $10 in free credits.

Get Started