JIT Runner Kit: one GitHub Actions job, one ephemeral Hetzner VM

I have published JIT Runner Kit v0.1.0, an MIT-licensed toolkit for ephemeral GitHub Actions runners.

The goal is simple: keep expensive build, test, and deploy work off GitHub-hosted runners without paying for an idle self-hosted server. A short GitHub-hosted control job creates a fresh Hetzner VM with a unique just-in-time label. One trusted workload job runs there; an always() cleanup job then removes the runner record, VM, Primary IPv4, firewall, and temporary SSH key. An independent TTL sweep is the backstop.

The repository also retains an external polling-controller mode for teams that need zero GitHub-hosted control minutes. No Mac or permanent controller VPS is required for the recommended Actions-driven mode.

What has been validated on a real Hetzner CX33 pilot:

  • successful provisioning, registration, workload execution, deregistration, and full cleanup;
  • cleanup after successful, failed, and cancelled workloads;
  • Docker/Compose, GitHub CLI, ShellCheck, PHP CLI, Python 3, and native build tooling on Ubuntu 24.04;
  • custom package and Bash/Zsh/Fish guidance;
  • immutable action pinning and an unprivileged runner user without sudo.

The project is still pre-1.0. It is intended for trusted repositories and controlled pilots. Public fork PRs should use a separate, secret-free execution path; a workflow job fully controls its runner VM. Parallelism is isolated: use one VM per job, with a small global cap, rather than multiple concurrent jobs sharing one machine.

The next controller design is provider-agnostic. The first serverless adapter is planned for Cloudflare Workers with a GitHub App, Queues, Durable Objects, and Cron; Hetzner remains the first compute adapter. The core lifecycle, lease state machine, idempotency, retry policy, concurrency limits, and cleanup contracts will not depend on either provider.

I would especially value feedback on:

  1. Which provider driver should come next?
  2. Which serverless controller adapter should follow Cloudflare?
  3. Which observability signals are essential before calling this production-ready?
  4. What image customization should be declarative rather than handled by cloud-init or a prebuilt snapshot?

Repository: GitHub - Arnon-hs/jit-runner-kit: Provider-agnostic toolkit for one-job GitHub Actions JIT runners, with Hetzner/OpenTofu and pluggable controllers · GitHub

Update: JIT Runner Kit v0.2.0

JIT Runner Kit v0.2.0 is now available.

This release turns the serverless-controller roadmap from a design into a controlled-canary implementation:

  • provider-agnostic job, lease, compute, runner, bootstrap, clock, and telemetry core;
  • Cloudflare Workers adapter with signed GitHub App workflow_job webhooks, Queues/DLQ, a SQLite Durable Object, alarms, and Cron;
  • direct Hetzner Cloud adapter with deny-inbound, SSH-free VMs and a one-time bootstrap exchange;
  • private organization runner-group policy restricted to exact trusted workflow files;
  • race-safe provisioning and cleanup, capacity leases, attempt fencing, ambiguous-create recovery, and independent TTL cleanup;
  • inert Wrangler and GitHub App templates plus an offline fail-closed deployment preflight.

The release candidate passed 37 mocked conformance tests, CLI tests, TypeScript checks, OpenTofu validation, dependency audit, Wrangler dry-run, and the public GitHub quality gate. The final v0.2.0 tag and main both point to commit 0c605fb873d06bcabc51eaa5725f294ac569b4a6.

Important boundary: Cloudflare mode is ready for a dedicated non-production canary, not production use. No Cloudflare account was accessed and no Cloudflare resource was created for this release. The existing GitHub control-job mode remains the recommended production path until real webhook, retry/DLQ, Durable Object recovery, success, failure, cancellation, and TTL cleanup gates all pass with an empty provider inventory.

The AtlasRepo guide has been updated with the v0.2.0 architecture, trust boundary, shell/package customization, offline setup, and canary checklist:

Ephemeral GitHub Actions runners on Hetzner with JIT Runner Kit

Feedback on controller adapters, compute providers, observability, and canary operations is welcome.

JIT Runner Kit v0.3.0 is now the verified AtlasRepo production path for trusted private-repository CI/CD.

The original one-VM-per-job control-job flow has been replaced by a repository-scoped GitHub App and Cloudflare Worker using Queues + Durable Objects. The Hetzner adapter scales from zero to one temporary CX33, supports at most two disposable runner/DinD pairs, reuses the host for adjacent jobs, and deletes all provider resources after the idle window.

Real Platform, Scout, Admin, and Web release workloads completed on the new flow. The Admin release included exact-image, Zeabur, private-edge, authenticated deep-link, and hard-refresh checks. Final independent inventory returned to servers=0, primary_ips=0, firewalls=0, ssh_keys=0, and repository runners =0.

The final workflow cutover was also a real concurrency test: Platform run 32994477386 and Scout run 32994479984 executed together as two isolated runner/DinD pairs on one CX33. Both full build/deploy/runtime pipelines succeeded; no second server was created.

This is an infrastructure release, not a claim that AtlasRepo’s product launch gates are all green. Scout continues to expose its remaining semantic, quality, catalogue, and alert blockers rather than turning a successful deployment into a false launch-ready signal.

The cutover also uncovered and fixed real operational gaps: asynchronous Primary IP readiness, retry classification, non-root JIT config ownership, DinD network reachability, PostgreSQL hostname semantics, and a missing GitHub CLI in the immutable runner image.

One important lesson: an exact-SHA GitHub PushEvent can exist before the Actions run becomes visible. Do not use a timed manual fallback for that SHA; the automatic run may arrive later and duplicate the deployment. v0.3.0 documents this explicitly.

Release: https://github.com/Arnon-hs/jit-runner-kit/releases/tag/v0.3.0

Production cutover runbook: https://github.com/Arnon-hs/jit-runner-kit/blob/v0.3.0/docs/production-cutover.md

Custom shells, packages, and runner images: https://github.com/Arnon-hs/jit-runner-kit/blob/v0.3.0/docs/custom-runner-image.md

Updated AtlasRepo article: Ephemeral GitHub Actions runners on Hetzner with JIT Runner Kit — AtlasRepo Blog