Skip to content
Configuration

Configuration

ephemerd is configured with a single TOML file at <data-dir>/config.toml:

  • Linux / macOS: /var/lib/ephemerd/config.toml
  • Windows: C:\ProgramData\ephemerd\config.toml

Override the data directory with --data-dir:

ephemerd serve --data-dir /opt/ephemerd

Provider auto-detection

Currently only one provider can be configured. ephemerd detects the active provider based on which section has credentials set, in this order:

  1. Forgejo – if forgejo.instance_url is set
  2. Gitea – if gitea.instance_url is set
  3. GitLab – if gitlab.instance_url is set
  4. Woodpecker – if woodpecker.server_url is set
  5. GitHub – default when none of the above are configured

Complete annotated example

# =============================================================================
# ephemerd configuration
# =============================================================================

# --- GitHub Actions (default provider) ----------------------------------------
[github]
owner = "your-org"
# repos = ["repo1", "repo2"]        # optional — omit for org-level runners

# Authentication: PAT or GitHub App (choose one)
# token = "ghp_..."                  # or set GITHUB_TOKEN env var
# app_id = 12345
# installation_id = 67890
# private_key_path = "/etc/ephemerd/app.pem"

# poll_interval = "30s"             # how often to poll for queued jobs

# --- Forgejo Actions ---------------------------------------------------------
# [forgejo]
# instance_url = "https://codeberg.org"
# token = "runner-registration-token"
# owner = "your-org"                 # optional — omit for instance-level runners
# repos = ["repo1"]                  # optional — omit for all repos
# job_image = "gitea/runner-images:ubuntu-24.04"

# --- Gitea Actions -----------------------------------------------------------
# [gitea]
# instance_url = "https://gitea.example.com"
# token = "runner-registration-token"
# owner = "your-org"
# repos = ["repo1"]
# job_image = "gitea/runner-images:ubuntu-24.04"

# --- GitLab CI ----------------------------------------------------------------
# [gitlab]
# instance_url = "https://gitlab.com"
# token = "glrt-xxxxxxxxxxxxxxxxxxxx"  # runner auth token (GitLab 16+)
# tags = ["linux", "docker"]

# --- Woodpecker CI -----------------------------------------------------------
# [woodpecker]
# server_url = "woodpecker.example.com:9000"
# agent_secret = "shared-secret"

# --- Webhook delivery --------------------------------------------------------
[webhook]
# tunnel = "none"                    # "none" (polling), "localtunnel", or "ngrok"
# tunnel_url = ""                    # localtunnel: self-hosted server URL
# ngrok_authtoken = ""               # ngrok auth token (or NGROK_AUTHTOKEN env)
# secret = ""                        # HMAC secret (auto-generated if tunnel is active)
# port = 8080                        # listen port for webhook/health endpoint
# tls_cert = ""                      # TLS cert path (direct TLS, no tunnel)
# tls_key = ""                       # TLS key path

# --- Runner -------------------------------------------------------------------
[runner]
max_concurrent = 4                   # max simultaneous jobs
# extra_labels = ["gpu", "large"]    # additional labels for runner registration
# default_image = ""                 # override default container image per platform
# job_timeout = "2h"                 # max duration per job
# shutdown_timeout = "5m"            # grace period for running jobs on shutdown

# --- Linux VM (Windows/macOS hosts only) --------------------------------------
[vm.linux]
# enabled = false                    # spin up a Linux VM for cross-OS Linux jobs
# cpus = 2                           # virtual CPUs
# memory_mb = 2048                   # memory in MB
# disk_size_gb = 50                  # sparse disk size in GB

# --- macOS VM (macOS hosts only) ----------------------------------------------
[vm.macos]
# disk_image = ""                    # path to pre-installed macOS VM disk, or
#                                    # auto-pulled from Tart OCI registry
# cpus = 4                           # CPUs per VM
# memory_mb = 8192                   # memory per VM in MB
# max_concurrent = 0                 # max simultaneous macOS VMs (0 = auto-detect)

# --- Networking ---------------------------------------------------------------
[network]
# subnet = ""                        # container subnet (auto-selected if empty)
# mtu = 0                            # bridge MTU (auto-detected from host if 0)

# --- Docker-in-Docker --------------------------------------------------------
[dind]
# enabled = false                    # mount fake Docker socket into containers

# --- Metrics ------------------------------------------------------------------
[metrics]
# enabled = false                    # expose Prometheus /metrics endpoint
# port = 9090                        # metrics listen port
# path = "/metrics"                  # metrics endpoint path

# --- Logging ------------------------------------------------------------------
[log]
level = "info"                       # debug, info, warn, error
format = "text"                      # text or json
# log_retention = "7d"               # max age for job log files (e.g. "7d", "24h")

Section reference

[github]

GitHub Actions provider configuration. This is the default provider.

FieldTypeDefaultDescription
ownerstringrequiredGitHub organization or user name
reposstring array[]Limit to specific repos. Omit for org-level runners.
tokenstring$GITHUB_TOKENPersonal access token. Falls back to GITHUB_TOKEN env var.
app_idintegerGitHub App ID (alternative to PAT auth)
installation_idintegerGitHub App installation ID (required with app_id)
private_key_pathstringPath to GitHub App private key PEM file (required with app_id)
poll_intervalstring"30s"How often to poll for queued jobs

Authentication requires either token (or GITHUB_TOKEN env var) or all three GitHub App fields (app_id, installation_id, private_key_path).

[forgejo]

Forgejo Actions provider. Setting instance_url activates this provider.

FieldTypeDefaultDescription
instance_urlstringForgejo instance URL (e.g., https://codeberg.org)
tokenstringrequiredRunner registration token from Forgejo admin
ownerstring""Organization or user. Empty for instance-level runners.
reposstring array[]Limit to specific repos. Empty for all repos.
job_imagestring"gitea/runner-images:ubuntu-24.04"Default job execution image

[gitea]

Gitea Actions provider. Setting instance_url activates this provider.

FieldTypeDefaultDescription
instance_urlstringGitea instance URL (e.g., https://gitea.example.com)
tokenstringrequiredRunner registration token from Gitea admin
ownerstring""Organization or user. Empty for instance-level runners.
reposstring array[]Limit to specific repos. Empty for all repos.
job_imagestring"gitea/runner-images:ubuntu-24.04"Default job execution image

[gitlab]

GitLab CI provider. Setting instance_url activates this provider.

FieldTypeDefaultDescription
instance_urlstringGitLab instance URL (e.g., https://gitlab.com)
tokenstringrequiredRunner authentication token (glrt-xxx format for GitLab 16+)
tagsstring array[]Runner tags for job matching

[woodpecker]

Woodpecker CI provider. Setting server_url activates this provider.

FieldTypeDefaultDescription
server_urlstringWoodpecker server gRPC URL (e.g., woodpecker.example.com:9000)
agent_secretstringrequiredShared secret for agent authentication

[webhook]

Webhook delivery and tunnel configuration. By default, ephemerd polls for jobs. Enable a tunnel for instant webhook delivery.

FieldTypeDefaultDescription
tunnelstring"none""none" (polling), "localtunnel", or "ngrok"
tunnel_urlstring""Self-hosted localtunnel server URL
ngrok_authtokenstring""ngrok auth token (or use NGROK_AUTHTOKEN env var)
secretstringauto-generatedWebhook HMAC secret. Auto-generated when a tunnel is active.
portinteger8080Listen port for webhook and health endpoint
tls_certstring""TLS certificate path (for direct TLS without a tunnel)
tls_keystring""TLS private key path

[runner]

Job execution settings.

FieldTypeDefaultDescription
max_concurrentinteger4Maximum simultaneous jobs
extra_labelsstring array[]Additional labels for runner registration (e.g., ["gpu"])
default_imagestringplatform-specificOverride the default container image
job_timeoutstring"2h"Maximum duration per job
shutdown_timeoutstring"5m"Grace period for running jobs during shutdown

Default images when default_image is not set:

  • Linux: ghcr.io/actions/actions-runner:latest
  • Windows: mcr.microsoft.com/windows/servercore:ltsc20XX (auto-detected from host build)

VM resource planning (Windows and macOS): On Windows and macOS, max_concurrent applies to the entire ephemerd instance — Linux container jobs and native OS jobs share the same concurrency pool. All Linux jobs run inside a single VM (WSL2 on Windows, Virtualization.framework on macOS), so if max_concurrent = 4, that VM could be running 4 jobs simultaneously. Size the VM’s CPU and memory ([vm.linux]) accordingly, or jobs will compete for resources and slow each other down.

[vm.linux]

Linux VM for running Linux jobs on Windows or macOS hosts.

FieldTypeDefaultDescription
enabledbooleanfalseEnable the Linux VM
cpusinteger2Virtual CPUs assigned to the VM
memory_mbinteger2048Memory in MB
disk_size_gbinteger50Sparse disk size in GB

On Windows, this creates a WSL2 distro with an embedded rootfs. On macOS, it uses Virtualization.framework.

[vm.macos]

macOS VM configuration for running macOS jobs (macOS hosts only). macOS jobs always run in per-job VMs – there is no toggle to disable this on darwin hosts.

FieldTypeDefaultDescription
disk_imagestring""Path to a pre-installed macOS VM disk, or auto-pulled from Tart OCI registry
cpusinteger4CPUs per VM
memory_mbinteger8192Memory per VM in MB
max_concurrentintegerauto-detectedMaximum simultaneous macOS VMs. Defaults to auto-detection from host CPU count.

[network]

Container networking configuration.

FieldTypeDefaultDescription
subnetstringauto-selectedContainer subnet CIDR. Auto-selected from a private range if empty.
mtuintegerauto-detectedBridge MTU. Auto-detected from the host’s default interface if 0.

[dind]

Docker-in-Docker support.

FieldTypeDefaultDescription
enabledbooleanfalseMount a fake Docker socket (/var/run/docker.sock) into job containers

[metrics]

Prometheus metrics endpoint.

FieldTypeDefaultDescription
enabledbooleanfalseEnable the /metrics endpoint
portinteger9090Metrics listen port
pathstring"/metrics"Metrics endpoint path

[log]

Logging configuration.

FieldTypeDefaultDescription
levelstring"info"Log level: debug, info, warn, error
formatstring"text"Log format: text or json
log_retentionstring"7d"Max age for job log files. Supports Go durations ("168h") and day shorthand ("7d").