How Long Do Runner Snapshots Last?

Runner snapshots are deleted after 15 days, so an alias no workflow refreshes stops resolving and the next job that asks for it boots from the base image.

Runner snapshots are temporary and are deleted after 15 days, per the snapshot runners documentation. An alias that no workflow rewrites inside that window stops resolving, and the next job that requests it boots from the base image and runs normally, so an expiry costs a slow job rather than a failed one.

Answer

The 15 day figure applies per snapshot, counted from when WarpBuilds/snapshot-save created it. Booting a job from a snapshot does not reset that clock. The only thing that keeps an alias alive is another save writing a new snapshot for the same alias string, which is why the lifetime question is really a question about how often your workflow saves.

That gives a short timeline for any alias whose producing workflow stops running.

Time since the last saveState of the aliasWhat a job with snapshot.key=<alias> does
Day 0 to day 14Snapshot existsBoots from the snapshot, WARPBUILD_SNAPSHOT_KEY is set
Day 15Snapshot deletedBoots from the base image, no error and no warning
After day 15No snapshot for the aliasSame as a first run: base image, full setup, job succeeds

The last row is the behavior that surprises people. Requesting an alias that has never existed and requesting one that expired produce the same result: the runner boots from the base image and the job proceeds, which is the documented behavior of snapshot.key in the snapshot runners documentation. There is no failure signal to alert on, so a repository can spend weeks paying cold boot time before anyone notices that the alias went stale.

The detectable signal is the environment variable. A runner created from a snapshot carries WARPBUILD_SNAPSHOT_KEY set to the alias it booted from, per the same documentation, and a runner that fell back to the base image does not. A single step that reads it turns a silent expiry into a visible one.

- name: Report snapshot state
  run: |
    if [ -z "$WARPBUILD_SNAPSHOT_KEY" ]; then
      echo "::warning::cold boot, no snapshot for this alias"
    else
      echo "restored from $WARPBUILD_SNAPSHOT_KEY"
    fi

Snapshot runners are supported only on WarpBuild Cloud Ubuntu runners. A snapshot label on a Windows, macOS, or BYOC runner is silently ignored, so on those runner types there is no alias to expire in the first place. The Ubuntu labels that accept snapshot labels are listed in the cloud runners documentation.

Detail

What an expiry costs on each runner size

An expiry is a wall-clock cost first. Take a job with 7 minutes of environment setup that a warm snapshot skips. On a miss, the job pays those 7 minutes back and skips the snapshot boot penalty, which the snapshot runners documentation puts at 45 to 60 seconds, so the run lands about 6 minutes longer than a warm one.

The dollar effect runs the other way on small runners, because a miss also skips the $0.04 snapshot restore fee from the pricing page, checked on 2026-08-13. Per-minute rates below come from the same page, and the delta column is (7 - 1) x rate - $0.04.

Runner labelPer minuteAdded minutes on a missDollar delta per job
warp-ubuntu-latest-x64-2x$0.0046-$0.016
warp-ubuntu-latest-x64-4x$0.0086$0.008
warp-ubuntu-latest-x64-8x$0.0166$0.056
warp-ubuntu-latest-x64-16x$0.0326$0.152
warp-ubuntu-latest-x64-32x$0.0646$0.344

On a 2 vCPU runner the expired alias is marginally cheaper per job, which matches the break-even arithmetic for that size. On a 32 vCPU runner the same expiry adds $0.344 per job and 6 minutes of pull request latency, and 500 jobs a month turns that into $172.00 and roughly 50 hours of added job time. Storage stops billing when the snapshot is deleted, which is covered in how snapshot storage costs are billed.

Refreshing the alias before it expires

The reliable pattern is a job on the default branch that boots clean and republishes the snapshot. Merges keep it fresh in active repositories, and a schedule covers the quiet weeks. A weekly cron gives two refresh attempts inside every 15 day lifetime, so one missed run does not expire the alias.

name: refresh-snapshot
on:
  schedule:
    - cron: "17 6 * * 1"
  workflow_dispatch:

jobs:
  refresh:
    runs-on: warp-ubuntu-latest-x64-4x;snapshot.enabled=true
    steps:
      - uses: actions/checkout@v5

      - name: Install system packages
        run: |
          sudo apt-get update
          sudo apt-get install -y libvips-dev protobuf-compiler

      - name: Install dependencies
        run: npm ci

      - name: Warm build caches
        run: npm run build

      - name: Cleanup credentials
        run: |
          rm -rf $HOME/.ssh $HOME/.aws
          git clean -ffdx

      - name: Save snapshot
        uses: WarpBuilds/snapshot-save@v1
        with:
          alias: "web-app-main"
          fail-on-error: true
          wait-timeout-minutes: 60

Three details in that file carry weight. The job uses snapshot.enabled=true rather than snapshot.key, so the refresh always starts from the base image and the published snapshot never accumulates drift from a chain of restores. The cron minute is 17 rather than 0, because GitHub documents that the schedule event can be delayed during periods of high load and recommends scheduling away from the top of the hour (GitHub Actions events reference). And workflow_dispatch sits alongside the schedule so anyone can rebuild the alias by hand after a base image change.

One GitHub behavior can silently end the refresh: in a public repository, scheduled workflows are disabled automatically when no repository activity has occurred in 60 days, per the same reference. A repository quiet enough to need the cron is exactly the repository where GitHub may switch it off, so treat a public repository schedule as a backstop and keep the merge-triggered save as the primary refresh.

Feature branch aliases and the cleanup step

The feature branch example in the snapshot runners documentation uses one alias per project, refreshed by every run, with the cleanup step immediately before the save. Every push boots from the previous snapshot for that alias and writes a new one, so the alias never approaches its 15 day lifetime while the branch is active, and the live snapshot count stays at one.

jobs:
  build:
    runs-on: warp-ubuntu-latest-x64-2x;snapshot.key=my-project-snapshot
    steps:
      - uses: actions/checkout@v5

      - name: Install dependencies
        run: npm ci

      - name: Cleanup credentials
        run: |
          rm -rf $HOME/.ssh $HOME/.aws
          git clean -ffdx

      - name: Save snapshot
        uses: WarpBuilds/snapshot-save@v1
        with:
          alias: "my-project-snapshot"
          fail-on-error: true
          wait-timeout-minutes: 60

The cleanup step matters more on this pattern than on any other, because a snapshot that gets rewritten on every branch push carries whatever the last run left on disk. rm -rf $HOME/.ssh $HOME/.aws removes the credential locations, and git clean -ffdx removes untracked files from the working tree, including files ignored by .gitignore. On a public repository any contributor can read the alias out of the workflow file and boot a job from it, so credentials must be gone before the save runs.

Aliases interpolated from a branch name, such as snapshot.key=deps-${{ github.head_ref }}, behave differently on expiry. Each merged branch leaves a snapshot that no workflow ever refreshes again, and the 15 day lifetime is what eventually clears it. Retiring one before then is covered in how to invalidate a runner snapshot, and the rollout patterns are on the snapshot runners page.

What happens when a snapshot alias expires?

Nothing fails. A job whose runs-on carries snapshot.key=<alias> for an expired alias boots from the base image and runs its setup steps again, exactly as it would on the first run before any snapshot existed. The only symptoms are the cold job duration and an unset WARPBUILD_SNAPSHOT_KEY environment variable. The snapshot runners documentation is the reference for that fallback behavior.

How often should a workflow rewrite its snapshot?

Often enough that the gap between two saves stays under 15 days. A save on every merge to the default branch covers active repositories on its own. Repositories that go quiet for a week or two need a scheduled run, and a weekly cron gives two chances to refresh before the 15 day lifetime ends. The snapshot runners page walks through which workloads justify keeping an alias alive at all.

Does a snapshot expire 15 days after creation or 15 days after its last use?

After creation. Restoring from a snapshot does not extend its life, so a busy alias still ages out on schedule unless a workflow calls WarpBuilds/snapshot-save again and writes a new snapshot for that alias. Because storage bills for every hour a snapshot exists, the lifetime also caps what an abandoned alias can cost, as how snapshot storage costs are billed sets out against the rates on the pricing page.

Start with $10 in free credits

Change the runner label in your workflow and keep the rest of your GitHub Actions setup. Runner time is billed per minute.