Vetrix Docs

CI Runners

A runner is an agent process that polls Vetrix for pending jobs, executes them, and streams logs back. Runners authenticate with a registration token issued by an admin.

Registering a runner

Only admins can register runners. Use the admin API or the web UI under Admin → Runners.

curl -X POST https://<vetrix-host>/api/v1/admin/runners \
  -H "Authorization: Bearer <admin-token>" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "linux-amd64-01",
    "tags": ["linux", "amd64", "docker"]
  }'

Response:

{
  "token": "runner_...",
  "runner": {
    "id": "01HZ...",
    "name": "linux-amd64-01",
    "tags": ["linux", "amd64", "docker"],
    "status": "offline"
  }
}

The token field is shown once only. Copy it immediately — it cannot be retrieved again. The stored value is a SHA-256 hash and cannot be reversed.

Starting a runner

The runner binary is vetrix-runner. Pass the token and endpoint at startup:

vetrix-runner \
  --url https://<vetrix-host> \
  --token runner_... \
  --name linux-amd64-01 \
  --concurrency 4

Or use environment variables:

export VETRIX_RUNNER_URL=https://<vetrix-host>
export VETRIX_RUNNER_TOKEN=runner_...
vetrix-runner --name linux-amd64-01 --concurrency 4

The runner registers itself on first start and then long-polls for jobs.

Runner configuration file

vetrix-runner.toml:

url         = "https://<vetrix-host>"
token       = "runner_..."
name        = "linux-amd64-01"
concurrency = 4

[docker]
privileged = false
volumes    = ["/cache:/cache"]

[executor]
type      = "docker"
image     = "alpine:3.19"  # fallback image if job doesn't specify one

Job routing

Jobs are routed to available runners. All registered runners poll for pending jobs and pick up work from the queue.

Runner lifecycle

Status Meaning
offline Never connected, or not seen in the last 60 seconds
idle Connected and waiting for a job
busy Currently executing one or more jobs

Runners update their last_seen_at timestamp every 30 seconds. A runner that stops updating is marked offline after 60 seconds.

Listing runners

curl https://<vetrix-host>/api/v1/admin/runners \
  -H "Authorization: Bearer <admin-token>"

Deleting a runner

Deleting a runner invalidates its token. Any in-flight jobs on that runner are marked failed.

curl -X DELETE https://<vetrix-host>/api/v1/admin/runners/<runner-id> \
  -H "Authorization: Bearer <admin-token>"

Docker executor

The Docker executor is the default and recommended choice. Each job gets a fresh container from the specified image.

Runners must have Docker installed and the runner process must have socket access:

# Add the runner service account to the docker group
usermod -aG docker vetrix-runner

Containerized worker (docker-compose --target worker): group name membership does not carry into a container — the worker must be granted the socket's numeric owning GID via group_add. See worker-docker-socket.md.

For security, privileged: false is the default. Enable only for jobs that need it (e.g., Docker-in-Docker builds).

Docker-in-Docker

build-image:
  stage: build
  image: docker:24
  variables:
    DOCKER_HOST: unix:///var/run/docker.sock
  commands:
    - docker build -t myapp .

Running as a systemd service

/etc/systemd/system/vetrix-runner.service:

[Unit]
Description=Vetrix CI Runner
After=network.target docker.service

[Service]
User=vetrix-runner
ExecStart=/usr/local/bin/vetrix-runner --config /etc/vetrix-runner/config.toml
Restart=always
RestartSec=5

[Install]
WantedBy=multi-user.target
systemctl enable --now vetrix-runner

Autoscaling

For dynamic workloads, runners can be deployed as Kubernetes pods or EC2 spot instances. The runner process itself is stateless — scale by increasing concurrency on existing runners or by registering additional runners and adding them to the same pool.

Resource limits (admin-configured)

Admins can cap runner resource usage instance-wide. See ../admin/cicd.md for the ci.max_job_timeout, ci.max_concurrency, and allowed Docker image settings.