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.
| Platform | RunsOn setup | Paths backed by local NVMe |
|---|---|---|
| Linux | One striped ext4 RAID 0 array across all available local NVMe disks | /mnt/ephemeral, /home/runner, /var/lib/docker, /var/lib/containerd, and /tmp |
| Windows | One NTFS volume; multiple disks use a striped Simple Storage Spaces volume | C:\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:
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_WORKSPACEFleet
Pin local-storage instance types in the runner catalog:
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
volumeto fit Docker images, check that the local disk is large enough instead. - Images pulled during a stopped pool’s
preinstalldo 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
i7iwrite 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.