> ## Documentation Index
> Fetch the complete documentation index at: https://namespace.so/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# GitHub Actions Runner Configuration

export const CenteredImage = ({src, alt, width, caption, className}) => {
  const [basePath, setBasePath] = useState("");
  useEffect(() => {
    const path = window.location.pathname;
    setBasePath(path === "/docs" || path.startsWith("/docs/") ? "/docs" : "");
  }, []);
  return <Frame caption={caption} className={className} style={{
    maxWidth: width,
    marginInline: "auto"
  }}>
			<OptimizedImage src={`${basePath}${src}`} alt={alt} />
		</Frame>;
};

## Runner Labels

As an alternative to using profiles, you can keep the entire runner configuration version-controlled within your workflow file.
This works by specifying one or more labels in the `runs-on` configuration of your workflow.

### Machine Label

The most important label is the one selecting architecture, operating system, and machine shape.

The label looks like `nscloud-{os}-{arch}-{shape}`

**Note:** only one `nscloud` label is allowed in the
`runs-on` field of your workflow file. Namespace will not schedule any
workflow job if `runs-on`
specifies more than one `nscloud` label or invalid ones.

**Supported machine labels per OS**

<div className="runner-labels-table">
  |                                                                                                                                                       | Ubuntu                                                                     | Windows                                                | macOS                                                                                                                                                               |
  | ----------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------- | ------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
  | **OS**                                                                                                                                                | `ubuntu-20.04`<br />`ubuntu-22.04`<br />`ubuntu-24.04`<br />`ubuntu-26.04` | `windows-2022`                                         | `macos-sonoma`<br />`macos-sequoia-stable`<br />`macos-sequoia`<br />`macos-tahoe`<br />`macos-tahoe-latest`<br />`macos-tahoe-slim`<br />`macos-tahoe-slim-latest` |
  | **Arch**                                                                                                                                              | `amd64`<br />`arm64`                                                       | `amd64`                                                | `arm64`                                                                                                                                                             |
  | **Shape**<p>These are our standard shapes. Larger and odd-sized shapes are available; see [Machine Shapes](/docs/architecture/compute/machine-shapes).</p> | `2x4`<br />`4x8`<br />`8x16`<br />`16x32`<br />`32x64`                     | `2x4`<br />`4x8`<br />`8x16`<br />`16x32`<br />`32x64` | `6x14`<br />`12x28`<br />`12x56`                                                                                                                                    |
</div>

Examples:

| Label                                 | OS                  | Architecture | vCPU   | Memory |
| ------------------------------------- | ------------------- | ------------ | ------ | ------ |
| `nscloud-ubuntu-22.04`                | Ubuntu 22.04        | AMD 64-bit   | 4 vCPU | 16 GB  |
| `nscloud-ubuntu-22.04-amd64`          | Ubuntu 22.04        | AMD 64-bit   | 4 vCPU | 16 GB  |
| `nscloud-ubuntu-26.04-amd64-4x8`      | Ubuntu 26.04        | AMD 64-bit   | 4 vCPU | 8 GB   |
| `nscloud-ubuntu-24.04-amd64-4x8`      | Ubuntu 24.04        | AMD 64-bit   | 4 vCPU | 8 GB   |
| `nscloud-ubuntu-22.04-amd64-4x8`      | Ubuntu 22.04        | AMD 64-bit   | 4 vCPU | 8 GB   |
| `nscloud-ubuntu-20.04-amd64-4x8`      | Ubuntu 20.04        | AMD 64-bit   | 4 vCPU | 8 GB   |
|                                       |                     |              |        |        |
| `nscloud-ubuntu-22.04-arm64`          | Ubuntu 22.04        | ARM 64-bit   | 4 vCPU | 8 GB   |
| `nscloud-ubuntu-26.04-arm64-4x8`      | Ubuntu 26.04        | ARM 64-bit   | 4 vCPU | 8 GB   |
| `nscloud-ubuntu-24.04-arm64-4x8`      | Ubuntu 24.04        | ARM 64-bit   | 4 vCPU | 8 GB   |
| `nscloud-ubuntu-22.04-arm64-4x8`      | Ubuntu 22.04        | ARM 64-bit   | 4 vCPU | 8 GB   |
| `nscloud-ubuntu-20.04-arm64-4x8`      | Ubuntu 20.04        | ARM 64-bit   | 4 vCPU | 8 GB   |
|                                       |                     |              |        |        |
| `nscloud-windows-2022-amd64-4x8`      | Windows Server 2022 | AMD 64-bit   | 4 vCPU | 8 GB   |
| `nscloud-windows-2022-amd64-8x16`     | Windows Server 2022 | AMD 64-bit   | 8 vCPU | 16 GB  |
|                                       |                     |              |        |        |
| `nscloud-macos-sonoma-arm64-6x14`     | macOS Sonoma        | ARM 64-bit   | 6 vCPU | 14 GB  |
| `nscloud-macos-sequoia-arm64-6x14`    | macOS Sequoia       | ARM 64-bit   | 6 vCPU | 14 GB  |
| `nscloud-macos-tahoe-arm64-6x14`      | macOS Tahoe         | ARM 64-bit   | 6 vCPU | 14 GB  |
| `nscloud-macos-goldengate-arm64-6x14` | macOS Golden Gate   | ARM 64-bit   | 6 vCPU | 14 GB  |

<h3 id="label-suffixes">
  Machine label suffixes
</h3>

A machine label can be extended with one or more `-with-*` suffixes to enable extra capabilities on the runner,
without changing its shape:

`nscloud-{os}-{arch}-{shape}[-with-cache][-with-features][-with-builders]`

For example, `nscloud-ubuntu-24.04-arm64-32x64-with-cache` selects an Ubuntu 24.04, arm64, 32x64 machine with a cache
volume attached. Suffixes can also be chained to combine capabilities on a single runner, as shown below.

| Suffix           | Effect                                                                                                                                |
| ---------------- | ------------------------------------------------------------------------------------------------------------------------------------- |
| `-with-cache`    | Attach a [cache volume](#using-a-cache-volume) to the runner.                                                                         |
| `-with-features` | Enable the settings passed in the [namespace-features:](#configuring-features) label.                                                 |
| `-with-builders` | Apply [Remote Builder](#namespace-remote-builders) configuration such as `nscloud-no-remote-builders` or `nscloud-in-runner-builder`. |

#### Combining cache and features

To request more than one capability on a single runner, chain the suffixes on the machine label. Order does not matter, and any companion labels are
listed as separate entries. For example, a runner that has a cache volume and also runs privileged:

```yaml theme={null}
runs-on:
  - nscloud-ubuntu-22.04-amd64-8x16-with-cache-with-features
  - nscloud-cache-tag-my-repo
  - namespace-features:container.privileged=true
```

Here `-with-cache` attaches the cache volume (sized and tagged by the
`nscloud-cache-*` companion labels) and `-with-features` enables the settings
passed in the `namespace-features:` label.

<h2 id="configuring-features">
  Configuring Features and Overrides
</h2>

Beyond selecting a machine shape or profile, you can pass additional feature settings and overrides to a
runner, for example `container.privileged`,
`github.run-id`, or [`tailscale.spec`](/docs/integrations/tailscale).
How you pass them depends on whether you use runner labels or a runner profile.

<Tabs>
  <Tab title="With runner profiles">
    The `namespace-features:` label cannot be used with profiles. Instead,
    append one or more `;key=value` pairs directly to the profile name:

    ```yaml theme={null}
    runs-on: namespace-profile-my-profile;container.privileged=true;container.host-pid-namespace=true
    ```
  </Tab>

  <Tab title="With runner labels">
    Pass the settings as a separate `namespace-features:` label (multiple
    values are separated by `;`), and append the
    `-with-features` suffix to your machine label:

    ```yaml theme={null}
    runs-on:
      - nscloud-ubuntu-22.04-amd64-4x8-with-features
      - namespace-features:container.privileged=true;container.host-pid-namespace=true
    ```

    The `-with-features` suffix matters because GitHub assigns a job to any
    runner whose labels are a superset of the job's labels. Keeping the suffix on the machine label prevents
    jobs that don't request these features from being scheduled onto a feature-enabled runner.
  </Tab>
</Tabs>

## Cache Volumes using Runner Labels

### Using a Cache Volume

<Tabs>
  <Tab title="With runner profiles">
    If you configured a runner profile using the web UI and use `namespace-profile-name` runner label, enable caching by simply going to the [runner profile](https://cloud.namespace.so/workspace/actions/profiles) and enabling caching.

    <CenteredImage width={500} alt="runner profile cache configuration" src="/docs/images/github-actions/runnerprofilecaching.png" />

    Cache volumes also enable caching for built-in software (Git, containerd, toolchain).

    You can also force a custom cache tag. This allows sharing a cache from multiple profiles or repositories:

    ```
    runs-on: namespace-profile-my-profile;overrides.cache-tag=new-tag-value
    ```
  </Tab>

  <Tab title="With runner labels">
    You can configure cache volumes by adding corresponding `runs-on` labels. Use the following required labels:

    * Append `-with-cache` to your [Machine Label](#machine-label), for example:
      `nscloud-ubuntu-22.04-amd64-8x16-with-cache`
    * `nscloud-cache-tag-{tag}`, where tag is the key to select the volume to mount.
      Namespace infrastructure will attempt to attach the most recently used volume with this tag. Valid tags may contain lowercase letters, numbers, `-` and `.` characters (`[a-z0-9.-]+`).

    Setting these labels will make the cache volume available to the runner at path `/cache`. This location is static and not configurable at the moment.

    By default the cache volume will be 20 GB. To configure a custom size for your cache volume, specify `nscloud-cache-size-{size}gb`. The maximum size depends on your subscription plan: The team plan includes 50 GB, and the Business plan offers 100 GB.

    See example below for a workflow configured to use a Cache Volume.
  </Tab>
</Tabs>

#### Example: Caching NPM Packages

Use our [`nscloud-cache-action`](/docs/reference/github-actions/nscloud-cache-action) to mount the volume
under the paths you want to cache.

<Tabs>
  <Tab title="With runner profiles">
    ```yaml {4,7-13} theme={null}
    jobs:
      tests:
        runs-on:
          - namespace-profile-node-tests

        steps:
          - name: Setup pnpm
            uses: pnpm/action-setup@v4

          - name: Setup PNPM cache
            uses: namespacelabs/nscloud-cache-action@v1
            with:
              cache: pnpm

          - name: Install dependencies and run tests
            run: |
              pnpm install
              pnpm run test
    ```
  </Tab>

  <Tab title="With runner labels">
    ```yaml {4-6,9-15} theme={null}
    jobs:
      tests:
        runs-on:
          - nscloud-ubuntu-22.04-amd64-8x16-with-cache
          - nscloud-cache-size-20gb
          - nscloud-cache-tag-my-repository-e2e-tests

        steps:
          - name: Setup pnpm
            uses: pnpm/action-setup@v4

          - name: Setup PNPM cache
            uses: namespacelabs/nscloud-cache-action@v1
            with:
              cache: pnpm

          - name: Install dependencies and run tests
            run: |
              pnpm install
              pnpm run test
    ```
  </Tab>
</Tabs>

If you run GitHub jobs in a container, you also need to mount the cache volume inside the
container. See [Running jobs in Containers](#jobs-in-containers)

### Caching Docker Images Across Invocations

Namespace also makes it trivial to cache container image pulls
(and unpacks, often the most expensive bit) across invocations.

<Tabs>
  <Tab title="With runner profiles">
    To enable this feature, just open the runner profile configuration, add a cache volume and enable `Container images`.

    ```yaml {4} theme={null}
    jobs:
      tests:
        runs-on:
          - namespace-profile-integration-tests

        steps:
          - name: Pull ubuntu image
            run: |
              time docker pull ubuntu
    ```
  </Tab>

  <Tab title="With runner labels">
    If using runner labels, configuring the cache volume with the [required set of labels](#using-a-cache-volume) and then add the label `nscloud-container-image-cache`.

    For example, the following workflow definition asks for a cache volume of 50 GB with tag `e2e-tests`. Then, it lets Namespace automatically use it to cache the Docker ubuntu image.

    ```yaml {7} theme={null}
    jobs:
      tests:
        runs-on:
          - nscloud-ubuntu-22.04-amd64-8x16-with-cache
          - nscloud-cache-tag-e2e-tests
          - nscloud-cache-size-50gb
          - nscloud-container-image-cache

        steps:
          - name: Pull ubuntu image
            run: |
              time docker pull ubuntu
    ```
  </Tab>
</Tabs>

The second time the above example runs, the time to pull the ubuntu container image
should be close to 0, as every layer was already cached by the first run.

#### Known incompatibilities for Docker Image caching

Docker image caching relies on standard Docker APIs.
However, if you are using a tool that does not fully support Docker APIs, Docker Image caching can break your workflow.
Known issues are:

* Spring Boot [does not provide full image spec compatibility](https://github.com/spring-projects/spring-boot/issues/40100) in their Buildpacks plugin

### Caching GitHub Tools

Namespace volumes can cache GitHub tools across invocations.

For example, `actions/setup-go` and `actions/setup-python`
actions first look for their binaries in

`$RUNNER_TOOL_CACHE` directory, before fetching
from remote.

<Tabs>
  <Tab title="With runner profiles">
    To make GitHub tool cache use Namespace volumes, open the runner profile configuration, add a cache volume and check `Toolchain downloads`.

    ```yaml {4,7-8} theme={null}
    jobs:
      tests:
        runs-on:
          - namespace-profile-go-python-tests

        steps:
          - uses: actions/setup-go@v6
          - uses: actions/setup-python@v6
    ```
  </Tab>

  <Tab title="With runner labels">
    If using runner labels, add the label `nscloud-runner-tool-cache-{size}gb`. Where `{size}` is an integer that indicates how large the cache volume storing the GitHub tools should be.

    ```yaml {5,8-9} theme={null}
    jobs:
      tests:
        runs-on:
          - nscloud-ubuntu-22.04-amd64-8x16-with-cache
          - nscloud-runner-tool-cache-20gb

        steps:
          - uses: actions/setup-go@v6
          - uses: actions/setup-python@v6
    ```
  </Tab>
</Tabs>

### Caching Git Repositories

With cache volumes, you can set your GitHub workflow to cache large git repositories to speed up the checkout phase.

After you enabled the Git caching (see below how), you'll need to change your workflows to call our optimized checkout action,
which will make use of the cache volume to store and retrieve the git mirrors. See the
[`nscloud-checkout-action`](/docs/reference/github-actions/nscloud-checkout-action)
page for more details.

<Tabs>
  <Tab title="With runner profiles">
    To enable this feature, just open the runner profile configuration, add a cache volume and check `Git repository checkouts`.

    ```yaml {4,7} theme={null}
    jobs:
      tests:
        runs-on:
          - namespace-profile-integration-tests

        steps:
          - uses: namespacelabs/nscloud-checkout-action@v8
            name: Checkout
            with:
              path: my-repo
          - run: |
              cd my-repo && git status
    ```
  </Tab>

  <Tab title="With runner labels">
    If using runner labels, add the runner label `nscloud-git-mirror-{size}gb`. Where `{size}` is an integer that indicates how large the cache volume storing the GitHub tools should be.

    To ensure cache isolation across GitHub repositories, Namespace automatically creates a cache volume for each GitHub repository where this label is in use. The cache volume tag follows the schema: `github-git-mirror-{owner}-{repository}`.

    For example, the following workflow definition asks for a git mirrors cache volume of 5 GB, and then uses the Namespace's checkout action to clone the workflow's repository into `my-repo` directory.

    ```yaml {5,8} theme={null}
    jobs:
      tests:
        runs-on:
          - nscloud-ubuntu-22.04-amd64-8x16-with-cache
          - nscloud-git-mirror-5gb

        steps:
          - uses: namespacelabs/nscloud-checkout-action@v8
            name: Checkout
            with:
              path: my-repo
          - run: |
              cd my-repo && git status
    ```
  </Tab>
</Tabs>

#### Expected Use Cases

Note that the `namespacelabs/nscloud-checkout-action` is optimized to speed up only specific use-cases.
If your workflows do not belong to these, you might not see the expected performance improvement.

* Very large repositories: when the workflow needs to check out a repository with many or big files.
* Checkout long commits history: when the workflow needs to check out many commits (the default is only 1).
* Repositories with a large set of submodules.
* Repositories with Git LFS objects.

### Custom Cache Identity

For scenarios requiring full control over the cache isolation boundary, you can specify custom cache tags.

<Tabs>
  <Tab title="With runner profiles">
    To specify a custom cache tag when using a profile, append `;overrides.cache-tag=new-tag-value` to the profile name:

    ```
    runs-on: namespace-profile-my-profile;overrides.cache-tag=new-tag-value
    ```
  </Tab>

  <Tab title="With runner labels">
    If using runner labels, add a label `nscloud-cache-tag-{tag-name}`

    ```yaml theme={null}
    runs-on:
      - nscloud-ubuntu-22.04-amd64-4x16-with-cache
      - nscloud-cache-tag-my-custom-cache
      - nscloud-cache-size-100gb
    ```
  </Tab>
</Tabs>

### Advanced: Protect Caches from Updates

You can configure Cache Volumes to limit what git branches can perform updates to them.
This configuration allows you to use the same cache as the main branch from other branches (e.g. pull requests), but not commit changes to it.

<Tabs>
  <Tab title="With runner profiles">
    To specify which branches can update the cache volume, open the cache volume configuration, then check `Show Advanced features`, and finally type the branch names.

    <CenteredImage width={500} alt="branch cache configuration" src="/docs/images/github-actions/branch-cache-protection.png" />
  </Tab>

  <Tab title="With runner labels">
    If using runner labels, add one or more labels `nscloud-cache-allow-commit-from-{branch-name}`.

    The `{branch-name}` is the name of the git branch whose GitHub Actions jobs can update the caches.

    ```yaml {7-8} theme={null}
    jobs:
      tests:
        runs-on:
            - nscloud-ubuntu-22.04-arm64-4x16-with-cache
            - nscloud-cache-tag-cache-npm
            - nscloud-cache-size-100gb
            - nscloud-cache-allow-commit-from-main
            - nscloud-cache-allow-commit-from-default

        steps:
          - uses: actions/setup-node@v6
    ```
  </Tab>
</Tabs>

Any GitHub Actions job belonging to git branches that are not included in the allow-list,
will be able to access the Cache Volumes, but their changes to the caches' content will
not be persisted in the end.

#### Manage Cache Access per Job

Instead of protecting your cache per branch, you can also select which jobs may write to the cache.
This allows you to share a cache amongst multiple jobs while some are not allowed to update the cache contents.
Any job without commit rights can still access the cache and can also change contents locally.
But any edits that it makes to the cache will be ignored for future runs.

If using runner labels, you can add the label `nscloud-cache-exp-do-not-commit` to a job.

```yaml {7} theme={null}
jobs:
  tests:
    runs-on:
        - nscloud-ubuntu-22.04-arm64-4x16-with-cache
        - nscloud-cache-tag-cache-npm
        - nscloud-cache-size-100gb
        - nscloud-cache-exp-do-not-commit

    steps:
      - uses: actions/setup-node@v6
```

## Controlling Job Order

By default, there is no queuing or ordering for jobs: as soon as a job is scheduled a runner is started for it.
This changes when your workspace is hitting [concurrency limits](https://namespace.so/docs/architecture/compute/resource-limits)
and jobs have to wait for resources to become available.

Namespace offers controls to influence the order of jobs to be picked up:

### Deterministic assignments

By default, a job can be assigned to any runner that matches that job's labels.
A runner will pick up the job that it's assigned by GitHub, which does not happen in order.
This means that while hitting concurrency limits, jobs with the same labels might not be executed in the expected order.

To control this behaviour, you can add the run id to the labels.
Then only that specific job can be picked up by the runner started for it, and jobs are executed in the order in which they are scheduled:

<Tabs>
  <Tab title="With runner profiles">
    ```yaml {2} theme={null}
    runs-on:
      - namespace-profile-my-profile;github.run-id=${{github.run_id}}
    ```
  </Tab>

  <Tab title="With runner labels">
    ```yaml {3} theme={null}
    runs-on:
      - nscloud-ubuntu-24.04-amd64-2x4-with-features
      - namespace-features:github.run-id=${{github.run_id}}
    ```
  </Tab>
</Tabs>

### Job Priority

When some workflows should not be blocked by other less, time-sensitive ones, you can assign them a priority.
Priority only affects scheduling while running into concurrency limits.

Pending jobs with the lowest numbers will be started first. Jobs with any priority specified take precedence over those without.

To specify a priority, append `job.priority=<number>` to the runs-on label:

<Tabs>
  <Tab title="With runner profiles">
    ```yaml theme={null}
    runs-on: namespace-profile-my-profile;job.priority=1
    ```
  </Tab>

  <Tab title="With runner labels">
    ```yaml theme={null}
    runs-on: nscloud-ubuntu-24.04-amd64-4x8;job.priority=1
    ```

    Unlike other label settings, `job.priority` must be appended inline directly to your machine label.
  </Tab>
</Tabs>

## Namespace Remote Builders

### Building very large images

<Info>
  For most users, relying on Remote Builders is the preferred option.
</Info>

When building very large images, data transfers to Remote Builders can add up.

Your Docker builds can benefit from local cross-invocation caching, instead of using Remote Builders.

With "Local caching" enabled, Namespace automatically configures your
instance to both upload build metadata to your workspace, so it's logged and
traced; and caching is configured using a previously attached cache volume.

Make sure to size your cache appropriately, to benefit from high cache hit ratio.

<Tabs>
  <Tab title="With runner profiles">
    To enable this feature, just open the [runner profile](https://cloud.namespace.so/workspace/actions/profiles) configuration and add a cache volume. Next, select `Locally cached` for your Docker builds.

    <CenteredImage width={500} alt="locally cached Docker build configuration" src="/docs/images/github-actions/in-runner-builder.png" />
  </Tab>

  <Tab title="With runner labels">
    If using runner labels, first make sure you configure [a user cache](#using-a-cache-volume) with labels and then add the following label `nscloud-in-runner-builder`.

    ```yaml {7} theme={null}
    jobs:
      tests:
        runs-on:
          - nscloud-ubuntu-22.04-amd64-8x32-with-cache
          - nscloud-cache-tag-example
          - nscloud-cache-size-100gb
          - nscloud-in-runner-builder

        steps:
          - name: Build and push
            uses: docker/build-push-action@v5
            with:
              context: .
    ```
  </Tab>
</Tabs>

#### Caveats

* Build caching using "local caching" is not shared with Remote Builders; each
  repository uses its own separate cache.
* Although multi-platform builds are supported, only builds of the same platform
  as the runner itself, will experience native performance.

### Large amounts of concurrent builds

Namespace Remote Builders are configured to offer great performance for many concurrent builds.
These defaults provide ideal performance for most customers.
If you run a very large amount of concurrent builds,
please reach out to [support@namespace.so](mailto:support@namespace.so) and we'll scale your Remote Builders to match your needs.

### Builds without output

When you want to test a build without pushing or loading it, use the `cacheonly` output type.
This runs the full build and populates the cache, but skips the export step:

```yaml theme={null}
- uses: docker/build-push-action@v6
  with:
    context: .
    outputs: type=cacheonly
```

### Disable Build Caching

If you prefer to skip build caching altogether, you have two options:

1. **Revert the Docker build context to the default**: Namespace configures Remote Builders
   as a separate `buildx` [context](https://docs.docker.com/engine/reference/commandline/context/). Before invoking a build that should not be cached, you can switch back to the
   default by calling `docker buildx use default`. E.g.

   ```yaml {4,5} theme={null}
   jobs:
     build:
       steps:
         - name: Use default builder
           run: docker buildx use default

         - name: Build and push
           uses: docker/build-push-action@v5
           with:
             context: .
             platforms: linux/amd64,linux/arm64
   ```
2. **Disable Remote Builders**: You can request that runners created for a
   particular workflow job do not use Remote Builders.

<Tabs>
  <Tab title="With runner profiles">
    To disable remote builders, just open the [runner profile](https://cloud.namespace.so/workspace/actions/profiles) configuration and select `No caching` for your Docker builds.

    <CenteredImage width={500} alt="local Docker builder configuration" src="/docs/images/github-actions/no-remote-builder.png" />
  </Tab>

  <Tab title="With runner labels">
    If you are configuring the runners with labels pass an additional configuration label `nscloud-no-remote-builders`, e.g.

    ```yaml {3} theme={null}
    jobs:
      myjob:
        runs-on:
          - nscloud-ubuntu-22.04-amd64-4x16-with-builders
          - nscloud-no-remote-builders
    ```

    <Info>
      Note the use of `with-builders` suffix; it's required in order to apply builder related configuration.
    </Info>
  </Tab>
</Tabs>

## Privileged workflows

Namespace Runner Instances run the runner software itself in a container.
This approach facilitates software packaging and enables [custom base images](/docs/solutions/github-actions/custom-base-images).

If your workflow requires deeper access to the host system, Namespace can run your workflow as privileged and in the host pid namespace.
A common case that requires this is modifying kernel settings using sysctl.

Another case is tooling that creates its own mount or user namespaces, such as bubblewrap (`bwrap`), which some sandboxing and Nix workflows rely on.
Without privileged mode these tools fail with an error like `pivot_root: Operation not permitted`. Enabling `container.privileged` resolves it.

To do so, enable the corresponding features in your runner labels:

<Tabs>
  <Tab title="With runner profiles">
    To make the runner container privileged append the feature `container.privileged`. Similarly, you can select the host pid namespace with `container.host-pid-namespace`.

    ```yaml {2} theme={null}
    runs-on:
      - namespace-profile-e2e-large;container.privileged=true;container.host-pid-namespace=true
    ```
  </Tab>

  <Tab title="With runner labels">
    If you are configuring the runners with labels, you can make the runner container privileged by enabling the feature `container.privileged` and can run in the host pid namespace with `container.host-pid-namespace`, e.g.

    ```yaml {3} theme={null}
    runs-on:
      - nscloud-ubuntu-22.04-arm64-4x16-with-features
      - namespace-features:container.privileged=true;container.host-pid-namespace=true
    ```
  </Tab>
</Tabs>

## Setting up Swap Space

Runners come without swap by default, when a job runs out of memory one or more process get terminated by the out-of-memory killer.
Adding a swap file lets the kernel page cold memory out to disk, which can carry a job past short memory spikes without moving to a larger shape.

<Tabs>
  <Tab title="With runner profiles">
    You can configure swap directly in the [profile editor](https://cloud.namespace.so/workspace/actions/profiles) under `Advanced Settings`.

    <CenteredImage width={500} alt="Swap Space configuration" src="/docs/images/github-actions/advanced-profile-swap-space.png" />
  </Tab>

  <Tab title="With runner labels">
    For label-based runners, automatic swap configuration is not yet supported. Swap can still be enabled manually using `container.privileged` and `container.mount-scratch`. The scratch mount is required because Linux does not support activating a swap file on the runner's default overlay-backed filesystem.

    `/namespace/scratch` provides an ephemeral filesystem that can be used for swap.

    When using runner labels, request the features with `namespace-features` and create the swap file in `/namespace/scratch`.

    ```yaml theme={null}
    jobs:
      build:
        runs-on:
          - nscloud-ubuntu-22.04-amd64-4x8-with-features
          - namespace-features:container.privileged=true;container.mount-scratch=true

        steps:
          - name: Set up swap
            run: |
              sudo fallocate -l 16G /namespace/scratch/swapfile
              sudo chmod 600 /namespace/scratch/swapfile
              sudo mkswap /namespace/scratch/swapfile
              sudo swapon /namespace/scratch/swapfile
    ```
  </Tab>
</Tabs>

## Connecting to Tailscale

To connect a runner to your Tailscale tailnet, reference a named Tailscale spec with the `tailscale.spec` feature. You
must first create that spec with `nsc integrations tailscale set`; the feature value is the spec name (for example
`corp`).

<Tabs>
  <Tab title="With runner profiles">
    ```yaml theme={null}
    runs-on: namespace-profile-my-profile;tailscale.spec=corp
    ```
  </Tab>

  <Tab title="With runner labels">
    ```yaml {3} theme={null}
    runs-on:
      - nscloud-ubuntu-22.04-amd64-4x16-with-features
      - namespace-features:tailscale.spec=corp
    ```
  </Tab>
</Tabs>

See the [Tailscale integration guide](/docs/integrations/tailscale) for creating the spec and the full setup.

## Access Level

Each runner profile can declare an **Access Level** that controls which Namespace
features the runner workload is allowed to use at runtime. Access levels apply
on top of any explicit workload permissions configured on the workspace, and
let you tighten what a particular runner can do without changing your workspace
defaults.

Access Level is a profile-only setting and is configured in the
[runner profile editor](https://cloud.namespace.so/workspace/actions/profiles)
under `Advanced Settings`.

Three levels are available:

* **Permissive** (default): the runner workload uses the workspace's standard
  Namespace permissions. This is the historical behavior and the right choice
  for most profiles.
* **Limited**: the runner workload is restricted to the features needed by
  GitHub Actions jobs, including remote builders, the container registry, the
  cache service, and federated tokens. Other Namespace APIs are not reachable
  from the runner.
* **Restricted**: Namespace feature access is disabled for the runner
  workload. Use this for jobs that should not be able to call any Namespace
  APIs from inside the runner (for example, when running fully untrusted
  third-party code).

If no access level is selected, profiles default to **Permissive**.

## Systemd

Namespace Runner images by default do NOT use Systemd.
For some users this might require small changes to their workflows, e.g. replacing
`sudo systemctl start ...`
with
`sudo service start ...`.

If your workflows rely on systemd, an image using systemd is available [upon request](mailto:support@namespace.so).

<h2 id="jobs-in-containers">
  Running Jobs in Containers
</h2>

Namespace Runners support using [custom containers for GitHub Jobs](https://docs.github.com/en/actions/using-jobs/running-jobs-in-a-container).

### Accessing Namespace resources from Containers

To access Namespace resources from within a container, extra configuration is required.
In particular, the directory `/var/run/nsc/` must be mounted into the container, and [`namespacelabs/nscloud-setup`](/docs/reference/github-actions/nscloud-setup) needs to be run.

See the following snippet for a working example of accessing Bazel:

```yaml {7,11} theme={null}
tests:
  runs-on: namespace-profile-my-profile-for-containers

  container:
    image: <my-image-ref>
    volumes:
      - /var/run/nsc/:/var/run/nsc/

    steps:
      - uses: actions/checkout@v4
      - uses: namespacelabs/nscloud-setup@v0
      - name: Setup Bazel cache
        run: |
          nsc bazel setup --remote=false --bazelrc /etc/bazel.bazelrc
      - name: Bazel test
        run: |
          bazel --bazelrc=/etc/bazel.bazelrc test //..
```

### Accessing Cache Volumes from Containers

To access [Cache Volumes](/docs/solutions/github-actions/caching#cache-volumes) from within a container, additional configuration is required.

For example, when using an Ubuntu-based custom image, the following snippet provides a working, minimal example:

```yaml {7,9,10,15-17} theme={null}
tests:
  runs-on: namespace-profile-my-profile-for-containers

  container:
    image: <my-image-ref>
    env:
      NSC_CACHE_PATH: ${{ env.NSC_CACHE_PATH }} # env.NSC_CACHE_PATH contains the path to Cache Volume directory, that is `/cache`.
    volumes:
      - /cache:/cache # Where the Cache Volume is mounted.
    options: --cap-add=SYS_ADMIN # Required to by nscloud-cache-action to call `mount`.

  steps:
    - uses: actions/checkout@v4

    - name: Install sudo
      run: |
        apt-get update -y && apt-get install -y sudo

    - name: Setup cache
      uses: namespacelabs/nscloud-cache-action@v1
      with:
        cache: rust
```

Please see our [nscloud-cache-action documentation](/docs/reference/github-actions/nscloud-cache-action#advanced-running-github-jobs-in-containers) for details.

### Accessing Git Mirrors from Containers

If your workflow runs in a container, additional configuration is required to use [cached Git Repositories](/docs/integrations/git-checkouts):

```yaml {7,9} theme={null}
tests:
  runs-on: namespace-profile-my-profile-for-containers

  container:
    image: <my-image-ref>
    env:
      NSC_GIT_MIRROR: ${{ env.NSC_GIT_MIRROR }} # env.NSC_GIT_MIRROR contains the path to the git mirror directory.
    volumes:
      - /gitmirror:/gitmirror # Where the git mirror cache is mounted.

  steps:
    - name: Checkout with Namespace Git mirrors cache
      uses: namespacelabs/nscloud-checkout-action@v8
```

Please see our [nscloud-checkout-action documentation](/docs/reference/github-actions/nscloud-checkout-action#advanced-running-github-jobs-in-containers) for details.

### Using Local build caching from Containers

While Remote Builders provide the best performance for most scenarios, in-runner builders (locally cached) excel when building massive images (10GB+).
Keeping the build local skips network transfer time.

To access in-runner builders from within a container, additional configuration is required.

<Steps titleSize="h3">
  <Step id="enable-local-build-caching" title={<span>Enable local build caching</span>}>
    <Tabs>
      <Tab title="With runner profiles">
        To enable this feature, just open the [runner profile](https://cloud.namespace.so/workspace/actions/profiles) configuration and add a cache volume. Next, select `Locally cached` for your Docker builds.

        <CenteredImage width={500} alt="locally cached Docker build configuration" src="/docs/images/github-actions/in-runner-builder.png" />
      </Tab>

      <Tab title="With runner labels">
        If using runner labels, first make sure you configure [a user cache](/docs/solutions/github-actions/caching) with labels and then add the following label `nscloud-in-runner-builder`.

        ```yaml {7} theme={null}
        jobs:
          tests:
            runs-on:
              - nscloud-ubuntu-22.04-amd64-8x32-with-cache
              - nscloud-cache-tag-example
              - nscloud-cache-size-100gb
              - nscloud-in-runner-builder

            steps:
              - name: Build and push
                uses: docker/build-push-action@v5
                with:
                  context: .
        ```
      </Tab>
    </Tabs>
  </Step>

  <Step id="update-your-workflow" title={<span>Update your workflow</span>}>
    In the job step definition, you need to forward the buildkit socket and create a builder inside the container that uses the parent:

    ```yaml {6,10-12} theme={null}
    jobs:
      myjob:
        container:
          image: <my-image-ref>
          volumes:
            - /var/run/buildkit/buildkitd.sock:/var/run/buildkit/buildkitd.sock
      steps:
        - uses: actions/checkout@v4
        - ...
        - name: Configure builder
          run: |
            docker buildx create --driver remote --name parent --use unix:///var/run/buildkit/buildkitd.sock
        - ...
    ```
  </Step>
</Steps>

## macOS Features

### Bleeding-edge Images

Namespace team continuously makes changes to macOS runner images to keep the software up-to-date and
add new Xcode versions as soon as Apple releases them.
[More info on image updates](/docs/architecture/compute/macos#macos-image-updates).

To avoid regressions upcoming images go through multiple release stages before production release.
This means that new Xcode versions become available to most customers with a short delay.
However, it is possible to take advantage of new Xcode versions early. You can enroll your
runners into using bleeding-edge macOS images.

**Note**: Images in the bleeding-edge channel have not passed the full set of validation checks and may contain regressions.
We are happy to hear feedback from early users of these images via Namespace support channels.
But enrolling should be done without expectation of perfect stability.

<Tabs>
  <Tab title="With runner profiles">
    Visit the [runner profile editor](https://cloud.namespace.so/workspace/actions/profiles) and select the **Use bleeding-edge images** checkbox.

    <CenteredImage width={500} alt="runner profile macOS bleeding edge checkbox" src="/docs/images/github-actions/macos-bleeding-edge.png" />
  </Tab>

  <Tab title="With runner labels">
    Add `namespace-features:macos.channel=preview` to the list of runner labels:

    ```yaml {3} theme={null}
    runs-on:
      - nscloud-macos-sequoia-arm64-6x14-with-features
      - namespace-features:macos.channel=preview
    ```
  </Tab>
</Tabs>
