Skip to content
Providers

Providers

ephemerd supports five CI providers. The provider determines how jobs are discovered, how runners are registered, and which runner binary executes inside the container. Everything else – container runtime, networking, VM support, concurrency control – is shared across all providers.

Auto-Detection

Currently only one provider can be configured. The provider is auto-detected from which configuration section has credentials set. The precedence order is:

Forgejo > Gitea > GitLab > Woodpecker > GitHub

GitHub is the default when no other provider section is configured.

GitHub

The primary provider. ephemerd polls the GitHub API (or receives webhooks) for queued workflow jobs, registers a JIT (just-in-time) runner for each one, and deregisters it after the job completes.

Authentication: Personal Access Token or GitHub App.

Job discovery: Polling (default) or webhooks (via tunnel or direct TLS). See Job Discovery for details.

Runner binary: The official GitHub Actions runner (actions/runner), embedded in the ephemerd binary.

[github]
owner = "your-org"
repos = ["repo1", "repo2"]   # optional: omit for org-level runners
# poll_interval = "10s"

# PAT auth (or set GITHUB_TOKEN env var):
# token = "ghp_xxx"

# GitHub App auth:
# app_id = 123456
# installation_id = 789012
# private_key_path = "/var/lib/ephemerd/app.pem"

Forgejo

Forgejo Actions uses GitHub Actions workflow syntax but a different protocol. There are two ways to run Forgejo jobs:

Option 1: ephemerd-runner-forgejo (recommended)

ephemerd includes ephemerd-runner-forgejo — a lightweight Go binary that speaks the Forgejo ConnectRPC protocol and executes workflow steps directly via os/exec. Single container, no Docker socket needed. Supports Linux, Windows, and macOS.

ephemerd-runner-forgejo handles run: steps. uses: steps (actions) are not yet supported — if your workflows rely heavily on actions, use Option 2.

Runner binary: ephemerd-runner-forgejo (built into ephemerd).

Option 2: upstream forgejo-runner + fake Docker socket

Use the upstream forgejo-runner with ephemerd’s fake Docker socket (pkg/dind). This is the two-container model — the runner daemon runs in one container and creates a separate job container via the Docker API. ephemerd intercepts those Docker API calls and translates them to containerd operations.

This option has full uses: action support via the embedded nektos/act engine, but only works on Linux.

Runner binary: forgejo-runner (pre-installed in the runner image).

Config

Authentication: Runner registration token from Forgejo admin.

Job discovery: ConnectRPC FetchTask polling.

[forgejo]
instance_url = "https://codeberg.org"
token = "runner-registration-token"
owner = "your-org"
# repos = ["repo1", "repo2"]  # optional: omit for all repos
# job_image = "gitea/runner-images:ubuntu-24.04"

Gitea

Gitea Actions shares the same workflow syntax and ConnectRPC protocol as Forgejo. The same two options apply — ephemerd-runner-forgejo (as gitea-runner) for the single-container model, or upstream act_runner with the fake Docker socket for full action support.

Authentication: Runner registration token from Gitea admin.

Job discovery: ConnectRPC FetchTask polling.

[gitea]
instance_url = "https://gitea.example.com"
token = "runner-registration-token"
owner = "your-org"
# repos = ["repo1", "repo2"]  # optional: omit for all repos
# job_image = "gitea/runner-images:ubuntu-24.04"

GitLab

GitLab CI uses a custom executor model where gitlab-runner drives the job lifecycle. Unlike the other providers, ephemerd does not discover jobs directly. Instead, gitlab-runner polls GitLab for jobs and calls ephemerd scripts to prepare, run, and clean up containers.

The lifecycle is inverted: gitlab-runner receives the job and calls ephemerd to manage the container. ephemerd responds to requests rather than initiating them.

gitlab-runner                    ephemerd
     |                              |
     |<-- polls GitLab for jobs --> |
     |     (gitlab-runner drives)   |
     |                              |
     |-- prepare -----------------> | create container
     |<-- build_dir path ---------- |
     |                              |
     |-- run (job script) --------> | exec in container
     |<-- exit code --------------- |
     |                              |
     |-- cleanup -----------------> | destroy container
     |                              |

Authentication: Runner authentication token (glrt-xxx for GitLab 16+).

Job discovery: gitlab-runner polls GitLab. ephemerd does not poll GitLab directly.

Runner binary: Official gitlab-runner in custom executor mode.

[gitlab]
instance_url = "https://gitlab.com"
token = "glrt-xxxxxxxxxxxx"
tags = ["linux", "docker", "ephemerd"]

Woodpecker CI

Woodpecker CI uses a server/agent architecture. The Woodpecker agent connects to the Woodpecker server via gRPC and polls for jobs using a shared secret for authentication. ephemerd manages the agent lifecycle and container execution.

Woodpecker requires a forge backend (Gitea, Forgejo, GitHub, or GitLab) for repository management. ephemerd handles the agent side only – it does not replace the Woodpecker server or the forge backend.

Authentication: Shared secret between agent and server.

Job discovery: Woodpecker agent gRPC connection to server.

Runner binary: Woodpecker agent binary.

[woodpecker]
server_url = "woodpecker.example.com:9000"
agent_secret = "your-shared-secret"

What Stays the Same

Regardless of which provider is active, the following subsystems are shared:

  • Container runtime (containerd, OCI images, overlayfs/windows snapshotter)
  • WSL2 dispatch for Linux jobs on Windows hosts
  • macOS VM support via Virtualization.framework
  • CNI bridge networking (Linux) and HCN NAT networking (Windows)
  • Concurrency limiting, job dedup, and graceful drain
  • gRPC control plane (ephemerd status, ephemerd jobs)