Skip to main content
A provisioner watches how many jobs are waiting and starts ephemeral, single-job runner containers to meet that demand — then lets them exit. It’s the alternative to manually starting runners: instead of keeping a fleet online at idle, you run one provisioner and it scales runners to the work.
Today Shipfox ships a Docker provisioner. The provisioner core is provider-agnostic, so other backends (Kubernetes, cloud VMs) can follow — those are roadmap.

How it works

The provisioner runs a continuous control loop against the Shipfox API:
1

Advertise capacity

For each template it can start, the provisioner reports free slots (max_concurrency minus what’s already running).
2

Poll for demand

It long-polls the API. When jobs are waiting for labels a template provides, the API grants reservations.
3

Mint ephemeral tokens

For each reservation, the provisioner mints a single-use ephemeral registration token (sf_ert_…, distinct from the long-lived sf_mrt_… manual token) that the new runner uses to register once.
4

Launch and reconcile

It starts a container per reservation, injecting the API URL, the ephemeral token, and the template’s labels. It reports each container’s lifecycle and reconciles against Docker on startup, reaping containers that never registered. If Docker is unreachable it advertises zero capacity and backs off rather than double-launching.
Each provisioned runner claims one job, runs it, and exits — so a burst of work spins up runners and quiet periods cost nothing.

Templates

A template maps a label set to the container that provides it. Templates live in a YAML file the provisioner loads at startup:
templates:
  docker-ubuntu22-2vcpu:
    labels: [ubuntu22, ubuntu22-2vcpu]   # labels the started runner registers with
    image: shipfox-runner:ubuntu22        # container image to start
    cpu: 2                                 # vCPUs (also the selection cost)
    memory: 4GiB                           # memory allocation
    max_concurrency: 100                   # cap on live containers from this template
When several templates satisfy a job’s labels, the provisioner picks the cheapest (fewest vCPUs), then the most specific.

Configuration

The Docker provisioner is a standalone app (@shipfox/provisioner-docker) configured through environment variables:
VariableRequiredPurpose
SHIPFOX_API_URLyesShipfox API base URL
SHIPFOX_PROVISIONER_TOKENyesLong-lived token that authenticates the provisioner
SHIPFOX_PROVISIONER_TEMPLATES_FILEyesPath to the templates YAML
SHIPFOX_RUNNER_API_URLnoAPI URL injected into runner containers, if they reach the API on a different host
SHIPFOX_PROVISIONER_DOCKER_HOSTnoDocker daemon socket or host (defaults to the local socket)
SHIPFOX_PROVISIONER_DOCKER_NETWORKnoDocker network to attach runners to
SHIPFOX_PROVISIONER_DOCKER_EXTRA_HOSTSnoExtra host mappings (e.g. host.docker.internal:host-gateway)
Create the provisioner token (SHIPFOX_PROVISIONER_TOKEN) on Settings → Runner Provisioners in the dashboard — the value is shown once, and you can revoke it there. Poll intervals, reservation limits, token batch size, and the registration deadline have sensible defaults and can be tuned with additional SHIPFOX_PROVISIONER_* variables.

Deploying it

Run the provisioner as a long-lived process next to a Docker daemon it can control:
SHIPFOX_API_URL=https://api.shipfox.io \
SHIPFOX_PROVISIONER_TOKEN=<YOUR_PROVISIONER_TOKEN> \
SHIPFOX_PROVISIONER_TEMPLATES_FILE=./templates.yaml \
pnpm --filter=@shipfox/provisioner-docker start
It authenticates on startup, then enters the control loop until it receives a shutdown signal, at which point it stops claiming new work and exits cleanly.

Runners

Manually started runners, labels, and registration.

Installation

Standing up the API, storage, and your first runner.