A lightweight webhook server that starts self-hosted GitHub Actions runners on demand and stops them when idle — saving RAM and CPU on your server.
- Zero dependencies — pure Python 3 stdlib, no pip installs
- Config-driven — one JSON file maps repos to runner services
- Flexible backends — systemd services, Docker containers, or any custom script
- Multi-repo — one instance handles unlimited repos and runners
- Safe — HMAC-SHA256 webhook signature verification, graceful shutdown
Push to repo
└─▶ GitHub sends workflow_job.queued webhook
└─▶ Autoscaler starts the runner service
└─▶ Runner picks up the job
└─▶ Job completes
└─▶ GitHub sends workflow_job.completed
└─▶ Autoscaler waits grace period
└─▶ Runner stops (if idle)
- Linux server with Python 3.8+
- Self-hosted GitHub Actions runner(s) already registered
- Server reachable from the internet on a port (default: 9000)
sudo bash -c "$(curl -fsSL https://raw.githubusercontent.com/gokturksigirtmac/Auto-Runner/main/install.sh)"Then edit the config and start the service:
nano /etc/Auto-Runner/config.json
systemctl start runner-autoscaler# 1. Copy files
sudo mkdir -p /opt/Auto-Runner /etc/Auto-Runner
sudo cp autoscaler.py /opt/Auto-Runner/
sudo cp config.example.json /etc/Auto-Runner/config.json
sudo cp runner-autoscaler.service /etc/systemd/system/
# 2. Edit config
sudo nano /etc/Auto-Runner/config.json
# 3. Enable and start
sudo systemctl daemon-reload
sudo systemctl enable --now runner-autoscaler
# 4. Check logs
journalctl -fu runner-autoscaler/etc/Auto-Runner/config.json:
{
"port": 9000,
"webhook_secret": "your-secret",
"grace_seconds": 30,
"runners": [
{
"repo": "your-org/your-repo",
"service": "actions.runner.your-org-your-repo.runner-name"
}
]
}| Field | Type | Default | Description |
|---|---|---|---|
port |
int | 9000 |
HTTP port to listen on |
webhook_secret |
string | "" |
GitHub webhook secret (leave empty to skip verification) |
grace_seconds |
int | 30 |
Seconds to wait after last job before stopping a runner |
runners |
array | [] |
List of runner definitions (see below) |
| Field | Type | Description |
|---|---|---|
repo |
string | Single repo (owner/name) |
repos |
string[] | Multiple repos sharing one runner |
service |
string | systemd service name to start/stop |
start_command |
string | Custom start command (overrides service) |
stop_command |
string | Custom stop command (overrides service) |
grace_seconds |
int | Per-runner override of the global grace period |
systemctl list-units 'actions.runner.*' --no-pagerUseful in Docker or CI:
| Variable | Overrides |
|---|---|
CONFIG_FILE |
Path to config file |
WEBHOOK_SECRET |
webhook_secret |
PORT |
port |
GRACE_SECONDS |
grace_seconds |
{
"runners": [
{
"repos": ["org/frontend", "org/backend"],
"service": "actions.runner.org-frontend.my-runner",
"grace_seconds": 60
}
]
}{
"runners": [
{
"repo": "org/repo",
"start_command": "docker start github-runner",
"stop_command": "docker stop github-runner"
}
]
}{
"runners": [
{
"repo": "org/frontend",
"service": "actions.runner.org-frontend.fe-runner"
},
{
"repo": "org/backend",
"service": "actions.runner.org-backend.be-runner",
"grace_seconds": 60
}
]
}For each repository:
- Go to Settings → Webhooks → Add webhook
- Payload URL:
http://YOUR_SERVER_IP:9000 - Content type:
application/json - Secret: same value as
webhook_secretin config - Events: select Workflow jobs only
- Click Add webhook — GitHub sends a ping that the autoscaler responds to
Generate a secure secret:
openssl rand -hex 32# UFW
sudo ufw allow 9000/tcp
# iptables
sudo iptables -A INPUT -p tcp --dport 9000 -j ACCEPTjournalctl -fu runner-autoscalerExample output:
2026-01-01 12:00:00 INFO Listening on port 9000 | 2 repo(s): ['org/frontend', 'org/backend']
2026-01-01 12:05:12 INFO [org/frontend] queued #987654321
2026-01-01 12:05:12 INFO systemctl start actions.runner.org-frontend.fe-runner
2026-01-01 12:08:44 INFO [org/frontend] completed #987654321
2026-01-01 12:08:44 INFO Job 987654321 completed. Active jobs for …fe-runner: 0
2026-01-01 12:09:14 INFO Stopping runner: …fe-runner
curl http://localhost:9000/health
# → 200 OKAuto-Runner includes an AGENTS.md file — a structured guide that lets any AI assistant install and configure it on your server with minimal input from you.
Share the AGENTS.md file (or just the repo link) with your AI tool and say something like:
"Install this on my server" "Set up Auto-Runner for me"
The AI will:
- Ask you for your server's IP, SSH credentials, and which repos to watch
- SSH into your server and discover existing runner services automatically
- Generate a secure webhook secret (or use one you provide)
- Write the config file, install the service, and open the firewall port
- Register GitHub webhooks for each repo
- Verify everything works with a health check
No copy-pasting commands, no reading docs — the AI handles the entire setup end to end.
"Install Auto-Runner on my server. AGENTS.md: https://github.com/gokturksigirtmac/Auto-Runner/blob/main/AGENTS.md"
"Bunu sunucuma kur: https://github.com/gokturksigirtmac/Auto-Runner"
The AGENTS.md file works with Claude, ChatGPT, Copilot, Gemini, and any other AI assistant that can read a URL or a pasted document.
MIT
Issues and PRs welcome. Keep it dependency-free.