quickstart →

Using tmpfs for faster builds

How to use the tmpfs feature with RunsOn, for faster builds

Since v2.8.2, you can configure RunsOn to automatically create a tmpfs volume, which will then be used to host the following directories:

  • /tmp (overlay)
  • /home/runner (overlay)
  • /var/lib/docker (bind mount)
  • /var/lib/containerd (bind mount, since v3.5.0)

Since v3.5.0, tmpfs runners of any size get compressed swap in memory, so rarely used tmpfs pages are compressed, and the tmpfs volume can grow to 150% of the runner’s memory. Without that swap (for example on a custom image that configures its own), the volume is capped at 100% of memory. If a job still fails for an unknown reason, increase your runner size (large-memory instance types such as r8* are highly recommended); the job gets an out-of-memory warning when it needs a bigger runner.

Docker images also use this memory. Ubuntu 26.04 images ship Docker 29, whose containerd image store keeps each downloaded layer next to its unpacked copy. A pulled image therefore takes about its compressed size plus its unpacked size. Docker also starts with an empty image store, so images baked into the AMI are not available on tmpfs runners.

Availability#

extras=tmpfs works on Linux runners only. It is available with both Flex and Fleet. Windows runners do not support this capability.

Watch tmpfs pressure#

When tmpfs is enabled, v3.2 adds a Tmpfs Memory Utilization (%) chart to the Complete runner step. It shows how much of /mnt/tmpfs the job used, separately from overall host-memory utilization. The chart is part of the standard inline metrics and needs no OpenTelemetry setup.

Usage#

Flex

Enable tmpfs from the job label and choose a runner with enough memory for the job’s writes:

.github/workflows/ci.yml
jobs:
tmpfs:
runs-on: runs-on=${{ github.run_id }}/family=r8/ram=16/extras=tmpfs
steps:
- uses: runs-on/action@v2
- run: df -ah /mnt/tmpfs
- run: df -ah /home/runner
- run: df -ah /tmp
- run: df -ah /var/lib/docker
- run: df -ah /var/lib/containerd
Fleet

For Fleet, publish a runner fleet with tmpfs enabled in Terraform:

main.tf
runners = {
linux-tmpfs = {
cpu = 8
ram = 16
family = ["r8g"]
image = "ubuntu24-full-arm64"
extras = ["tmpfs"]
}
}

Use cases#

Using tmpfs is a good fit for:

  1. Speeding up I/O intensive workflows, since any writes to the directories listed above are done in memory, and are much faster than writing to disk.

To see how tmpfs compares with EBS gp3 and local NVMe on the same instances, down to fio numbers and real build times, see EC2 storage for GitHub Actions.

Accessing tmpfs#

If you include tmpfs in the extras parameter of your workflow job, a tmpfs volume will be created in the /mnt/tmpfs directory, and the directories specified above will automatically be bind-mounted or overlay-mounted into it.

Using tmpfs in your workflow#

.github/workflows/ci.yml
jobs:
efs:
runs-on: runs-on=${{ github.run_id }}/family=r8/ram=16/extras=tmpfs
steps:
- uses: runs-on/action@v2
- run: df -ah /mnt/tmpfs
# tmpfs 16G 724K 16G 1% /mnt/tmpfs
- run: df -ah /home/runner
# overlay 16G 724K 16G 1% /home/runner
- run: df -ah /tmp
# overlay 16G 724K 16G 1% /tmp
- run: df -ah /var/lib/docker
# tmpfs 16G 724K 16G 1% /var/lib/docker
- run: df -ah /var/lib/containerd
# tmpfs 16G 724K 16G 1% /var/lib/containerd

Considerations#

Again, this is a kind of YOLO mode (but with potentially great rewards), so make sure your are correctly right-sizing your runners.

Volatility (stopped/warm pools)#

tmpfs is volatile. Because /var/lib/docker and /var/lib/containerd are bind-mounted onto the tmpfs volume, all Docker state (images, containers, volumes) is lost on every stop/start. For stopped pools, warm boots skip host setup to keep resume latency low, so any container started during preinstall on the first boot will not exist on the next boot. Recreate required containers via prerun or job hooks.