Self Hosted CI Runners: Building Safer Build Systems
Learn how self hosted CI runners improve control and isolation, plus how to use ephemeral environments for safer builds and untrusted code execution.
DM
A CI runner can have access to far more than the code it is compiling. Give a runner credentials, network access, Docker, or a path into internal infrastructure, and an unsafe build can turn a routine pipeline into an infrastructure problem. Self hosted CI runners give you control over the execution environment, but that control also makes isolation and cleanup your responsibility.
For teams running AI-agent sandboxes, untrusted code, Docker workloads, ephemeral development environments, or private deployments, the practical answer is to make each build disposable, restrict what it can reach, and destroy the environment when the job ends.
TL;DR:
- Self hosted CI runners let you control compute, networking, software, and access to private infrastructure.
- Permanent runners are convenient and can execute quickly, but persistent state increases the blast radius of a compromised job.
- Ephemeral runners provide stronger build isolation by starting clean and disappearing after the job.
- Network restrictions, resource limits, logging, updates, and careful runner labels are core parts of CI/CD security.
- A microVM-based environment can provide a stronger isolation boundary than a container alone for workloads that execute untrusted code.
What Are Self Hosted CI Runners?
Self-hosted runners are machines or environments that you deploy and manage yourself to execute CI jobs. GitHub describes a self-hosted runner as a system you deploy and manage to run jobs from GitHub Actions, while CircleCI supports self-hosted execution through container and machine runners. urlGitHub's self-hosted runner documentationhttps://docs.github.com/actions/hosting-your-own-runners
The attraction is straightforward. You decide what operating system, CPU architecture, dependencies, tools, network placement, and system configuration the job receives. That matters when a build needs access to a private Kubernetes cluster, internal packages, specialized hardware, or a custom toolchain.
The trade-off is just as straightforward: you now own the security boundary.
A runner should be treated as part of your production security architecture, not merely as a faster build machine.
That distinction becomes especially important when workflows execute pull requests, third-party dependencies, generated code, or commands produced by AI agents. The runner may be asked to execute code you have not fully reviewed.
Why Self-Hosted Runners Need Stronger Isolation
The biggest mistake is assuming that self hosted CI runners are safe because the repository is trusted. The workflow can install packages, execute scripts, access credentials, contact external services, and interact with the filesystem. GitHub's guidance similarly highlights the security exposure created by builds that use third-party libraries, runtimes, and tools. urlGitHub's guidance on hosted and self-hosted runnershttps://github.blog/enterprise-software/ci-cd/when-to-choose-github-hosted-runners-or-self-hosted-runners-with-github-actions
For untrusted workloads, build isolation should cover several layers:
- Filesystem: A job should not inherit unrelated files from an earlier build.
- Process isolation: A compromised process should not have an easy path to the host.
- Network isolation: Outbound traffic should be limited when the workload does not need unrestricted access.
- Credentials: Secrets should be scoped to the job and kept away from environments that do not need them.
- Resources: CPU, memory, and execution time should have explicit ceilings.
- Lifecycle: A contaminated environment should be disposable rather than repaired and reused.
Most people get this wrong by treating Docker as the entire security boundary. Containers provide useful process and filesystem isolation, but they share the host kernel. If your threat model includes hostile or unreviewed code, a separate kernel can provide a stronger boundary.
For genuinely untrusted execution, choose the isolation boundary according to the threat model, not according to what is easiest to configure.
How to Build a Safer Runner Architecture
Start with the job, not the infrastructure. Ask what the build actually needs to reach.
A typical secure design looks something like this:
- A commit or workflow trigger requests a runner.
- A clean environment is created from a known base image.
- The runner receives only the permissions and credentials required for that job.
- Network access is restricted to required destinations.
- The build runs with CPU, memory, and time limits.
- Logs and artifacts are collected centrally.
- The environment is destroyed after completion.
This architecture removes an especially dangerous property of permanent machines: accumulated state.
A persistent runner can contain package caches, credentials, temporary files, source trees, build artifacts, or configuration left behind by previous jobs. If one workflow compromises the machine, a later workflow may inherit the consequences.
Runner tags and labels also matter. Use them to direct jobs to environments with the capabilities they actually require. A job needing a particular architecture or dependency set should not accidentally land on a runner with broader access to sensitive systems.
For private infrastructure, network placement deserves the same attention as runner software. A runner that can reach production databases, internal admin panels, and cloud control planes has a much larger blast radius than one that can reach only source repositories and package registries.
The safest default is a clean runner with narrowly scoped access, explicit resource limits, centralized logs, and automatic teardown.
Ephemeral Runners vs Permanent Runners
The choice between ephemeral and permanent runners is mostly a trade-off between isolation, operational complexity, speed, and cost.
Ephemeral Runners
An ephemeral runner exists for a specific job or short workload and is then removed. This reduces persistent state and makes cleanup part of the architecture rather than a manual maintenance task.
The downside is startup and provisioning overhead. If every job installs the entire toolchain from scratch, builds can become unnecessarily slow and expensive.
A useful compromise is a prebuilt base image or snapshot. The environment starts from a known state, while the job itself still gets a clean filesystem and fresh execution context.
Permanent Runners
Permanent runners stay available between jobs. They can be convenient when startup time matters and when workloads benefit heavily from local caches.
But persistence creates maintenance work and a larger security problem. A runner that survives hundreds of builds can also accumulate hundreds of opportunities for stale software, forgotten credentials, or contaminated state.
Use permanent runners only when their operational advantages justify the additional persistence risk.
Running Self-Hosted Runners on Isolated Infrastructure
For teams executing Docker builds, AI-generated code, preview environments, or other workloads that should not share a host kernel with unrelated jobs, microVM-based infrastructure is one approach worth considering.
Krova Cloud's Cube is an isolated Firecracker microVM with its own Linux kernel. Each runner can start from a snapshot, run with full root access inside the environment, and be deleted after the pipeline finishes. The lifecycle of these self hosted CI runners can be managed through the REST API, SDK, or CLI. urlKrova Cloud's ephemeral CI and development environmentshttps://krova.cloud/solutions/ci-runners
That model fits a simple CI pattern: provision, execute, collect results, destroy.
Cubes are billed by the minute, and compute charges stop when a Cube is powered off. RAM and disk are reserved 1:1 with the host rather than oversold, which can make resource behavior more predictable for builds. urlKrova Cloud Cube pricinghttps://krova.cloud/pricing/cube
There is still engineering work involved. You need to decide which networks the runner can reach, what credentials it receives, which images are trusted, how logs are retained, and what happens when provisioning or teardown fails.
For teams that need a managed alternative to maintaining physical or virtual runner hosts, the point is not to eliminate those decisions. It is to make the execution environment itself disposable and isolated.
If your workloads include AI agents or untrusted code, the same principle applies beyond self hosted CI runners. A disposable microVM can limit the blast radius when generated code installs an unexpected package, modifies the filesystem, or consumes excessive resources. Krova Cloud's guidance recommends resource caps, restricted networking, scoped persistence, and centralized logging for these workloads. urlKrova Cloud's guidance on isolated AI coding environmentshttps://krova.cloud/blog/how-to-run-ai-coding-agents-safely-in-isolated-cloud-environments
The practical next step is small: pick one workflow that currently runs on a persistent host and make that workflow disposable first. Measure startup time, build duration, resource usage, and the network access it actually needs. Then tighten the boundary based on what you observe.
FAQ
Are Self-Hosted Runners More Secure Than Hosted Runners?
Not automatically. They give you more control, but you also become responsible for isolation, updates, credentials, networking, monitoring, and lifecycle management. Security depends on how the runner is configured and operated.
When Should I Use Ephemeral Runners?
Use ephemeral runners when builds execute untrusted or frequently changing code, handle sensitive workloads, or need strong separation between jobs. They are particularly useful when persistent runner state creates an unacceptable security risk.
Are Docker Containers Enough for Build Isolation?
They can be appropriate for many trusted workloads, but containers share the host kernel. If your threat model includes hostile code or meaningful container-escape risk, a microVM with its own kernel provides a stronger isolation boundary.
How Can I Start Using Ephemeral CI Runners?
Choose one pipeline, create a clean base environment, restrict its network and credentials, apply CPU and memory limits, capture logs, and automatically destroy the environment after the job. With Krova Cloud, you can manage Cube lifecycle from an existing CI system through the REST API, SDK, or CLI.



