outrunner

package module
v0.1.0-rc2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 30, 2026 License: MIT Imports: 12 Imported by: 0

README

gha-outrunner

CI Go Report Card Go Reference

Ephemeral GitHub Actions runners, no Kubernetes required.

How gha-outrunner works

outrunner provisions fresh Docker containers or VMs for each GitHub Actions job, then destroys them when the job completes. It uses GitHub's scaleset API to register as an autoscaling runner group.

Why? GitHub's Actions Runner Controller (ARC) requires Kubernetes. If you're running on bare metal or a simple VPS, you shouldn't need a cluster just to get ephemeral runners. outrunner gives you the same isolation guarantees with Docker, libvirt, or Tart.

Provisioners

Provisioner Host OS Runner OS How it works
Docker Linux, macOS Linux Container per job. Fastest startup.
libvirt Linux Windows, Linux KVM VM from qcow2 golden image with CoW overlays. QEMU Guest Agent for command execution.
Tart macOS (Apple Silicon) macOS, Linux (ARM64) VM clone per job. Tart guest agent for command execution.

Install

macOS (Homebrew)
brew tap NetwindHQ/tap
brew install outrunner
Ubuntu / Debian
curl -LO https://github.com/NetwindHQ/gha-outrunner/releases/latest/download/outrunner_amd64.deb
sudo dpkg -i outrunner_amd64.deb
CentOS / RHEL
curl -LO https://github.com/NetwindHQ/gha-outrunner/releases/latest/download/outrunner_amd64.rpm
sudo rpm -i outrunner_amd64.rpm
From source
go install github.com/NetwindHQ/gha-outrunner/cmd/outrunner@latest
Binary download

Prebuilt binaries for Linux (amd64, arm64) and macOS (arm64) are available on the Releases page.

Quick Start

outrunner \
  --url https://github.com/your/repo \
  --token ghp_xxx \
  --config outrunner.yml

See the tutorials for step-by-step setup guides for each backend.

Documentation

Tutorials

Step-by-step guides to get your first runner working.

How-to Guides

Solutions for specific tasks.

Reference

Technical specifications.

Explanation

Background and design decisions.

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func ResolveToken

func ResolveToken(flagToken string, cfg *Config) (string, error)

ResolveToken determines the GitHub token using the following precedence:

  1. flagToken (--token CLI flag)
  2. GITHUB_TOKEN environment variable
  3. $CREDENTIALS_DIRECTORY/github-token (systemd-creds)
  4. token_file from config

func ResolveURL

func ResolveURL(flagURL string, cfg *Config) (string, error)

ResolveURL determines the GitHub URL using the following precedence:

  1. flagURL (--url CLI flag)
  2. url from config

Types

type Config

type Config struct {
	URL       string                  `yaml:"url"`
	TokenFile string                  `yaml:"token_file"`
	Runners   map[string]RunnerConfig `yaml:"runners"`
}

Config is the outrunner configuration file format.

func LoadConfig

func LoadConfig(path string) (*Config, error)

LoadConfig reads and parses a config file.

type DockerImage

type DockerImage struct {
	Image     string `yaml:"image"`
	RunnerCmd string `yaml:"runner_cmd"`
}

DockerImage configures a Docker-based runner.

type LibvirtImage

type LibvirtImage struct {
	Path      string `yaml:"path"`
	RunnerCmd string `yaml:"runner_cmd"`
	Socket    string `yaml:"socket"`
	CPUs      int    `yaml:"cpus"`
	MemoryMB  int    `yaml:"memory"`
}

LibvirtImage configures a libvirt/QEMU-based runner.

type Provisioner

type Provisioner interface {
	// Start provisions a new runner environment and starts the GitHub Actions
	// runner process inside it. The runner should use the JIT config to
	// register itself with GitHub and pick up the assigned job.
	//
	// Start must return after the runner process has been launched.
	// It does not need to wait for the job to complete.
	Start(ctx context.Context, req *RunnerRequest) error

	// Stop tears down the runner environment. Called after the job completes
	// or if the runner needs to be forcefully removed.
	Stop(ctx context.Context, name string) error

	// Close releases any resources held by the provisioner (e.g., Docker client).
	Close() error
}

Provisioner creates and destroys ephemeral runner environments. Each implementation handles a different backend (Docker, libvirt, etc.).

type RunnerConfig

type RunnerConfig struct {
	Labels     []string      `yaml:"labels"`
	MaxRunners int           `yaml:"max_runners,omitempty"`
	Docker     *DockerImage  `yaml:"docker,omitempty"`
	Libvirt    *LibvirtImage `yaml:"libvirt,omitempty"`
	Tart       *TartImage    `yaml:"tart,omitempty"`
}

RunnerConfig defines a runner environment and the scale set it registers. The map key in Config.Runners is used as the scale set name. Exactly one of Docker, Libvirt, or Tart must be set.

func (*RunnerConfig) ProviderType

func (r *RunnerConfig) ProviderType() string

ProviderType returns which provisioner backend this runner uses.

type RunnerPhase

type RunnerPhase int

RunnerPhase represents the current lifecycle phase of a runner.

const (
	RunnerProvisioning RunnerPhase = iota
	RunnerIdle
	RunnerRunning
	RunnerStopping
)

func (RunnerPhase) String

func (p RunnerPhase) String() string

type RunnerRequest

type RunnerRequest struct {
	// Name is a unique identifier for this runner instance.
	Name string

	// JITConfig is the base64-encoded JIT configuration from GitHub.
	// Pass to: ./run.sh --jitconfig <JITConfig>
	JITConfig string

	// Runner is the matched runner configuration.
	Runner *RunnerConfig
}

RunnerRequest contains everything a provisioner needs to start a runner.

type RunnerSnapshot

type RunnerSnapshot struct {
	Name      string
	RunnerID  int
	Phase     RunnerPhase
	CreatedAt time.Time
	StartedAt time.Time
}

RunnerSnapshot is a point-in-time copy of a runner's state, safe to read without holding the scaler's mutex.

type RunnerState

type RunnerState struct {
	Name      string
	RunnerID  int // from GenerateJitRunnerConfig().Runner.ID
	Phase     RunnerPhase
	CreatedAt time.Time
	StartedAt time.Time // when Start() completed (provisioning finished)
	// contains filtered or unexported fields
}

RunnerState holds the full state of a single runner instance.

func (*RunnerState) SignalDone

func (r *RunnerState) SignalDone()

SignalDone closes the done channel, signaling the runner goroutine to stop. Safe to call multiple times.

type ScaleSetClient

type ScaleSetClient interface {
	GenerateJitRunnerConfig(ctx context.Context, setting *scaleset.RunnerScaleSetJitRunnerSetting, scaleSetID int) (*scaleset.RunnerScaleSetJitRunnerConfig, error)
	RemoveRunner(ctx context.Context, runnerID int64) error
}

ScaleSetClient is the subset of scaleset.Client that Scaler uses. Extracted as an interface for testability.

type Scaler

type Scaler struct {
	// contains filtered or unexported fields
}

Scaler implements listener.Scaler by provisioning real runner environments. Each runner gets its own goroutine that manages the full lifecycle: provisioning, waiting for job completion, stopping, and deregistration.

func NewScaler

func NewScaler(logger *slog.Logger, client ScaleSetClient, scaleSetID, maxRunners int, namePrefix string, runner *RunnerConfig, prov Provisioner) *Scaler

func (*Scaler) HandleDesiredRunnerCount

func (s *Scaler) HandleDesiredRunnerCount(ctx context.Context, count int) (int, error)

func (*Scaler) HandleJobCompleted

func (s *Scaler) HandleJobCompleted(ctx context.Context, jobInfo *scaleset.JobCompleted) error

func (*Scaler) HandleJobStarted

func (s *Scaler) HandleJobStarted(ctx context.Context, jobInfo *scaleset.JobStarted) error

func (*Scaler) Runners

func (s *Scaler) Runners() []RunnerSnapshot

Runners returns a snapshot of current runner states.

func (*Scaler) Shutdown

func (s *Scaler) Shutdown(ctx context.Context)

Shutdown cancels all runner goroutines and waits for them to finish.

type TartImage

type TartImage struct {
	Image     string `yaml:"image"` // OCI image or local VM name
	RunnerCmd string `yaml:"runner_cmd"`
	CPUs      int    `yaml:"cpus"`
	MemoryMB  int    `yaml:"memory"`
}

TartImage configures a Tart-based runner (macOS/Linux on Apple Silicon).

Directories

Path Synopsis
cmd
outrunner command
provisioner

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL