# Migrate from Namespace runners without a cold cache

> Migrate from Namespace runners: the profile label map, the nscloud actions to undo, and the cache scope change that makes every new branch start cold.

Source: https://latchkey.dev/learn/runners/migrate-from-namespace  
Updated: 2026-09-21

Teams that migrate from Namespace runners are almost never leaving because of the hardware, which is good, and almost always underestimate the same thing: Namespace is not only a label, it is a set of drop-in actions and a cache that behaves unlike any other cache in this market. The label change takes a minute and the cache change decides whether the migration reads as neutral or as a regression.

Start with what moves and what does not. The label moves: `runs-on` is one line and every destination has a size that matches. The workflow steps mostly move untouched, because they are ordinary steps.

Three things do not move, and each one is a real edit. The `nscloud-` actions have to be swapped back for the standard ones. The cache changes scope, from shared to branch-isolated, which is a behavior change with no error attached. And the remote Docker builders that were on by default stop existing, so a workflow that deleted its buildx setup has to get it back.

## What a runner profile was carrying

Namespace attaches its features to a runner profile rather than to individual steps, which is why the label looks so short and the migration is longer than it looks. A job that reads `runs-on: namespace-profile-default` may be getting a cache volume, a git mirror, a container image cache and a remote Docker builder without any of those appearing in the workflow file.

The undo list below is therefore a list of things to look for in the profile as well as in the YAML. Work through it before you change the label, because after you change it the symptoms are all the same symptom: the job is slower and nothing failed.

| Namespace surface | What it did | Standard replacement | What changes |
| --- | --- | --- | --- |
| `namespace-profile-*` label | Selected the profile and everything attached to it | A size label, for example `latchkey-small` | Features attached to the profile stop being implicit |
| `nscloud-checkout-action` | A faster checkout using a git mirror | `actions/checkout` | Clone time returns to an ordinary shallow clone |
| `nscloud-cache-action` | A cache volume, shared across branches | `actions/cache` with explicit keys | You now write keys, and restores obey branch scope |
| Remote Docker builders, on by default | A warm BuildKit builder beside the runner | `docker/setup-buildx-action` plus cache export | Layer cache is exported and imported per build |
| Unit-minute billing | Charged at max(vCPU, RAM divided by 2) per minute | A fixed per-size rate | Memory stops costing extra when cores sit idle |

> Namespace surfaces read from its GitHub Actions documentation on 2026-09-21. The unit-minute model, its platform multipliers and the prepaid against overage split are worked through separately in [Namespace alternatives](/learn/runners/namespace-alternatives); this page assumes you have already decided to leave and is only about the cutover.

## The cache is the migration

This is the change most likely to make a migrated pipeline slower with nothing in the log to explain it. Namespace documents that its cache action does not support manual partitioning: entries are shared across everything using that runner profile, which includes the default branch and every feature branch at once. `actions/cache` is the opposite by design, restorable only from the branch that wrote the entry or from the default branch.

So a workflow that never thought about cache keys was quietly getting cross-branch hits, and on the other side it will not. The fix is not exotic, it is a prefix key and a `restore-keys` fallback, but it has to be written before the first run rather than discovered from a timing graph a week later. Write the keys so a new branch falls back to the default branch entry, and accept that the first run on a new dependency set is cold.

```.github/workflows/ci.yml
- uses: actions/cache@v4
        with:
          path: ~/.npm
          key: npm-${{ runner.os }}-${{ hashFiles('**/package-lock.json') }}
          restore-keys: |
            npm-${{ runner.os }}-
            npm-
```

> The two `restore-keys` prefixes are the whole point. Without them a branch with a changed lockfile gets no entry at all rather than a near miss, which on Namespace it would have got from the shared volume. Size matters too: GitHub gives 10 GB per repository with 7-day eviction, and Latchkey 25 GB per repository, so anything larger than your destination allowance has to stop being cached deliberately rather than by eviction.

## Undo the nscloud actions

Two actions account for most of what a Namespace workflow looks like, and both have a plain equivalent. The checkout action used a git mirror to make cloning cheaper; `actions/checkout` at its default depth of one is the replacement, and on a large repository the difference is a few seconds rather than a minute. The cache action becomes `actions/cache` with the keys from the section above.

Grep for the prefix rather than for the two names, because the surface is wider than most people remember: entries such as a container image cache, a tool cache directory, a cache size hint and a flag that disables the remote builders all share it. Anything matching `nscloud-` is either an action to replace or a setting whose effect is about to disappear.

```.github/workflows/ci.yml
jobs:
  test:
    # before
    # runs-on: namespace-profile-default
    # after
    runs-on: latchkey-small
    steps:
      # - uses: namespacelabs/nscloud-checkout-action@v8
      - uses: actions/checkout@v5
      # - uses: namespacelabs/nscloud-cache-action@v1
      #   with:
      #     cache: npm
      - uses: actions/cache@v4
        with:
          path: ~/.npm
          key: npm-${{ hashFiles('**/package-lock.json') }}
          restore-keys: npm-
      - run: npm ci && npm test
```

## Docker builds lose a warm builder

Namespace runs remote Docker builders and has them on by default, which means a workflow may have deleted its `docker/setup-buildx-action` step because it stopped being necessary. That step has to come back, and with it the cache export and import that a warm builder made unnecessary.

Expect the first few builds to be slower than the last few on Namespace were, and expect the ordinary Docker failure modes to reappear along with them: a rate limit once every job pulls base images again rather than finding them warm, or a cache backend that refuses a reservation. If image builds are most of your minutes, price a destination that sells a real build cache rather than assuming the layer cache covers it.

## What you are giving up, stated plainly

Three properties have no equivalent on a Linux-x86_64-only destination, and one of them is usually why the team chose Namespace. It publishes the widest platform range in this market: Linux AMD64 and arm64, Windows, macOS on Apple Silicon, and Linux running on Apple Silicon at a seven times multiplier. It sells the smallest machine anybody sells, 1 vCPU with 2 GB, where every fixed-size vendor floors at two cores.

It also publishes a concurrency ceiling you can plan against, 32, 64 or 160 Linux vCPUs by plan, and its Business plan allows jobs up to 24 hours where a GitHub-hosted job caps at six. If any of those is load-bearing for you, this is a partial migration at best, and the right shape is to move the plain Linux x64 jobs and leave the rest.

## The pre-flight checklist

Six items. Two greps, two decisions and two things to write before the first run rather than after it.

- **Labels.** Grep for `runs-on:.*namespace-` and list every job, including any on Windows, macOS or Apple Silicon that cannot move at all.
- **Actions.** Grep for `nscloud-`. Every hit is either an action to swap or a profile feature whose effect is about to go away silently.
- **Cache keys.** Write them, with `restore-keys` prefixes, before the cutover. This is the item that decides whether the migration feels neutral.
- **Cache size.** Compare what you are caching against the destination allowance, 10 GB per repository on GitHub-hosted and 25 GB on Latchkey, and decide what stops being cached.
- **Docker.** Grep for `docker/build-push-action` and check whether a buildx setup step was removed. Put it back with `cache-from` and `cache-to` before the first image build.
- **Concurrency.** Namespace publishes a vCPU ceiling by plan and most destinations publish a job count instead. A wide matrix that fitted one may queue on the other.

## What no run backs, and why

Nothing on this page was reproduced on a runner, and the reason is specific to it: the two behaviors that matter most, a cache shared across branches and a cache scoped to one, only differ on the second run of a second branch. A single job proves nothing, and a harness that ran the same workflow twice on two vendors would be measuring their hardware, not their cache semantics.

So the cache behavior here is quoted from the two vendors' own documentation rather than timed, and the label and action names are read from the Namespace GitHub Actions docs on 2026-09-21. The numbers you should trust least are any about how much slower your first post-migration run will be, which is why this page does not state one.

## FAQ

### What replaces nscloud-cache-action when I leave Namespace?

`actions/cache` with keys you write yourself, plus `restore-keys` prefixes. The behavior is not equivalent: Namespace documents that its cache is shared across everything using a runner profile and does not support manual partitioning, while `actions/cache` restores only from the writing branch or the default branch. Without prefix fallbacks, every new feature branch starts cold.

### Why is my pipeline slower after moving off Namespace?

Three usual causes, none of which logs an error. The cache changed scope, so cross-branch hits stopped. The remote Docker builders that were on by default went away, so layer cache is exported and imported per build again. And `nscloud-checkout-action` was replaced by an ordinary clone, which is slower on a large repository. Check them in that order.

### Can I move all of my Namespace jobs to one destination?

Only if they are all plain Linux x86_64. Namespace publishes Linux AMD64 and arm64, Windows, macOS on Apple Silicon and Linux on Apple Silicon, and most cheap managed vendors are Linux x86_64 only. It also sells a 1 vCPU with 2 GB shape that every fixed-size vendor floors above. Expect a split rather than a switch if any of those is in use.

### What do I lose in job length and concurrency?

Namespace publishes a concurrency ceiling in vCPUs by plan, 32, 64 or 160 for Linux, which is a number you can plan capacity against, and its Business plan allows jobs up to 24 hours. Destinations usually publish a concurrent job count instead, and a GitHub-hosted job is capped at six hours. A wide matrix or a long job may behave differently for reasons that have nothing to do with speed.

## References

- [Namespace GitHub Actions docs: runner profiles, cache volumes and remote builders (verified 2026-09-21)](https://namespace.so/docs/features/faster-github-actions)
- [Namespace pricing: unit minutes, plan pools, concurrency ceilings and rounding (verified 2026-09-21)](https://namespace.so/pricing)
- [GitHub Docs: dependency caching reference, branch scope, size limit and eviction (verified 2026-09-21)](https://docs.github.com/en/actions/reference/dependency-caching-reference)
- [actions/checkout: the default fetch depth and what a shallow clone costs (verified 2026-09-21)](https://github.com/actions/checkout)

---

Latchkey runs CI/CD that repairs its own failures. Agent entry points: https://latchkey.dev/agent.txt, https://latchkey.dev/openapi.json, https://latchkey.dev/llms.txt
