Documentation · Runners
Security
One virtual machine per job, no inbound connection from the internet or other jobs, cache credentials scoped to one repository, what of your code and builds we see, and who may change a sticky disk.
How one job is kept apart from every other, where a job's network ends, what of your code and builds passes through our hands, and which builds may change the state that outlives a job.
Each job runs in its own virtual machine, which no other job uses and which is destroyed when the job ends. Nothing a job leaves on its machine reaches the next job. The only things that outlast a job are the ones you keep on purpose: entries in the Actions cache, layers in the Docker layer cache, and a sticky disk if you have asked for one.
Each job also gets its own single-use runner, registered on your repository for that one job. Connect your account lists every permission the GitHub App asks for, and what each one is used for.
Inside its machine a job has root, through passwordless sudo, as it does on GitHub's own runners. So what keeps a job apart from other jobs is not inside its machine: the rules that do it are applied outside, where nothing the job does can change them.
Nothing on the internet, and no other job, can open a connection to a running job. Its machine accepts no inbound connection from either. Only the replies to connections the job itself opened come back in.
A job can reach the internet. Package registries, container registries, git remotes and your own APIs need no allowlist on our side.
The caches are reached with a credential issued to the job's machine. It names your GitHub account and repository by their GitHub ids, and it expires. Which repository's entries a job reads and writes is decided by that credential, never by what the workflow asks for, so a job cannot reach another repository's entries, or another account's.
Your code and the secrets your job uses. While a job runs, the code it checks out and the secrets GitHub hands its runner are on a machine we operate, as they are on any runner you do not host yourself. When the job ends, the machine is destroyed.
The job's log. We keep a copy of each job's log, and its page shows it for 90 days, the period GitHub keeps Actions logs for by default. The job's timings and minutes stay after that, because your bill is worked out from them.
What you keep on purpose. Actions cache entries, Docker layers and a sticky disk's contents are stored by us between jobs, and a job's page shows the test results we read from it for the same 90 days as its log. The Actions cache, the Docker layer cache and using sticky disks say how long each is kept.
What the GitHub App reads. The permissions it holds, and what it uses each one for, are listed on Connect your account.
A sticky disk is state that every later build of a repository starts from, so which builds may change it is decided on our side, not by your workflow file. Only a successful build of your default branch, started by a trusted event such as push, can change it. Every other build of the repository changes nothing on it. A build whose workflow run comes from a fork is not given a sticky disk, and nothing a fork's build writes is ever kept. But GitHub hands a runner whichever queued job of your repository matches its label, so a fork's build can land on a runner we prepared for a trusted build, and then it can read that runner's copy of the disk. Protecting a repository closes that: only the trusted builds of its default branch read its disk.
Protecting a sticky disk has the whole of it: the events that count as trusted, the credentials we refuse to keep, the record of every build that had the disk, and how to reset one.
We have not published a third-party audit or certification report. For a security question, or to report a problem you have found, email hello@runners.io, the address on Support.