Running CI/CD pipelines on shared cloud infrastructure means your build logs, source code, and artifacts pass through servers you don’t control. For teams handling proprietary code, compliance-sensitive workloads, or simply wanting faster builds on local hardware, self-hosted CI runners are the answer. This guide compares the three most popular self-hosted CI execution agents: GitHub Actions Runner, GitLab Runner, and Woodpecker CI — covering architecture, docker deployment, configuration, and when to choose each one.
Why Self-Host Your CI Runner?
Cloud-hosted CI is convenient but comes with trade-offs:
- Cost at scale: Shared runners charge per-minute. Running hundreds of builds monthly adds up quickly compared to using your own hardware.
- Security and compliance: Source code and build artifacts never leave your infrastructure. Critical for HIPAA, SOC 2, or internal security policies.
- Hardware control: Need GPUs for ML model training, ARM64 for cross-compilation, or 128 GB RAM for large builds? Self-hosted runners give you full hardware access.
- Network speed: Local runners pull dependencies from your internal artifact registry at LAN speeds instead of downloading over the internet.
- Custom environments: Pre-install proprietary SDKs, internal tooling, or licensed software that cloud runners can’t access.
The runner is the component that actually executes your pipeline steps. It connects to your CI platform, pulls job definitions, runs commands, and reports results back. Choosing the right runner affects everything from build speed to security posture.
Project Overview
| Feature | GitHub Actions Runner | GitLab Runner | Woodpecker CI |
|---|---|---|---|
| Repository | actions/runner | gitlab-org/gitlab-runner | woodpecker-ci/woodpecker |
| Stars | 5,954 | 2,551 | 6,834 |
| Language | C# | Go | Go |
| Last Updated | 2026-04-17 | 2026-04-18 | 2026-04-18 |
| License | MIT | MIT | Apache 2.0 |
| Docker Support | Native container actions | Docker executor, Docker-in-Docker | Docker-native (container-per-step) |
| Executor Types | Process, Containekubernetes | Shell, Docker, Kubernetes, Parallels, Custom | Docker, Kubernetes, Local, SSH |
| Platform Lock-in | GitHub only | GitLab only | Platform-agnostic |
| Scaling | Auto-scaling via actions-runner-controller | Auto-scaling via Docker Machine / Kubernetes | Horizontal via additional agents |
| Ephemeral Jobs | Yes (container actions) | Yes (Docker executor) | Yes (default model) |
GitHub Actions Runner is Microsoft’s official execution agent for GitHub Actions. It connects to your repository or organization and runs jobs defined in .github/workflows/. The runner itself is open source (MIT), though the Actions platform is proprietary. It supports both self-hosted Linux/Windows/macOS machines and container-based execution.
GitLab Runner is the execution agent for GitLab CI/CD. Written in Go, it’s lightweight and supports multiple executor backends including Docker, Kubernetes, Shell, and SSH. It can run jobs for GitLab.com (SaaS) or self-hosted GitLab instances. The auto-scaling feature via Docker Machine or Kubernetes makes it suitable for high-volume CI environments.
Woodpecker CI takes a different approach — it’s a complete CI/CD engine (server + agent) that’s fully open source and platform-agnostic. Rather than being tied to a specific Ggitearge, Woodpecker integrates with GitHub, GitLab, Gitea, Forgejo, and Bitbucket through OAuth. Its architecture is container-native: every pipeline step runs in its own isolated Docker container, making it ideal for teams that want a clean, self-contained CI system.
Installation and Configuration
GitHub Actions Runner
The GitHub Actions Runner can be deployed on any Linux, Windows, or macOS machine. Here’s the Docker-based deployment for a Linux runner:
| |
You can also deploy at scale using the official actions-runner-controller for Kubernetes:
| |
Register a runner manually on a bare-metal machine:
| |
Runner configuration is stored in .runner files within the runner directory. Each runner has labels that determine which workflow jobs it picks up:
| |
GitLab Runner
GitLab Runner supports multiple executor modes. The Docker executor is the most common for self-hosted setups:
| |
Deploy with Docker Compose:
| |
Register a runner programmatically:
| |
GitLab CI pipeline configuration uses .gitlab-ci.yml:
| |
Woodpecker CI
Woodpecker CI has a server-and-agent architecture. The server manages the UI, webhooks, and scheduling; agents execute the actual pipeline steps.
| |
Woodpecker pipeline configuration uses .woodpecker.yml in the repository root:
| |
Scale agents by adding more agent containers behind the same server:
| |
Architecture Comparison
Execution Model
GitHub Actions Runner uses a job-based model where each job runs on a single runner. Jobs can use container actions (Docker-based) or run directly on the host (process executor). The runner maintains a persistent connection to GitHub’s servers via WebSocket, pulling job assignments as they become available. Multiple runners can be organized into pools with label-based routing.
GitLab Runner operates similarly but with more executor flexibility. Each runner instance can handle multiple concurrent jobs (controlled by the concurrent setting). The Docker executor spawns a new container per job, providing isolation. The Kubernetes executor creates pods on demand. GitLab Runner also supports the services directive, which spins up additional containers (databases, caches) alongside the main job container.
Woodpecker CI uses a pipeline-as-containers model. Every step in a pipeline runs in its own Docker container, and steps execute sequentially by default. Parallel execution is possible using the depends_on directive. The architecture is simpler by design — no concept of “executors” since Docker is the default and primary execution backend. This makes Woodpecker easier to reason about but less flexible for non-container workloads.
Security Model
| Aspect | GitHub Actions Runner | GitLab Runner | Woodpecker CI |
|---|---|---|---|
| Runner Registration | Time-limited registration tokens | Registration tokens or JWT | Shared agent secret |
| Job Isolation | Container or process-level | Docker container / Kubernetes pod | Docker container per step |
| Secrets Storage | GitHub Encrypted Secrets | GitLab CI/CD Variables | Woodpecker Secrets (per-repo) |
| Network Access | Runner has full outbound access | Runner outbound; services internal | Agent-to-server only |
| Privileged Mode | Supported in container actions | Configurable per runner | Not available by default |
| Audit Logging | GitHub audit log + runner diagnostic logs | Runner logs + GitLab audit events | Server logs + agent logs |
For compliance-sensitive environments, GitLab Runner offers the most granular control — you can configure TLS verification, set specific Docker networks, restrict which images are allowed, and enable job-level logging rotation. GitHub Actions Runner benefits from GitHub’s enterprise audit infrastructure but gives you less control over the runner itself. Woodpecker CI’s simplicity is a security advantage — fewer moving parts means a smaller attack surface.
Performance and Scaling
When evaluating runners for production use, consider these factors:
Startup time: Woodpecker’s container-per-step model means each step pulls its image fresh unless cached. GitLab Runner with Docker executor caches images between jobs. GitHub Actions Runner reuses the host filesystem between jobs, giving the fastest warm-start times.
Concurrent execution: GitLab Runner’s
concurrentsetting controls how many jobs run simultaneously on a single instance. Woodpecker scales horizontally by adding more agent instances. GitHub Actions Runner scales by adding more runner instances managed through labels or the Kubernetes controller.Caching strategies: GitLab Runner supports S3, GCS, and Azure cache backends for shared caching across runners. GitHub Actions provides a
cacheaction with configurable keys. Woodpecker relies on Docker volume caching and therestore_cache/rebuild_cachepipeline steps.Resource limits: GitLab Runner lets you set CPU and memory limits per executor. GitHub Actions Runner inherits the host’s resource limits. Woodpecker agents can be constrained via Docker resource flags.
For teams running 50+ concurrent builds, GitLab Runner with Kubernetes executor provides the most robust auto-scaling. For smaller teams (5-20 concurrent builds), a few GitHub Actions Runners or Woodpecker agents on dedicated VMs work well.
Which Runner Should You Choose?
Choose GitHub Actions Runner if:
- Your team already uses GitHub for version control
- You want seamless integration with GitHub Actions ecosystem (Actions Marketplace)
- You need Windows or macOS runners alongside Linux
- You’re already invested in the GitHub ecosystem for issues, projects, and packages
Choose GitLab Runner if:
- You run a self-hosted GitLab instance
- You need Kubernetes-native CI execution
- You want the most executor variety (Docker, SSH, Shell, Kubernetes, Parallels)
- You need fine-grained caching with S3/GCS/Azure backends
Choose Woodpecker CI if:
- You want a fully open-source, self-contained CI/CD system
- You use Gitea or Forgejo as your Git forge
- You prefer a simple, container-native architecture
- You want platform independence — not locked into a single Git provider
- You value a minimal resource footprint and straightforward configuration
For related reading, see our self-hosted CI/CD platforms comparison for a broader overview of CI engines, the container build tools guide for optimizing build steps within your runners, and the self-hosted Git forge comparison for choosing the version control platform that pairs with your runner.
FAQ
Can I run GitHub Actions Runner without an internet connection?
GitHub Actions Runner requires outbound connectivity to github.com to receive job assignments and report results. It cannot operate in a fully air-gapped environment. If you need fully offline CI, consider Woodpecker CI with a self-hosted Gitea instance, or GitLab Runner connected to an air-gapped GitLab instance.
How do I secure secrets in self-hosted runners?
All three runners support encrypted secrets that are injected as environment variables during job execution. GitHub Actions uses repository or organization-level encrypted secrets. GitLab Runner uses CI/CD variables with masking and protection rules. Woodpecker CI stores secrets per-repository on the server, transmitted to agents over an encrypted channel. Never log secrets in pipeline output — all three platforms support secret masking in build logs.
Can I mix cloud and self-hosted runners in the same project?
Yes. GitHub Actions lets you route specific jobs to runs-on: self-hosted while others use runs-on: ubuntu-latest. GitLab Runner uses tags — tag your self-hosted runners and reference them with tags: [self-hosted] in .gitlab-ci.yml. Jobs without matching tags will use shared runners if available. Woodpecker CI only runs on your self-hosted agents, but you can connect it to cloud Git repositories.
How many concurrent jobs can a single runner handle?
GitHub Actions Runner processes one job at a time per instance — scale by adding more runner instances. GitLab Runner can handle multiple concurrent jobs on a single instance, controlled by the concurrent setting in config.toml (default: 1). Woodpecker agents process workflows sequentially but support multiple steps in parallel within a single workflow using the depends_on directive. Scale Woodpecker by deploying additional agent containers.
Do self-hosted runners support Docker-in-Docker (DinD)?
Yes, all three support Docker-in-Docker workflows. GitHub Actions Runner exposes the Docker socket via the docker:// service container. GitLab Runner enables DinD with privileged = true in the Docker executor configuration and the docker:dind service. Woodpecker CI mounts the Docker socket into step containers, allowing build steps that create Docker images. For production DinD, consider using Kaniko (building without Docker daemon) for better security isolation.
What happens if a runner goes offline during a job?
GitHub Actions will mark the job as failed and queue it for retry if another runner is available. GitLab Runner has a configurable output_limit and job timeout — if a runner disconnects, the job is marked stuck and can be retried manually or automatically. Woodpecker CI detects agent disconnection via heartbeat and marks the pipeline as errored; the server can reschedule failed pipelines if configured with retry policies.