Shrinking a NixOS ISO from 458 MiB to 183 MiB

logickkk11 pts0 comments

I can haz smoller NixOS ISOs? | natkr's ramblings

I can haz smoller NixOS ISOs?

2026-06-19

::<br>Tech

:: tags:<br>#Deep Dive<br>#ISO<br>#Nix<br>#NixOS<br>#Virtualization

In which I painstakingly remove functionality from a Linux live image.<br>Like a normal person.

But first, I can haz ISO at all

One of NixOS' cooler party tricks is that you can<br>trivially take some configuration and run it as a virtual machine (VM).<br>nixos-rebuild build-vm will give you one for your system<br>configuration1. But you can also do it for any given configuration<br>you want, system or not!

let pkgs = import (fetchTarball {<br>url = "https://github.com/NixOS/nixpkgs/archive/567a49d1913ce81ac6e9582e3553dd90a955875f.tar.gz";<br>sha256 = "1vq77hlx8mi3z03pw2nf6r5h7473r1p9yxyf58ym3fh01zppmfln";<br>}) {};<br>in pkgs.nixos {<br>system.stateVersion = "26.05";<br>services.getty.autologinUser = "root";

That's enough to get a minimal VM, you can run it yourself with<br>$(nix-build basic-vm.nix --attr vm --log-format bar --no-out-link)/bin/run-nixos-vm.

This will create a "thin" VM: there's a disk image, but it only<br>contains the files you've made yourself once you're inside of the<br>VM.2 Everything else (cough /nix/store) gets mounted in from your<br>host OS instead.

And that's great when it fits, but sometimes we just need something a<br>bit more normal. Something we can just run in<br>libvirt3, or even ship off to some remote<br>host that might not have Nix. It might not even run Linux at all. Hell,<br>there might not even be a hypervisor!4

In short, I just want a damn ISO.

Thankfully, NixOS still has my back here:

let pkgs = import (fetchTarball {<br>url = "https://github.com/NixOS/nixpkgs/archive/567a49d1913ce81ac6e9582e3553dd90a955875f.tar.gz";<br>sha256 = "1vq77hlx8mi3z03pw2nf6r5h7473r1p9yxyf58ym3fh01zppmfln";<br>}) {};<br>in pkgs.nixos ({ lib, ... }: {<br>system.stateVersion = "26.05";<br>services.getty.autologinUser = "root";

imports = [ "${pkgs.path}/nixos/modules/installer/cd-dvd/iso-image.nix" ];<br>image.baseName = lib.mkForce "nixos";<br>})

We'll need to run QEMU ourselves this time, but it's not too bad:<br>qemu-system-x86_64 --cdrom $(nix-build basic-iso.nix --attr isoImage --log-format bar --no-out-link)/iso/nixos.iso -m 1G --accel kvm5

Trouble in paradise

So we're done here, right? But... we haven't even touched the<br>salad actual topic of the title! So, uh, about that...

$ ls -lh $(nix-build basic-iso.nix --attr isoImage --log-format bar --no-out-link)/iso/nixos.iso<br>-r--r--r-- 1 root root 458M jan 1 1970 /nix/store/kg8mv6296hbhm8als26r400nj1s7ry1n-nixos.iso/iso/nixos.iso

Uh oh. 458MiB‽6 And it doesn't even do anything yet!

[root@nixos:~]# vim<br>-bash: vim: command not found

That's almost 10x larger than what my childhood's Damn Small<br>Linux needed to run a<br>surprisingly complete desktop environment!7 We're probably not going<br>to reach those levels, but surely there's room to do at least a little<br>better.

For comparison Alpine's VM<br>ISO<br>currently sits at around 66MiB. So I guess that's a reasonable baseline<br>to compare against.

What else even is there?

Well, let's have a look at what we're paying for...

$ sudo mount $(nix-build basic-iso.nix --attr isoImage --log-format bar --no-out-link)/iso/nixos.iso iso --mkdir<br>$ du iso --all --block-size=1M | sort -n | tail -n10<br>3 iso/isolinux<br>13 iso/boot/nix/store/92id8yn2g9kj7bskld42p222pmk3y3ms-linux-6.18.35<br>13 iso/boot/nix/store/92id8yn2g9kj7bskld42p222pmk3y3ms-linux-6.18.35/bzImage<br>26 iso/boot/nix/store/zvn61hpw86ncvgsr8vjrfi3ahnk2c9hb-initrd-linux-6.18.35<br>26 iso/boot/nix/store/zvn61hpw86ncvgsr8vjrfi3ahnk2c9hb-initrd-linux-6.18.35/initrd<br>39 iso/boot<br>39 iso/boot/nix<br>39 iso/boot/nix/store<br>416 iso/nix-store.squashfs<br>458 iso

So.. 416MiB for the main userspace, 26MiB for the early boot<br>environment, and 13MiB for the kernel itself. At least we can dismiss<br>the latter two for now.8 So let's crack that squash open too, and<br>see what secrets it hides...

$ sudo mount iso/nix-store.squashfs squash --mkdir<br>$ du squash --max-depth=1 --block-size=1M | sort -n | tail -n20<br>11 squash/bizyfqdw0h67wzqmp10knmf9s2pqahdb-file-5.47<br>13 squash/pa0x6m662kr9vr875fcp1cl9wwkswsa8-coreutils-full-9.11<br>14 squash/dscn63ni6fp8p58y99b3ywk478b4bvmv-texinfo-interactive-7.2<br>14 squash/gf6i4cbisapj28y2dnqhpk1s95vd2r36-util-linux-2.42-lib<br>15 squash/qmajd4nyy8zf70g9p4x2lcq45gq5gzy3-boost-1.89.0<br>16 squash/jlyahda14aya375lv7k9fsin2zk90nxz-glib-2.88.1<br>21 squash/cfjm0s2jnlaiz9y3byvfa0fc6fp2la20-systemd-minimal-260.1<br>22 squash/92id8yn2g9kj7bskld42p222pmk3y3ms-linux-6.18.35<br>24 squash/dis2sflz0lifcji3gb81rbr7896dw39l-nix-manual-2.34.7<br>26 squash/zvn61hpw86ncvgsr8vjrfi3ahnk2c9hb-initrd-linux-6.18.35<br>28 squash/ryi73l6ic3pz9vh69nj0i4l188vkqgaq-nixos-manual-html<br>30 squash/r1nzk3ga4fk9q2xw77md2ln88m98d2vc-grub-2.12<br>32 squash/cl0a76f140zrrbx0n0qpjx07794q5gzn-grub-2.12<br>34 squash/57iz36553175g3178pvxjij8z5rcsd4n-glibc-2.42-61<br>39 squash/clpq5c7bysml4vqpa1x60a5yk3nzkfj4-icu4c-76.1<br>56 squash/6plwsm6pkq79yjv4xvy8csk2pd4hzr67-perl-5.42.0<br>60 squash/a8avqfxd649rfgfpqldja6v38ljb8fj5-systemd-260.1<br>128...

nixos squash linux store boot even

Related Articles