Step by step
1
GitHub says a job is queued
A
workflow_job webhook arrives. If none of the job’s labels start with
airship-, Airship stops here and the job costs nothing. Jobs on
ubuntu-latest never touch any of this.2
Airship reads your config at that commit
airship.yaml is read from the job’s own commit, so a branch can try a
new machine shape before it is merged. Organization-wide definitions in
.airship are read from that repository’s
default branch.If the file does not parse, the job is skipped with a reason naming the
field, and the error lands on Events.3
The job is planned once
Every matching definition becomes an ordered list of attempts — one per
combination in its fallback lists, then any
other definition sharing the label. The plan is fixed now. Editing the
file afterwards does not change how a waiting job is served.
4
A runner slot is claimed
Under a row lock on your installation, Airship checks the live runner
count against
max_runners. Several control
plane replicas cannot both take the last slot.5
GitHub mints a single-use registration
A just-in-time runner configuration, good for exactly one registration and
one job, scoped to that repository. Its GitHub runner ID is stored
before the machine is created, so nothing can boot without a record to
clean up.Registration takes a round trip. If the job was cancelled or picked up
elsewhere in that window, no machine is created and the registration is
removed at once.
6
Your cloud launches one instance
Airship opens your sealed credential, makes one API call, and discards
it. The machine carries tags identifying the installation, repository and
runner, so you can group a bill by repository with no extra
instrumentation.On first use in a region, Airship also creates the shared pieces it needs
— an egress-only security group, or a network, or a NAT — and reuses them
forever after. What Airship creates in your
account.
7
The machine boots and takes the job
A startup script schedules its own shutdown deadline first, then installs
(or finds)
actions/runner, registers with the configuration it was
given, and runs as an ephemeral runner. It exits after one job.The runner talks to GitHub directly. Logs, artifacts, secrets and
checkouts never pass through Airship.8
The machine destroys itself
Every exit path powers the machine off: the job finishing, the runner
crashing, the script failing, the deadline expiring. Airship then deletes
the instance along with the disks, address and interface it created, and
removes the GitHub registration two minutes later — but only if GitHub
agrees the runner is not busy.
One job per machine
This is the property everything else rests on. A runner is registered for a single job and the machine is destroyed afterwards, so no job can read another’s checkout, caches on disk, environment or in-memory state. There is no pool of warm machines quietly accumulating state between jobs — even idle runners are replaced the moment they take a job.When things go wrong
Clouds run out of capacity, webhooks arrive out of order, and control planes get restarted mid-launch. The design assumes all three.A launch fails
A launch fails
The next attempt in the plan is tried. A failure your cloud marks
retryable — capacity, throttling — starts the list over, until the job has
had three failed launches or one per attempt, whichever is more. A
permanent failure such as a missing image stops once every attempt has
been tried. Every failure is on Events, with your cloud’s own message.
The job is cancelled
The job is cancelled
While queued, no machine is created and the registration is removed. While
running, the runner stops, the machine powers off and the instance is
deleted. Cancelling a job is the cheapest way to confirm this for
yourself.
The answer to a launch is lost
The answer to a launch is lost
The runner is marked
launch_unconfirmed: the cloud may or may not have
made a machine. The job is retried, and an inventory scan either finds the
machine and revives the runner or, after a grace period, writes it off.
Nothing is left running and nothing is double-charged for long.A control plane replica dies mid-launch
A control plane replica dies mid-launch
Launching runners hold a lease. When it expires another replica takes
over, reconciling against what the cloud actually has rather than what the
database last wrote.
A machine is stopped by hand
A machine is stopped by hand
It runs no job and still bills for its disk, so it is treated as gone: the
next inventory pass deletes it and the runner is finished.
The control plane disappears entirely
The control plane disappears entirely
Every machine schedules its own shutdown before anything else runs, so
nothing runs forever. You may be left with instances to clean up by tag.
Runner states
A launched runner that no job arrives for within ten minutes finishes as
idle — almost always a label mismatch. Every machine has a hard lifetime
(eight hours by default) after which it finishes as max_lifetime.
Reading the trail
Jobs shows what was planned, Runners shows what was launched, and
Events shows everything that failed or quietly degraded along the way.