
Self-Hosted CI Controller
Open-source GitHub Actions controller that starts a Scaleway worker for queued jobs and stops it when idle.
Project Overview
Self-Hosted CI is an open-source controller for GitHub Actions jobs that need a self-hosted runner. Repository maintainers label a job for the worker; the controller starts a Scaleway instance when there is work and powers it off after an idle period.
The problem
A runner that remains powered on between jobs consumes compute even when a repository is quiet. A runner rebuilt from nothing for every job also loses the benefit of a retained machine and disk. This controller keeps the worker instance available as a stopped resource, starts it for queued jobs, and uses an ephemeral GitHub runner registration for each job.
Key features
- Receives GitHub
workflow_jobwebhooks and selects jobs with the required runner labels. - Verifies webhook signatures before changing queue state.
- Suppresses duplicate queued jobs and stores controller state in a local JSON file.
- Creates a Scaleway worker with cloud-init, or finds and reconciles an existing worker.
- Starts the instance when the queue needs capacity and stops it after an idle interval.
- Gives the worker a job through an authenticated internal endpoint and records completion through another.
- Requests GitHub App installation and runner registration tokens for the target repository.
- Exposes a health response with queue length, worker power state, and current work.
Technical architecture
A Node.js and Express controller listens on localhost. GitHub sends webhook events to its public route through the intended reverse proxy. The controller records jobs and worker metadata in data/state.json, written with restricted file permissions. It calls the Scaleway CLI to create, start, inspect, and stop the instance. Cloud-init installs the worker toolchain and a systemd polling loop. The worker requests its next job, registers an ephemeral GitHub Actions runner, runs the job, and reports completion. GitHub App authentication supplies short-lived tokens rather than a stored personal access token.
Engineering highlights
- Both webhook HMACs and internal Bearer secrets use timing-safe comparisons.
- The controller checks required configuration at startup and limits its listener to loopback.
- Queue state survives controller restarts through the restricted local state file.
- Worker reconciliation can adopt an already-created instance after a controller restart.
- Idle shutdown is a power-state transition; the retained instance can be started for later jobs.
Image Gallery
No media available for this project.
Client Reviews
Feedback from people who used or collaborated on this project.
Share your experience with this project
Related projects

WhatsApp Channel and Chat API
Self-hosted API for WhatsApp channels and private chats, with media checks, send queues, and SQLite deduplication.

Vertex AI Proxy
Open-source Gemini-compatible API gateway for Vertex AI with prepaid keys, usage tracking, limits, and an admin console.

Levix Self-Hosted WhatsApp Bot
MIT-licensed WhatsApp bot with AI tools, moderation, scheduling, Android hosting, and a companion website.