quickstart →

Local storage (NVMe)

Use local NVMe on EC2 instance-store types for build workspaces, temporary files, and Docker layers on Linux and Windows.

EC2 instance store provides fast, physically attached NVMe storage without a separate EBS volume. RunsOn detects this storage during boot, prepares it, and places write-heavy runner data on it automatically. When the instance exposes more than one local NVMe disk, RunsOn stripes them into one volume for higher aggregate throughput and IOPS.

Use local NVMe for build workspaces, compiler output, temporary files, and Docker layers that only need to live for one runner instance. The storage is included with the selected EC2 instance type; there is no separate EBS storage charge.

Availability#

RunsOn has supported automatic local NVMe setup on Linux for earlier releases. RunsOn v3.2.0 adds the same automatic setup on Windows. Both platforms work with Flex and Fleet. In every case, you must select an EC2 instance type with NVMe instance store.

PlatformRunsOn setupPaths backed by local NVMe
LinuxOne striped ext4 RAID 0 array across all available local NVMe disks/mnt/ephemeral, /home/runner, /var/lib/docker, /var/lib/containerd, and /tmp
WindowsOne NTFS volume; multiple disks use a striped Simple Storage Spaces volumeC:\actions-runner\_work

If you select a type without instance store, local NVMe is not active and the same paths stay on the root EBS volume. Workflows do not need a feature flag or mount step after selecting a compatible type.

On Linux, extras=tmpfs selects RAM-backed storage instead and skips local NVMe setup. Do not enable tmpfs when the job should use the instance-store disks.

Select an instance with local NVMe#

You cannot enable local NVMe on an arbitrary EC2 type. Select an exact instance-store type. For C, M, and R families, restrict selection to the d variant. Do not use a broad family such as m8i* when the job requires local storage; use m8id* instead. Alternatively, select a storage-optimized family such as i7ie, which offers dense local NVMe storage despite having no d in its name. Then check the matched type in the Set up job log. The EC2 instance selector ↗ can filter types by local storage.

Flex

Choose the instance type in the job label:

.github/workflows/nvme.yml
jobs:
linux-build:
runs-on: runs-on=${{ github.run_id }}/family=i7ie.2xlarge/image=ubuntu24-full-x64
steps:
- uses: runs-on/action@v2
- uses: actions/checkout@v7
- run: |
df -h /mnt/ephemeral
df -h "$GITHUB_WORKSPACE"
windows-build:
runs-on: runs-on=${{ github.run_id }}/family=i7ie.2xlarge/image=windows25-full-x64
steps:
- uses: runs-on/action@v2
- uses: actions/checkout@v7
- shell: pwsh
run: |
Get-Volume -FileSystemLabel RunsOnEphemeral
Get-Item $env:GITHUB_WORKSPACE
Fleet

Pin local-storage instance types in the runner catalog:

main.tf
runners = {
linux-nvme = {
family = ["i7ie.2xlarge"]
image = "ubuntu24-full-x64"
}
windows-nvme = {
family = ["i7ie.2xlarge"]
image = "windows25-full-x64"
}
}

Use several compatible instance types when Spot availability matters. Do not mix types with and without instance store unless the job can safely fall back to root EBS storage.

Multi-disk striping#

Some EC2 instance types expose two or more local NVMe disks. RunsOn combines their capacity into one striped volume automatically. Reads and writes can use the devices in parallel, which increases aggregate throughput and IOPS compared with using a single disk.

  • Linux creates an ext4 RAID 0 array across every available local NVMe disk.
  • Windows creates an NTFS volume on a Simple Storage Spaces pool. If the pool cannot be created safely, RunsOn falls back to the first local NVMe disk.

Striping provides performance and combined capacity, not redundancy. Losing one disk invalidates the whole volume. This matches the intended use of local NVMe as temporary build storage, never as the only copy of persistent data.

How RunsOn prepares the disks#

On Linux, RunsOn combines every unclaimed EC2 NVMe instance-store disk into a striped RAID 0 array and mounts it at /mnt/ephemeral. It then redirects the runner home, Docker data, and temporary directory to that array.

Docker data covers both /var/lib/docker and /var/lib/containerd; see Docker images.

On Windows, RunsOn formats one local disk as NTFS. When several local disks are available, it stripes them through a Simple Storage Spaces volume. The volume is mounted directly at C:\actions-runner\_work, without consuming a permanent drive letter.

RunsOn only prepares disks identified as EC2 NVMe instance store. It refuses to clear a boot disk, EBS volume, mounted disk, or disk already claimed by another service. With a custom AMI, disable any image service that mounts local NVMe before the RunsOn agent if you want RunsOn to manage that storage.

Docker images#

Docker keeps images and build layers in /var/lib/docker up to Docker 28, and in containerd’s image store under /var/lib/containerd with Docker 29, which Ubuntu 26.04 images ship. RunsOn moves both directories, empty, to the local NVMe array at every boot. Before v3.5.0, RunsOn left /var/lib/containerd on the root EBS volume, so Ubuntu 26.04 images landed there.

On these runners:

  • Docker starts with an empty image store. Images baked into the AMI, including the few that Ubuntu 26.04 images pre-pull (such as the Dependabot updater), are only available on instance types without local NVMe. Here, Docker pulls them when a job needs them. For Docker Hub images, the pull-through cache (extras=ecr-pull-through) keeps those pulls fast.
  • Images use the local NVMe capacity, not the root volume. If you raised volume to fit Docker images, check that the local disk is large enough instead.
  • Images pulled during a stopped pool’s preinstall do not survive stop/start. Pull them in the job, or rely on a registry cache.
  • Small instance-store types write more slowly than the default root volume. An m8id.xlarge local disk writes about 240 MiB/s, against about 400 MiB/s for the default gp3 root volume, so image pulls and builds can take slightly longer there. Larger sizes and storage-optimized families such as i7i write 800 MiB/s or more.

Data lifetime#

Stopped warm pools also lose local NVMe data across stop/start. RunsOn prepares the empty storage again when the instance resumes.

Do not use local NVMe as a cross-job cache or artifact store. Use sticky disks for a filesystem cache that survives runners, Magic Cache for S3-backed GitHub Actions caches, or EFS for shared Linux files.

See AWS instance store data persistence ↗ for the underlying EC2 lifecycle contract.