Self Hosted Runner using Docker
Run GitHub Actions on your own computer without cluttering your OS. Learn how to containerize your runner infrastructure using Docker.
Prerequisites
- Docker Desktop Installed
- GitHub Account & Repository
- Basic Terminal Knowledge
Never share your Personal Access Token (PAT). We will use a .env file to keep it safe on your machine.
The 7-Step Setup Guide
Generate PAT (Classic)
Create a Personal Access Token with 'repo' and 'workflow' scopes. This allows your runner to talk to GitHub.
Prepare Workspace
Create a new folder (e.g., 'my-runner') on your machine to hold the configuration files.
The Configuration Trio
Create Dockerfile, start.sh, and docker-compose.yml. These define your runner's environment.
Connect with Secrets
Create a .env file with your GITHUB_TOKEN and REPO_URL. Never commit this to Git!
Spin it up!
Run 'docker compose up --build' in your terminal and watch your runner go online.
Verification
Check 'Settings > Actions > Runners' in your repo. You should see a green 'Idle' dot.
Deploy to Runner
Update your workflow file to use 'runs-on: self-hosted' and trigger a manual run.
Technical Deep Dive
1. The Dockerfile (The Machine)
infrastructure-as-code# FILE: Dockerfile
FROM ubuntu:22.04
# Don't ask for user input during install
ENV DEBIAN_FRONTEND=noninteractive
# Install dependencies (curl, jq, sudo, etc.)
RUN apt-get update && apt-get install -y \
curl git jq sudo unzip ca-certificates \
&& rm -rf /var/lib/apt/lists/*
# Install Node.js 20
RUN curl -fsSL https://deb.nodesource.com/setup_20.x | bash - \
&& apt-get install -y nodejs \
&& rm -rf /var/lib/apt/lists/*
# Create a safe non-root user called "runner"
RUN useradd -m -s /bin/bash runner \
&& echo "runner ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers
# Download and extract the GitHub Actions runner app
ENV RUNNER_VERSION=2.316.1
WORKDIR /home/runner
RUN curl -o runner.tar.gz -L \
"https://github.com/actions/runner/releases/download/v${RUNNER_VERSION}/actions-runner-linux-x64-${RUNNER_VERSION}.tar.gz" \
&& tar xzf runner.tar.gz \
&& rm runner.tar.gz \
&& ./bin/installdependencies.sh
# Copy startup script and set permissions
COPY start.sh /home/runner/start.sh
RUN chmod +x /home/runner/start.sh \
&& chown -R runner:runner /home/runner
USER runner
ENTRYPOINT ["/home/runner/start.sh"]This file defines a lightweight Ubuntu machine, installs Node.js, and downloads the official GitHub Runner binary. We use a non-root runner user for improved security.
2. start.sh (The Brain)
#!/bin/bash
# FILE: start.sh
set -e
# Get registration token from GitHub API
echo "🔑 Getting registration token from GitHub..."
REPO_PATH=$(echo "$REPO_URL" | sed 's|https://github.com/||')
REG_TOKEN=$(curl -s -X POST \
-H "Authorization: Bearer $GITHUB_TOKEN" \
-H "Accept: application/vnd.github+json" \
"https://api.github.com/repos/${REPO_PATH}/actions/runners/registration-token" \
| jq -r '.token')
echo "⚙️ Registering runner..."
./config.sh --url "$REPO_URL" --token "$REG_TOKEN" --unattended --replace
# Cleanup on exit
cleanup() {
echo "🛑 Removing runner..."
./config.sh remove --token "$REG_TOKEN"
}
trap cleanup EXIT SIGTERM SIGINT
echo "🚀 Runner is ONLINE!"
./run.shThis script handles the heavy lifting: it uses your PAT to request a short-lived Registration Token from GitHub, configures the runner, and ensures it removes itself cleanly when you stop the container.
3. docker-compose.yml (The Coordinator)
# FILE: docker-compose.yml
services:
runner:
build: .
env_file: .env
environment:
RUNNER_NAME: my-docker-runner
restart: unless-stoppedInstead of long docker commands, we use Compose to map our .env secrets and ensure the runner automatically restarts if your computer reboots.
Testing Your Runner
Save this workflow to see your Docker container in action.
name: Use My Self-Hosted Runner
on:
workflow_dispatch:
jobs:
run-on-my-machine:
runs-on: self-hosted
steps:
- uses: actions/checkout@v4
- name: System Info
run: |
echo "Hostname : $(hostname)"
echo "OS : $(lsb_release -d | cut -f2)"
node --version