CLI tool
Mic92/nixos-shell avatar
Mic92/nixos-shell

nixos-shell puts a NixOS virtual machine in your terminal

Spawns lightweight nixos vms in a shell

922 stars49 forksNixMIT

At a glance

What is it?
nixos-shell builds a NixOS virtual machine from a vm.nix module in the current directory, boots it headless under QEMU, mounts your home directory and nix profile into it, and drops you into a console in the same terminal window. The interesting constraint is that the configuration it builds has to be overridable, which means a different rule for how you declare a NixOS system.
Who is it for?
Adopt nixos-shell when you want to reproduce a NixOS environment for a build or a test without touching the machine you work on, especially as a remote builder. Do not adopt it for anything that needs a graphical desktop without reading the graphics option, and do not expect it to work with a system configuration that is not overridable, because it aborts with a non-zero exit status in that case.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 3 days ago.
What is it written in?
Mainly Nix, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

A VM described by one small Nix module

The whole idea fits in a single file. nixos-shell spawns a headless QEMU virtual machine based on a vm.nix NixOS module in the current working directory, mounts $HOME and the user's nix profile into the virtual machine, and provides console access in the same terminal window. The example module is three lines.

nix
{ pkgs, ... }: {
  boot.kernelPackages = pkgs.linuxPackages_latest;
}

That is the entire configuration surface at its minimum. Everything else, the memory size, the CPU count, the disk size, the mounts, the graphics, comes from NixOS options that the tool injects and that you override in the same file. This is a different model from running a full virtual machine with a disk image you install once. There is no image to download and no installation step. The configuration is evaluated, the machine is built from the store, and the VM starts. For a developer who wants a clean NixOS environment to run a build in, that is the point of the tool. The nix profile mount is the detail that makes it usable rather than merely interesting. Because your profile is mounted in, the tools you have installed are available inside the VM, so you are not limited to the bare NixOS defaults. The package is available in nixpkgs, so installation is a matter of adding it to your environment rather than building from this repository.

extendModules, or the configuration has to be overridable

This is the constraint that catches people, and it is stated in a note in the README. nixos-shell must be able to extend the specified system configuration with certain modules. If your version of nixpkgs provides the extendModules function on system configurations, nixos-shell uses it to inject the required modules, and no additional work is needed on your part. If your version of nixpkgs does not provide extendModules, then you must make your system configurations overridable with lib.makeOverridable.

nix
{
  nixosConfigurations = let
    lib = nixpkgs.lib;
  in {
    vm = lib.makeOverridable lib.nixosSystem {
      # ...
    };
  };
}

And the failure mode is explicit: specifying a non-overridable system configuration will cause nixos-shell to abort with a non-zero exit status. That is a sensible design. The tool has to inject modules into your configuration to add the VM specific bits, and a configuration that is not a function cannot be extended. Rather than failing silently or half applying the modules, the tool refuses to start. If you are picking this up on an older nixpkgs, the fix is in your own flake, not in the tool. Wrapping the configuration in lib.makeOverridable is a small change, and the README shows exactly where it goes.

Flake outputs, attribute search order and layering local modules

Starting a VM is a single command.

console
$ nixos-shell

In that case nixos-shell reads vm.nix in the current directory. Instead of vm.nix it also accepts other modules on the command line.

console
$ nixos-shell some-nix-module.nix

The --flake flag starts a VM from a flake's nixosConfigurations or nixosModules output.

console
$ nixos-shell --flake github:Mic92/nixos-shell#vm-forward

The attribute search order is documented precisely, and it differs depending on whether you name an attribute. With no attribute given, nixos-shell tries packages.<system>.nixosConfigurations.<vm>, then nixosConfigurations.<vm>, then nixosModules.<vm>. When a name is given it tries the same three forms with that name in place of <vm>. That ordering is what makes the same tool work with a flake that exposes a full configuration, with one that exposes a bare module, and with one that packages a configuration under the current system. The --extra-module flag layers a local module on top of a flake defined base, and it can be passed more than once.

console
$ nixos-shell --flake github:Mic92/nixos-shell#vm --extra-module ./project.nix --extra-module ./private.nix

The same flag works without --flake, extending a plain vm.nix with additional modules.

console
$ nixos-shell vm.nix --extra-module ./private.nix

The layering is the practical part. You keep a base configuration in a flake and add the bits that are specific to your machine, which may well be secrets you do not want to publish.

Mounts use virtiofs, and the cache mode controls propagation

Your home directory and your nix profile are mounted in by default. Beyond that, extraMounts lets you mount anything else inside the virtual machine.

nix
{
  nixos-shell.mounts.extraMounts = {
    # simple USB stick sharing
    "/media" = /media;

    # override options for each mount
    "/var/www" = {
      target = ./src;
      readOnly = true; # default is false
    };
  };
}

The shape is a path on the guest mapped to a path on the host, either as a plain string or as an attribute set where target, readOnly and similar options can be set. The default for readOnly is false. The default mount settings can be changed too.

nix
{
  nixos-shell.mounts = {
    mountHome = false;
    mountNixProfile = false;
    cache = "never"; # default is "auto"
  };
}

The cache option is the interesting one, because it controls virtiofsd's caching behaviour and therefore how host changes propagate into the guest. The README lists never as no guest-side caching, meaning host changes are always immediately visible, and the default is auto. Shared directories are exported to the guest via virtiofs, which is the reason the propagation behaviour is a configuration knob rather than a fixed property. If you plan to use the machine as a remote builder, the recommendation in the README is to disable the tmpfs backing of the store, which the configuration option virtualisation.writableStoreUseTmpfs = false does.

Resources are conservative by default, and the disk option has a catch

The defaults are deliberately small, and the README lists each knob. Memory: by default QEMU will allow at most 500MB of RAM, and this can be increased using virtualisation.memorySize, in megabytes.

nix
{ virtualisation.memorySize = 1024; }

CPUs: to increase the CPU count use virtualisation.cores, which defaults to 1.

nix
{ virtualisation.cores = 2; }

Hard drive: to increase the size of the virtual hard drive, for example to 20GB, use virtualisation.diskSize, where the default is 512M.

nix
{ virtualisation.diskSize = 20 * 1024; }

Two caveats come with that last one. The option may not take effect until you delete the previous block device files created by QEMU, named nixos.qcow2. And changes in the nix store are written to an overlayfs backed by tmpfs rather than to the block device configured by diskSize, which you can turn off with virtualisation.writableStoreUseTmpfs = false. That tmpfs behaviour is not an accident of the implementation, it is the point. A store on tmpfs is fast and disposable, and the README recommends disabling it if you plan to use nixos-shell as a remote builder, where you want the work to survive in the block device. Other options covered in the README include virtualisation.graphics for graphical applications, networking.firewall.enable, since by default nixos-shell does not enable a firewall for the user's convenience, and booting with EFI through virtualisation.qemu.options with the OVMF firmware path.

Networking: user mode by default, with forwarding and bridge as opt in

Port forwarding is done through the standard NixOS option virtualisation.forwardPorts, and the README points at examples/vm-forward.nix where the ssh server running on port 22 inside the virtual machine is made accessible through port 2222 on the host. The same result can be had with an environment variable.

console
$ QEMU_NET_OPTS="hostfwd=tcp::2222-:22" nixos-shell

QEMU is started with user mode network by default, which needs no setup and is why port forwarding is expressed as a hostfwd rule. For bridge networking you set virtualisation.qemu.networkingOptions to something like [ "-nic bridge,br=br0,model=virtio-net-pci,mac=11:11:11:11:11:11,helper=/run/wrappers/bin/qemu-bridge-helper" ]. The helper path is NixOS specific and differs on other Linux distributions. There is a privilege consequence. QEMU needs to be installed on the host to get qemu-bridge-helper with the setuid bit set, otherwise you have to start the VM as root. On NixOS this is achieved with virtualisation.libvirtd.enable = true. SSH login uses your keys for passwordless login as root. At the moment only ~/.ssh/id_rsa.pub, ~/.ssh/id_ecdsa.pub and ~/.ssh/id_ed25519.pub are added automatically, and users.users.root.openssh.authorizedKeys.keyFiles is the way to add more. One caveat is called out in a note: sshd is not started by default, and it can be enabled with services.openssh.enable = true. To exit, type Ctrl-a x in the virtual machine, or run poweroff in the VM console, or switch to the QEMU console with Ctrl-a c and type quit.

MIT licensed, a 2.x line, and an example driven test suite

The licence file is License.md at the root of the tree and the project is MIT licensed. The releases are 2.2.0 on 2026-04-23, 2.1.0 on 2026-02-15 and 2.0.0 on 2024-08-14. The last push was on 2026-10-14 and the repository is not archived, so the tree is receiving work. The repository layout is small and purposeful: bin/ holds the nixos-shell script, share/ holds the modules that get injected, including share/modules/nixos-shell.nix and share/modules/nixos-shell-config.nix, and examples/ holds the reference configurations. There is a default.nix for the nix-build path, a flake.nix with a flake.lock, a version.txt and a Makefile. The Makefile is where the testing approach shows. The test targets are named after the example configurations, so test runs examples/vm.nix, test-resources runs examples/vm-resources.nix, test-forward, test-graphics, test-mounts and test-efi each run their matching file. There is even a test-broken target that expects the command to fail, using a flake attribute named BROKEN-DO-NOT-USE-UNLESS-YOU-KNOW-WHAT-YOU-ARE-DOING, which asserts the non-zero exit behaviour for a configuration that cannot be extended. The install target copies the script and the three share/ files into your prefix, which tells you the tool needs those modules present at runtime, not only the binary.

Editorial conclusion

Adopt nixos-shell when you want to reproduce a NixOS environment for a build or a test without touching the machine you work on, especially as a remote builder. Do not adopt it for anything that needs a graphical desktop without reading the graphics option, and do not expect it to work with a system configuration that is not overridable, because it aborts with a non-zero exit status in that case. Before you rely on it, check that your nixpkgs provides extendModules, and if it does not, wrap the configuration in lib.makeOverridable.

Frequently asked questions

How do I install nixos-shell?

It is available in nixpkgs, so adding the package to your environment is enough. The Makefile also has an install target that copies the nixos-shell binary and the modules from share/ into your prefix.

What does nixos-shell need from my NixOS configuration?

It must be able to extend the configuration with additional modules. If your nixpkgs provides extendModules on system configurations, nixos-shell injects what it needs automatically. Otherwise you must wrap the configuration in lib.makeOverridable, and a non-overridable configuration makes nixos-shell abort with a non-zero exit status.

How do I start a virtual machine from a flake?

Use the --flake flag, for example nixos-shell --flake github:Mic92/nixos-shell#vm-forward. Without an attribute name it tries packages.<system>.nixosConfigurations.<vm>, nixosConfigurations.<vm> and nixosModules.<vm> in that order.

How do I forward a port from the VM to the host?

Use the virtualisation.forwardPorts NixOS option, which the README illustrates with examples/vm-forward.nix mapping port 22 in the VM to port 2222 on the host. The same can be done with the QEMU_NET_OPTS environment variable, for example QEMU_NET_OPTS="hostfwd=tcp::2222-:22" nixos-shell.

How much memory and disk does a nixos-shell VM get by default?

QEMU is allowed at most 500MB of RAM by default, which you raise with virtualisation.memorySize in megabytes, and the virtual hard drive defaults to 512M, changed with virtualisation.diskSize. Note that diskSize may not take effect until the previous nixos.qcow2 block device files are deleted.

Official sources

  1. Issues
  2. License: MIT
  3. Mic92/nixos-shell on GitHub
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/mic92-nixos-shell.svg)](https://hysenlabs.com/projects/mic92-nixos-shell)

Was this useful?