← All TILs · nixos

NixOS on WSL2: a short admin runbook, and the files WSL manages instead of NixOS

nixos - 2026-09-25

NixOS-WSL runs a full NixOS inside WSL2, with systemd, nixos-rebuild and generations included. Everything in the first-steps entry still applies. What changes is that WSL owns a few files NixOS normally manages. This runbook checks install, configuration and recovery against the project's own docs and module source (release 2605.7.2, which tracks NixOS 26.05).

Prerequisites

Install

Download nixos.wsl from the latest release. Releases before 2411 called it nixos-wsl.tar.gz. Then, in PowerShell:

WSL version Command
2.4.4 or later wsl --install --from-file nixos.wsl (or double-click the file). Use --name and --location to change the defaults.
Older wsl --import NixOS $env:USERPROFILE\NixOS nixos.wsl --version 2

On WSL 2.4.4 and later, the install starts the distro immediately and prints the NixOS-WSL welcome banner:

PowerShell: wsl --install --from-file nixos.wsl with --name NixOS and --location f:\wsl\nixos installs the distribution and launches it; the NixOS-WSL welcome banner asks to run sudo nix-channel --update and sudo nixos-rebuild switch, and notes it disappears after the first rebuild; the prompt is nixos@nixos in /mnt/c/Users/abdel.
Installing from nixos.wsl with a custom --name and --location. The first shell opens in the Windows directory the command was run from, under /mnt/c. Select the image for full size.

The banner asks for two commands, not just the channel update the install docs mention. Run both, and set a password first:

passwd                       # user "nixos", in wheel; sudo asks for a password by default
sudo nix-channel --update    # needed once before the first nixos-rebuild
sudo nixos-rebuild switch    # picks up the latest NixOS and NixOS-WSL; also removes the banner

Expect that first switch to take a while. It downloads everything the configuration describes, and the later ones only fetch what changed.

The banner only comes from the configuration baked into the tarball, which is used until the first nixos-rebuild. After that, nixos-wsl-welcome shows it again on demand. Later, start the distro with wsl -d NixOS.

Make it the default distro with wsl -s NixOS.

Configuration

Keep the configuration outside the distro

/etc/nixos/configuration.nix lives in the distro's virtual disk. For wsl --unregister, Microsoft warns that "all data, settings, and software associated with that distribution will be permanently lost", and that includes the only description of your system. Put the configuration in a Git repository with a remote from day one. Git isn't installed yet, but nix-shell -p git provides it for the session until the configuration adds it for good.

The push is what protects you, not where the clone lives. A clone under /mnt/c would also survive an unregister, but Linux tools read Windows files over 9P, and Microsoft's file-system guidance is to keep them in the Linux file system "for the fastest performance speed". So keep the clone in your home directory.

Channels or flakes

The installed /etc/nixos/configuration.nix imports <nixos-wsl/modules>. The installer adds a nixos-wsl channel next to the usual nixos one, so sudo nix-channel --update updates both. That setup is fine to keep.

To switch to flakes, follow the project's flakes how-to: add a nixos-wsl input and put nixos-wsl.nixosModules.default in your module list with wsl.enable = true. Things to watch:

Then rebuild from the repository, naming the configuration from nixosConfigurations:

sudo nixos-rebuild switch --flake .#nixos

Interop with Windows

Four switches control how Windows binaries and PATH leak into NixOS. All are verified in the module source:

Option Default What it does
wsl.wslConf.interop.enabled true Allows running .exe files from the Linux shell
wsl.wslConf.interop.appendWindowsPath true Tells WSL to add the Windows PATH to $PATH
wsl.interop.includePath true "Include Windows PATH in WSL PATH", applied by NixOS-WSL's shell init
wsl.interop.register false Re-registers the binfmt handler for Windows executables

The last one is the trap. If you add any binfmt registration, for example boot.binfmt.emulatedSystems = [ "aarch64-linux" ] to build ARM images, the module warns that doing so "without re-registering WSLInterop (wsl.interop.register) will break running .exe files from WSL2". Set wsl.interop.register = true alongside it.

GPU acceleration

The module already sets hardware.graphics.enable = true. To use the Windows host's GPU driver, add:

wsl.useWindowsDriver = true;

That links the host libraries from /usr/lib/wsl/lib into a package NixOS can use. The generic WSL approach, wsl.wslConf.automount.ldconfig, doesn't help here. Its own option description says it "does not work with NixOS and wsl.useWindowsDriver should be used instead".

Docker without Docker Desktop

Docker Desktop needs a paid subscription for professional use in organizations with 250 or more employees or $10 million or more in annual revenue. Inside NixOS-WSL you can use the standard NixOS module instead. The project removed its old wsl.docker-native option with the message "Additional workarounds are no longer required for Docker to work":

virtualisation.docker.enable = true;
virtualisation.docker.autoPrune.enable = true;   # weekly `docker system prune -f`; volumes are kept
users.users.nixos.extraGroups = [ "docker" ];

enableOnBoot already defaults to true. Group membership only applies to new sessions, so run wsl -t NixOS before the first docker run. If you'd rather keep Docker Desktop, wsl.docker-desktop.enable = true sets up its integration (the docker group, and the helper binaries it expects).

VS Code Remote needs nix-ld

The VS Code server downloads a generic Linux Node.js binary, which expects /lib64/ld-linux-x86-64.so.2. That file doesn't exist on NixOS. The project's VS Code how-to gives two fixes. The more robust one:

programs.nix-ld.enable = true;
environment.systemPackages = [ pkgs.wget ];   # the how-to requires it for both fixes

nix-ld already ships a default library set that includes zlib, openssl, curl and the C++ runtime. Add to programs.nix-ld.libraries only when a binary still reports a missing .so. The module sets NIX_LD as a session variable, so restart the distro with wsl -t NixOS, then reconnect VS Code.

/etc/wsl.conf changes need a distro restart

Because /etc/wsl.conf is generated, edit wsl.wslConf.* in configuration.nix, never the file itself. A rebuild overwrites the file. WSL also only reads it at startup (Microsoft's docs have you restart with wsl --shutdown after editing it). So after any nixos-rebuild switch that touches wsl.wslConf, run:

wsl -t NixOS      # this distro only
wsl --shutdown    # all distros, if -t isn't enough

The troubleshooting docs note that "some issues will only be resolved after a full restart of WSL".

Troubleshooting

Rollback without a boot menu

WSL boots its own kernel, so NixOS-WSL turns off the bootloader (loader.grub.enable = false, and installBootLoader is true, a no-op). The "pick an older generation from the boot menu" escape from the first-steps entry doesn't exist here. While the distro still starts, roll back from inside:

sudo nixos-rebuild switch --rollback
ls -l /nix/var/nix/profiles/system-*-link   # the generations you can go back to

When it doesn't start, use the recovery shell.

Recovery shell

If a bad generation leaves the distro unusable, this bypasses your system's normal startup:

wsl -d NixOS --system --user root -- /mnt/wslg/distro/bin/nixos-wsl-recovery

Per the recovery docs, it loads WSL's system distro, activates your configuration and chroots into it, "similar to what nixos-enter would do". To boot into an older generation, add --system /nix/var/nix/profiles/system-42-link. That path is relative to the NixOS root.

Networking: WSL owns /etc/hosts and resolv.conf

wsl.wslConf.network.generateHosts and generateResolvConf both default to true, so WSL writes both files at startup.

For /etc/hosts, NixOS-WSL then disables NixOS's own copy (hosts.enable = false in the module). NixOS builds that file from networking.hosts and networking.extraHosts, so both are silently ignored.

For resolv.conf, WSL and NixOS's resolvconf service can both end up writing it. If you want networking.nameservers to be the only source, turn WSL's generation off:

wsl.wslConf.network.generateHosts = false;      # networking.extraHosts works again
wsl.wslConf.network.generateResolvConf = false; # NixOS alone manages resolv.conf
networking.nameservers = [ "1.1.1.1" ];

Rebuild, then restart the distro, as with any other wsl.wslConf change. The WSL hostname follows networking.hostName by default, through wsl.wslConf.network.hostname.

File permissions on /mnt/c

Windows drives are mounted with metadata,uid=1000,gid=100 by default (wsl.wslConf.automount.options). Microsoft's docs describe metadata as adding metadata "to Windows files to support Linux system permissions". So chmod on /mnt/c files works and persists. Everything also appears owned by UID 1000, the default nixos user. If your user has a different UID, change the mount options to match.

Renaming the default user

Set wsl.defaultUser, then follow the documented order exactly. Use nixos-rebuild boot, not switch, which the docs say "may lead to the new user account being misconfigured". Then run wsl -t NixOS, wsl -d NixOS --user root exit, and wsl -t NixOS again, and open a new shell.

Further reading

Stéphane Robert's NixOS dans WSL : environnement complet (in French) builds the same setup into a full workstation: a flake repository split into hosts/ and modules/, Docker, Zsh with Oh My Zsh, and nix-ld for VS Code. The Docker, VS Code and "keep the configuration in Git" sections above follow its outline, checked against the NixOS-WSL and nixpkgs sources. Its Zsh part has its own entry.

Created 2026-09-25T20:22:13+02:00, updated 2026-09-26T13:19:42+02:00 · History · Edit