# NixOS Froze During a Rust Build. I Had No Swap.

A Rust build froze my NixOS workstation. I added disk swap, zram and lower build concurrency after finding memory pressure in the logs.

Author: Ruhani Rabin
Canonical: https://rabin.blog/nixos-freeze-memory-pressure-swap/
Published: 2026-11-09T02:14:00.000Z

After almost 2 and half months of using NixOS, I had my first **freeze** that needed to use the power button to reset. A **Rust** build was running, memory reached about **96%**, and the desktop **completely froze**.

The workstation has 32 GB of RAM. But, It had no swap at all.

## Check what happened first

After restarting, I checked swap and the previous boot’s logs:

```bash
swapon --show
free -h
journalctl -b -1 --no-pager | grep -Ei 'out of memory|oom|memory pressure|killed process'
```

Here is an example output from `journalctl`:

```bash
Nov 09 08:52:31 nixos kernel: OOM killer disabled.
Nov 09 08:52:31 nixos kernel: OOM killer enabled.
Nov 09 09:24:32 nixos systemd-journald[576]: Under memory pressure, flushing caches.
Nov 09 09:24:35 nixos systemd-journald[576]: Under memory pressure, flushing caches.
```

The **journal showed memory pressure** just before the restart. I couldn’t find a recorded OOM kill because I used physical power button to restart.

So I can say the machine was under memory pressure, but I can’t prove the kernel’s out-of-memory killer caused the freeze. Even though I am pretty certain about it.

Nix also allowed 20 simultaneous builds, with each build allowed to use all CPU cores. That was more aggressive than I wanted on a workstation I was still using.

## Add swap and reduce build concurrency

I chose 32 GiB of disk swap, plus zram at 25% of RAM. [Zram stores compressed pages in RAM](https://docs.kernel.org/admin-guide/blockdev/zram.html). Its higher priority makes Linux use it before disk swap.

My root filesystem is single-device Btrfs. I added the following inside the existing attribute set in `configuration.nix`:

configuration.nix

```nix
swapDevices = [ {
  device = "/swap/swapfile";
  size = 32768; # MiB
  priority = 10;
} ];


zramSwap = {
  enable = true;
  algorithm = "zstd";
  memoryPercent = 25;
  priority = 100;
};


nix.settings.max-jobs = 2;
nix.settings.cores = 4;


systemd.services.mkswap-swap-swapfile.preStart = ''
  if [ ! -e /swap ]; then
    btrfs subvolume create /swap
  fi
  btrfs subvolume show /swap >/dev/null
  chmod 0700 /swap
'';
```

The pre-start hook creates a separate `/swap` subvolume before NixOS creates the swapfile. It assumes `/swap` is unused or already a Btrfs subvolume. An ordinary directory there will fail the check. This hook is specific to my Btrfs layout.

Keeping swap in its own subvolume avoids including it in parent snapshots. Active swapfiles still affect some Btrfs maintenance operations, including balance and scrub. Read the [Btrfs swapfile restrictions](https://btrfs.readthedocs.io/en/latest/Swapfile.html) before copying this setup.

**Note**: The [Nix build limits](https://nix.dev/manual/nix/2.35/command-ref/conf-file.html#conf-cores) control concurrent builds and cores per build. They don’t put a hard memory limit on a compiler.

## Watch the first rebuild

The catch is timing: swap becomes active **after** the configuration builds. Adding it won’t protect the build needed to activate it.

For Codex Rust build, I changed it to the package source’s signed binary cache before retrying. Check that your rebuild downloads the package instead of starting the same large compilation again.

Keep your usual rebuild target and pass `--max-jobs 2 --cores 4` to the first `nixos-rebuild test` so it uses the lower limits before they’re activated.

I tested the configuration, then switched it permanently. After activation, `swapon --show` reported:

```text
NAME           TYPE      SIZE USED PRIO
/dev/zram0     partition 7.8G   0B  100
/swap/swapfile file       32G   0B   10
```

**Zero usage is fine when RAM is available**. I haven’t repeated the heavy build or checked swap persistence after a normal reboot yet.

If you undo this later, removing the declaration **leaves the swapfile on disk**. Plan its deactivation and deletion when there’s enough free RAM. Turning off heavily used swap during memory pressure can exhaust RAM again.
