3 minutes
Cloudflare Runners
Commercial CI/CD runners are expensive, and hosting your own GitLab runner fleet is a chore. You have to patch host OS kernels, manage Docker daemon storage, prune orphaned volumes, and keep an eye on monthly VM bills.
What if you could run your entire CI/CD workload on enterprise-grade infrastructure completely for free, with zero servers to manage?
Cloudflare Pages lets developers build and host Jamstack sites for free. On every git commit, Cloudflare spins up a fully functional, high-performance Linux build container to compile your frontend assets. I figured, since they are giving us a free Linux container with root privileges and outbound internet access, we might as well put it to work.
To make this happen, I built Cloudflare Runners — a delightfully hacky script that turns Cloudflare Pages build environments into on-demand, serverless GitLab CI/CD runners.
graph TD
Commit[Commit to GitLab Codebase] -->|Trigger Pipeline| GL[GitLab Instance]
GL -->|Web Hook / Trigger| CF[Cloudflare Pages Deploy]
CF -->|Start Build Container| Run[run.sh Executed]
Run -->|Download & Register| Runner[gitlab-runner Daemon]
Runner -->|Fetch & Execute Jobs| GL
Runner -->|Timeout 15 mins| Clean[Clean Unregister]
Clean -->|Publish logs to HTML| Pages[Cloudflare Pages Live Site]
Hijacking the build runtime
When a Cloudflare Pages build is triggered, the build agent pulls down your project from GitHub or GitLab and executes your specified build command. Instead of compiling a React app, we point Cloudflare at a shell script (run.sh).
This script downloads the official gitlab-runner Linux binary, validates its SHA256 checksum, and registers a fresh GitLab runner instance using a registration token.
#!/bin/bash
set -ex
# Grab the latest runner binary and verification hash
wget -c https://gitlab-runner-downloads.s3.amazonaws.com/latest/binaries/gitlab-runner-linux-amd64 -O gitlab-runner-linux-amd64
wget -c https://gitlab-runner-downloads.s3.amazonaws.com/latest/release.sha256 -O release.sha256
sed -i 's;binaries/;./;g' release.sha256
sha256sum --ignore-missing -c release.sha256
chmod +x ./gitlab-runner-linux-amd64
Auto-concurrency and timeout control
Cloudflare’s build environments are surprisingly beefy. To ensure we leverage the full power of the underlying virtualization nodes, the script adjusts the runner’s concurrency dynamically to match the container’s available CPU cores.
# Register the runner as a shell executor
RUNNER_NAME="$(hostname)" RUNNER_EXECUTOR=shell ./gitlab-runner-linux-amd64 register -n
# Crank up concurrency to use every single vCPU core
sed -i "s/concurrent = 1/concurrent = $(nproc)/" $HOME/.gitlab-runner/config.toml
But we have to be careful. Cloudflare enforces a twenty-minute build timeout. If a build runs too long, Cloudflare forcefully kills the container, which would leave our runner marked as “active but dead” inside GitLab’s controller.
To ensure a graceful exit, we run the runner inside a strict fifteen-minute timeout boundary. Once fifteen minutes have elapsed, the daemon stops accepting new jobs and gracefully shuts down. The script then executes a clean unregistration to prune itself from GitLab.
# Run the agent for up to 15 minutes
timeout 900 ./gitlab-runner-linux-amd64 run || true
# Clean up registration before the container vanishes
./gitlab-runner-linux-amd64 unregister --all-runners || true
Storing logs as static assets
Because Cloudflare Pages expects a build to generate static website files, we capture all stdout and stderr streams from our runner session and save them into null/index.html.
echo "<html><head><title>Cloudflare Pages - Free CI/CD</title></head><body><pre>Start: $(date)</pre><pre>" > null/index.html
{
# Runner logs go here
} &>> null/index.html
echo "</pre><pre>End: $(date)</pre></body>" >> null/index.html
When the build completes, Cloudflare uploads the generated HTML file and deploys it as a static page. You can then navigate directly to your Cloudflare Pages URL to view the live execution logs, start times, and pipeline statistics of your runner fleet!
It is a beautifully simple, highly scalable, and completely serverless solution that keeps my private pipeline projects running smoothly for absolutely zero dollars.
- Codebase & Contributing: f0o/cloudflare-runners on Github
Related Content: