The Setup
I run GitLab Runner on a ChromeOS thin client (Debian container, 2.7GB RAM, no swap). It builds my blog (Babashka static site generator) and deploys to GitLab Pages.
No GitHub. No shared runners. No 400-minute compute quotas. No noise.
Why Not GitHub?
GitHub Actions is fine, but: - Shared runners have quotas (400 minutes/month) - GitHub is social (recruiters, noise, distractions) - I wanted complete control
GitLab self-hosted runner = unlimited CI/CD. The runner is exempt from GitLab.com’s quotas because it’s my hardware.
The Hardware
ChromeOS thin client specs: - 2.7GB RAM (584MB available after OS) - 41GB storage - ARM64 (aarch64) - No swap
GitLab Runner requirements: - 512MB RAM minimum → I have 2.7GB ✅ - ARM64 support → Yes ✅ - 100MB disk → Yes ✅
Installation
# Download GitLab Runner for ARM64
curl -L --output /tmp/gitlab-runner \
"https://gitlab-runner-downloads.s3.amazonaws.com/latest/binaries/gitlab-runner-linux-arm64"
sudo mv /tmp/gitlab-runner /usr/local/bin/gitlab-runner
sudo chmod +x /usr/local/bin/gitlab-runner
gitlab-runner --version
# Version: 19.2.0 (ARM64)
Binary size: 110MB (single file, no dependencies)
Configuration
Register the runner (needs token from GitLab):
gitlab-runner register \
--url "https://gitlab.com" \
--token "<YOUR_TOKEN>" \
--executor "shell" \
--description "homepage-self-hosted" \
--tag-list "homepage-self-hosted"
Key settings: - executor: shell - runs
directly on the host (no Docker overhead) -
tag-list: homepage-self-hosted - GitLab routes jobs to this
runner only
The Blog Build Pipeline
My blog uses Babashka (Clojure scripting) to: 1.
Convert Markdown → HTML (pandoc) 2. Render D2 diagrams → SVG 3. Generate
static site → public/
.gitlab-ci.yml:
pages:
stage: deploy
tags:
- homepage-self-hosted
script:
- bb build
- bb validate-links
artifacts:
paths:
- public/
timeout: 15m
Build time: ~30 seconds (Babashka is fast)
Keeping the Runner Alive
The runner dies when the shell session ends. Keep it alive with
setsid:
setsid ~/.local/bin/gitlab-runner run \
--config ~/.config/gitlab-runner/config.toml \
> ~/.local/var/gitlab-runner.log 2>&1 < /dev/null &
Check if running:
ps aux | grep gitlab-runner
Why This Matters
Sovereignty: - My CI/CD runs on my hardware - No dependency on GitHub’s infrastructure - No shared runner queues - No compute quotas
Minimal: - Single binary (110MB) - No Docker (shell executor) - No JVM (Babashka is native) - No bloat
Collapse Entropy: - GitLab < GitHub (less noise, more focus) - Self-hosted < Cloud (control) - Thin client < Workstation (constraints force simplicity)
The Result
Every git push to main: 1. Triggers the
runner (on this thin client) 2. Builds the blog (Babashka + pandoc + d2)
3. Deploys to GitLab Pages 4. Live at nurazhar.com in ~45
seconds
Zero maintenance. The runner just works.
Lessons
- Thin clients are enough - 2.7GB RAM runs CI/CD fine
- Self-hosted = freedom - No quotas, no queues, no noise
- Babashka is perfect - Native Clojure, fast builds, minimal deps
- GitLab > GitHub for solo devs who want control
Try It
Want your own self-hosted runner?
- Install GitLab Runner (single binary)
- Register with your GitLab token
- Tag your jobs
- Push code
Hardware needed: Any Linux box with 512MB RAM.
Time to set up: 15 minutes.
ROI: Unlimited CI/CD forever.
Written on a ChromeOS thin client with 584MB RAM available. If it can run GitLab Runner, so can you.