# Nix

Share Nix build outputs across runners with nixcache and cache public packages on demand.

`nixcache` is a local HTTP binary-cache proxy backed by Namespace blobstorage. It combines a local disk cache with shared storage, so a fresh runner can reuse cached Nix outputs without landing on the machine that built them.

> **NOTE**  
> Nix caching is in **early access**. [Contact support](mailto:support@namespace.so) to set up a cache namespace and the credentials your workflows need.

The proxy supports Linux and macOS on amd64 and arm64. It works with an existing Nix installation and does not change where builds execute. You can also use it on a [remote Nix builder](#remote-builders); configure each machine that needs to substitute or publish outputs.

## Install nixcache

Install the latest released binary:

```bash
theme={null}
curl -fsSL https://public.namespaceblobs.com/nixcache/install.sh | sh
```

The installer verifies the archive's SHA-256 checksum and installs to `~/.local/bin/nixcache` without sudo by default. It requires `curl`, `tar`, `sed`, `awk`, and either `sha256sum` or `shasum`. It does not install Nix, modify Nix configuration, or start the proxy.

## Start the proxy

After cache access is enabled for your workspace, start the proxy:

```bash
theme={null}
if [ -n "${NSC_CACHE_PATH:-}" ]; then
  cache_root="$NSC_CACHE_PATH"
elif [ "$(uname -s)" = Darwin ]; then
  cache_root="$HOME/Library/Caches"
else
  cache_root="${XDG_CACHE_HOME:-$HOME/.cache}"
fi

~/.local/bin/nixcache \
  -cache_dir "$cache_root/nixcache"
```

The command starts a background process and returns once the listener is ready. It prints the listener address, PID, and log path. The default address is `http://127.0.0.1:18765`.

The example prefers the [Cache Volume](/content/docs/architecture/storage/cache-volumes.md) at `NSC_CACHE_PATH` to reuse local downloads across jobs. Without a volume, it uses the OS user cache directory: `~/Library/Caches` on macOS, or `XDG_CACHE_HOME` with a fallback to `~/.cache` on Linux. The examples below reuse this `cache_root` variable.

`-cache_dir` selects a dedicated local directory for cached archives and staged uploads. Without it, the proxy uses temporary storage that is removed on clean shutdown. Shared blobstorage remains available to other runners regardless of the local directory.

### Authentication

On Namespace instances, `nixcache` automatically uses the instance's credentials.

On other machines, sign in with [`nsc login`](/content/docs/reference/cli/login/index.html) before starting the proxy:

```bash
theme={null}
nsc login
```

The credentials must have access to the cache namespace.

## Configure Nix

Add these settings to your Nix configuration to route substitution through the proxy:

```ini
theme={null}
substituters = http://127.0.0.1:18765
require-sigs = true
```

Keep Nix's standard `cache.nixos.org-1` trusted public key. Public packages retain their original signatures, so no additional key is required for the default public fallback.

For multi-user Nix, set these options in the daemon's configuration, normally `/etc/nix/nix.conf`, and reload or restart the daemon as required by your Nix installation. The daemon must be able to reach the proxy's loopback address. A proxy running inside a different container or network namespace is not reachable at the daemon's `127.0.0.1`.

Use `extra-substituters` instead of `substituters` if you want to retain other configured caches. Keeping `https://cache.nixos.org` as a direct substituter lets Nix bypass the proxy, so those downloads may not populate the Namespace cache.

After configuration, ordinary Nix commands use the cache:

```bash
theme={null}
nix build .#your-package
```

Replace `your-package` with an output from your project. This example uses flakes and the `nix` command, which must be enabled in your Nix installation.

## Public-cache fallback

By default, `nixcache` falls back to `https://cache.nixos.org` for entries missing from Namespace blobstorage. It downloads archives into the local cache using parallel byte ranges and serves available bytes to Nix before the full download completes.

Choose how upstream entries are cached with `-cache-upstream`:

| Mode                | Behavior                                                                                                                                                                                                                                    |
|---------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `persist` (default) | Fetch missing entries from the public upstream, cache archives locally, and publish entries to Namespace blobstorage in the background. Requires write access to populate shared storage.                                                          |
| `local-only`        | Fetch missing entries from the public upstream and cache archives locally, without publishing fallback entries to blobstorage. Use this with read-only credentials.                                                                            |
| `off` or `disabled` | Disable public fallback. Continue serving local and blobstorage entries. Explicit uploads remain enabled.                                                                                                                                  |

For example, restrict a consumer to entries already available in Namespace:

```bash
theme={null}
~/.local/bin/nixcache \
  -cache_dir "$cache_root/nixcache" \
  -cache-upstream=off
```

Namespace credentials are never sent to the public upstream.

Fallback happens only on a blobstorage not-found response. Authentication failures, throttling, and storage outages do not trigger public fallback. Namespace credentials are still required in `local-only` mode.

In `persist` mode, the proxy publishes the archive before its original `.narinfo` metadata. It preserves Nix hashes and signatures, so no private signing key is needed to mirror public packages. Once publication finishes, another runner can fetch the entry from blobstorage even with fallback disabled.

Publication is asynchronous and best-effort. Failures are logged without interrupting the read. If a job must populate shared storage, allow publication to finish before stopping the proxy: a `fallback entry stored` log for a `.narinfo` path confirms that its archive and metadata were published. Stopping the proxy cancels pending publication.

## Publish your own builds

Trusted CI needs cache write access and a Nix private signing key. Follow the [authentication instructions](#authentication), then publish a built output and its dependencies using native `nix copy`:

```bash
theme={null}
~/.local/bin/nixcache \
  -cache_dir "$cache_root/nixcache-writer"

nix build .#your-package
nix copy \
  --to 'http://127.0.0.1:18765?secret-key=/run/secrets/cache-private.key' \
  ./result
```

Run the writer instead of the reader proxy on that machine, or assign separate loopback ports with `-listen` and update the URLs accordingly. Replace the signing-key path with a file provisioned by your CI secret manager.

`secret-key` is a native Nix store URI setting. Nix reads the signing key locally; it is not sent to the proxy. No separate upload client is required.

Consumers of your signed outputs must add the corresponding public key to their Nix configuration:

```ini
theme={null}
extra-trusted-public-keys = MY-CACHE:BASE64_PUBLIC_KEY
```

Replace the placeholder with your cache's public signing key. Keep `require-sigs = true` and retain any upstream public keys you use.

### Isolate untrusted forks

Give fork jobs credentials with only namespace-scoped `blobs` read access. Trusted CI uses separate writer credentials and holds the private signing key. Support can help configure these permissions during early access.

> **WARNING**  
> `-cache-upstream=local-only` disables automatic fallback publication, not writes. Blobstorage enforces access permissions. Any process that can reach the proxy can use its credential's permissions, so never expose a writer proxy, writer credential, private signing key, or writable trusted cache directory to untrusted jobs.

Use a separate proxy and local cache directory for each trust boundary. Public signing keys can be shared with readers; private keys cannot.

## Operation and troubleshooting

Background logs are written to `cache_dir/cacheproxy.log` when `-cache_dir` is set. Use `-log_file` to choose another location, or `-foreground` to run under a process supervisor and log to the terminal. Logs include HTTP access and download or publication failures.

At startup, the proxy removes unfinished downloads and staged uploads while preserving completed local entries. Stop it with `kill -TERM <printed-pid>` during job cleanup. There is no local eviction policy yet: budget disk space for downloads and concurrent uploads, and remove old cache directories while the proxy is stopped.

Local cache entries are partitioned by storage endpoint, namespace, upstream URL, and credential scope. Token rotation can cause cold local reads; a persistent directory does not guarantee a local hit across credential changes.

## Remote builders

Namespace compute instances can also run as remote Nix builders using Nix's [distributed builds](https://nix.dev/manual/nix/2.18/advanced-topics/distributed-builds) feature. Remote builders execute builds; `nixcache` stores and serves their outputs. You can use either independently or configure the cache on your remote builders as well.

### 1. Create a bare compute instance

Create a new compute instance with your SSH key:

```bash
theme={null}
nsc create --bare --ssh_key ~/.ssh/id_ed25519.pub
```

You can use an existing SSH key or create a new one specifically for this purpose with `ssh-keygen -t ed25519`.

Grab the instance ID from the output.

### 2. Install Nix on the remote instance

SSH into the instance and install Nix using the Determinate installer:

```bash
theme={null}
ssh <id>@ssh.ord2.namespace.so "curl -fsSL https://install.determinate.systems/nix | sh -s -- install linux --determinate --init none --no-confirm"
```

Bare instances run as root and do not require `sudo`. With `--init none`, the installer leaves the Nix daemon stopped and may report a missing daemon socket during its self-test. Start it after configuring the cache below.

### 3. Install and start nixcache on the builder

Run the following from your client. The quoted heredoc keeps the environment variables on the remote instance, so the cache directory belongs to the builder rather than your client:

```bash
theme={null}
ssh <id>@ssh.ord2.namespace.so 'bash -s' <<'EOF'
set -eu
curl -fsSL https://public.namespaceblobs.com/nixcache/install.sh | sh

cache_root="${NSC_CACHE_PATH:-${XDG_CACHE_HOME:-$HOME/.cache}}"
~/.local/bin/nixcache -cache_dir "$cache_root/nixcache"
EOF
```

This Linux example prefers the instance's Cache Volume when available. The proxy uses the instance's credentials and defaults to `-cache-upstream=persist` to [publish public packages to shared storage](#public-cache-fallback).

### 4. Configure Nix on the builder

Add a separate configuration file and include it from `nix.custom.conf`, which the Determinate installer reserves for user settings:

```bash
theme={null}
ssh <id>@ssh.ord2.namespace.so 'bash -s' <<'EOF'
set -eu
cat > /etc/nix/nixcache.conf <<'CONF'
substituters = http://127.0.0.1:18765
require-sigs = true
CONF
if ! grep -qxF '!include nixcache.conf' /etc/nix/nix.custom.conf; then
  printf '\n!include nixcache.conf\n' >> /etc/nix/nix.custom.conf
fi
EOF
```

Keep the existing trusted public keys. Determinate also adds its own installer substituter; the default `cache.nixos.org` route now goes through the proxy.

### 5. Start the Nix daemon

Start the daemon once on this fresh instance. Redirect its input and output so it remains running after SSH disconnects:

```bash
theme={null}
ssh <id>@ssh.ord2.namespace.so 'bash -s' <<'EOF'
set -eu
nohup /nix/var/nix/profiles/default/bin/nix-daemon \
  > /var/log/nix-daemon.log 2>&1 < /dev/null &
EOF
```

On an existing builder with a running daemon, restart that daemon after configuration changes instead of starting a second one.

### 6. Test the installation

Run a package on the builder. Nix uses the builder's local proxy for substitution, which falls back to `cache.nixos.org` on a shared-cache miss:

```bash
theme={null}
ssh <id>@ssh.ord2.namespace.so nix run nixpkgs#hello
```

If the package is already in the builder's Nix store, Nix does not need to fetch it again. Check the proxy's access logs to see which requests reached the cache.

Follow Nix's distributed-build configuration to connect your client to this instance. Enable `builders-use-substitutes = true` on the client to let remote builders fetch available dependencies from their configured substituters instead of copying them from the client. The proxy must run on the builder: its `127.0.0.1` is separate from the client's.
