Configuring NixOS This chapter describes how to configure various aspects of a NixOS machine through the configuration file /etc/nixos/configuration.nix. As described in , changes to that file only take effect after you run nixos-rebuild.
Package management This section describes how to add additional packages to your system. NixOS has two distinct styles of package management: Declarative, where you declare what packages you want in your configuration.nix. Every time you run nixos-rebuild, NixOS will ensure that you get a consistent set of binaries corresponding to your specification. Ad hoc, where you install, upgrade and uninstall packages via the nix-env command. This style allows mixing packages from different Nixpkgs versions. It’s the only choice for non-root users. The next two sections describe these two styles.
Declarative package management With declarative package management, you specify which packages you want on your system by setting the option . For instance, adding the following line to configuration.nix enables the Mozilla Thunderbird email application: environment.systemPackages = [ pkgs.thunderbird ]; The effect of this specification is that the Thunderbird package from Nixpkgs will be built or downloaded as part of the system when you run nixos-rebuild switch. You can get a list of the available packages as follows: $ nix-env -qaP '*' --description nixos.pkgs.firefox firefox-23.0 Mozilla Firefox - the browser, reloaded ... The first column in the output is the attribute name, such as nixos.pkgs.thunderbird. (The nixos prefix allows distinguishing between different channels that you might have.) To “uninstall” a package, simply remove it from and run nixos-rebuild switch.
Customising packages Some packages in Nixpkgs have options to enable or disable optional functionality or change other aspects of the package. For instance, the Firefox wrapper package (which provides Firefox with a set of plugins such as the Adobe Flash player) has an option to enable the Google Talk plugin. It can be set in configuration.nix as follows: nixpkgs.config.firefox.enableGoogleTalkPlugin = true; Unfortunately, Nixpkgs currently lacks a way to query available configuration options. Apart from high-level options, it’s possible to tweak a package in almost arbitrary ways, such as changing or disabling dependencies of a package. For instance, the Emacs package in Nixpkgs by default has a dependency on GTK+ 2. If you want to build it against GTK+ 3, you can specify that as follows: environment.systemPackages = [ (pkgs.emacs.override { gtk = pkgs.gtk3; }) ]; The function override performs the call to the Nix function that produces Emacs, with the original arguments amended by the set of arguments specified by you. So here the function argument gtk gets the value pkgs.gtk3, causing Emacs to depend on GTK+ 3. (The parentheses are necessary because in Nix, function application binds more weakly than list construction, so without them, environment.systemPackages would be a list with two elements.) Even greater customisation is possible using the function overrideDerivation. While the override mechanism above overrides the arguments of a package function, overrideDerivation allows changing the result of the function. This permits changing any aspect of the package, such as the source code. For instance, if you want to override the source code of Emacs, you can say: environment.systemPackages = [ (pkgs.lib.overrideDerivation pkgs.emacs (attrs: { name = "emacs-25.0-pre"; src = /path/to/my/emacs/tree; })) ]; Here, overrideDerivation takes the Nix derivation specified by pkgs.emacs and produces a new derivation in which the original’s name and src attribute have been replaced by the given values. The original attributes are accessible via attrs. The overrides shown above are not global. They do not affect the original package; other packages in Nixpkgs continue to depend on the original rather than the customised package. This means that if another package in your system depends on the original package, you end up with two instances of the package. If you want to have everything depend on your customised instance, you can apply a global override as follows: nixpkgs.config.packageOverrides = pkgs: { emacs = pkgs.emacs.override { gtk = pkgs.gtk3; }; }; The effect of this definition is essentially equivalent to modifying the emacs attribute in the Nixpkgs source tree. Any package in Nixpkgs that depends on emacs will be passed your customised instance. (However, the value pkgs.emacs in nixpkgs.config.packageOverrides refers to the original rather than overriden instance, to prevent an infinite recursion.)
Adding custom packages It’s possible that a package you need is not available in NixOS. In that case, you can do two things. First, you can clone the Nixpkgs repository, add the package to your clone, and (optionally) submit a patch or pull request to have it accepted into the main Nixpkgs repository. This is described in detail in the Nixpkgs manual. In short, you clone Nixpkgs: $ git clone git://github.com/NixOS/nixpkgs.git $ cd nixpkgs Then you write and test the package as described in the Nixpkgs manual. Finally, you add it to environment.systemPackages, e.g. environment.systemPackages = [ pkgs.my-package ]; and you run nixos-rebuild, specifying your own Nixpkgs tree: $ nixos-rebuild switch -I nixpkgs=/path/to/my/nixpkgs The second possibility is to add the package outside of the Nixpkgs tree. For instance, here is how you specify a build of the GNU Hello package directly in configuration.nix: environment.systemPackages = let my-hello = with pkgs; stdenv.mkDerivation rec { name = "hello-2.8"; src = fetchurl { url = "mirror://gnu/hello/${name}.tar.gz"; sha256 = "0wqd8sjmxfskrflaxywc7gqw7sfawrfvdxd9skxawzfgyy0pzdz6"; }; }; in [ my-hello ]; Of course, you can also move the definition of my-hello into a separate Nix expression, e.g. environment.systemPackages = [ (import ./my-hello.nix) ]; where my-hello.nix contains: with <nixpkgs> {}; # bring all of Nixpkgs into scope stdenv.mkDerivation rec { name = "hello-2.8"; src = fetchurl { url = "mirror://gnu/hello/${name}.tar.gz"; sha256 = "0wqd8sjmxfskrflaxywc7gqw7sfawrfvdxd9skxawzfgyy0pzdz6"; }; } This allows testing the package easily: $ nix-build my-hello.nix $ ./result/bin/hello Hello, world!
Ad hoc package management With the command nix-env, you can install and uninstall packages from the command line. For instance, to install Mozilla Thunderbird: $ nix-env -iA nixos.pkgs.thunderbird If you invoke this as root, the package is installed in the Nix profile /nix/var/nix/profiles/default and visible to all users of the system; otherwise, the package ends up in /nix/var/nix/profiles/per-user/username/profile and is not visible to other users. The flag specifies the package by its attribute name; without it, the package is installed by matching against its package name (e.g. thunderbird). The latter is slower because it requires matching against all available Nix packages, and is ambiguous if there are multiple matching packages. Packages come from the NixOS channel. You typically upgrade a package by updating to the latest version of the NixOS channel: $ nix-channel --update nixos and then running nix-env -i again. Other packages in the profile are not affected; this is the crucial difference with the declarative style of package management, where running nixos-rebuild switch causes all packages to be updated to their current versions in the NixOS channel. You can however upgrade all packages for which there is a newer version by doing: $ nix-env -u '*' A package can be uninstalled using the flag: $ nix-env -e thunderbird Finally, you can roll back an undesirable nix-env action: $ nix-env --rollback nix-env has many more flags. For details, see the nix-env1 manpage or the Nix manual.
User management NixOS supports both declarative and imperative styles of user management. In the declarative style, users are specified in configuration.nix. For instance, the following states that a user account named alice shall exist: users.extraUsers.alice = { createHome = true; home = "/home/alice"; description = "Alice Foobar"; extraGroups = [ "wheel" ]; isSystemUser = false; useDefaultShell = true; openssh.authorizedKeys.keys = [ "ssh-dss AAAAB3Nza... alice@foobar" ]; }; Note that alice is a member of the wheel group, which allows her to use sudo to execute commands as root. Also note the SSH public key that allows remote logins with the corresponding private key. Users created in this way do not have a password by default, so they cannot log in via mechanisms that require a password. However, you can use the passwd program to set a password, which is retained across invocations of nixos-rebuild. A user ID (uid) is assigned automatically. You can also specify a uid manually by adding uid = 1000; to the user specification. Groups can be specified similarly. The following states that a group named students shall exist: users.extraGroups.students.gid = 1000; As with users, the group ID (gid) is optional and will be assigned automatically if it’s missing. Currently declarative user management is not perfect: nixos-rebuild does not know how to realise certain configuration changes. This includes removing a user or group, and removing group membership from a user. In the imperative style, users and groups are managed by commands such as useradd, groupmod and so on. For instance, to create a user account named alice: $ useradd -m alice The flag causes the creation of a home directory for the new user, which is generally what you want. The user does not have an initial password and therefore cannot log in. A password can be set using the passwd utility: $ passwd alice Enter new UNIX password: *** Retype new UNIX password: *** A user can be deleted using userdel: $ userdel -r alice The flag deletes the user’s home directory. Accounts can be modified using usermod. Unix groups can be managed using groupadd, groupmod and groupdel.
File systems You can define file systems using the configuration option. For instance, the following definition causes NixOS to mount the Ext4 file system on device /dev/disk/by-label/data onto the mount point /data: fileSystems."/data" = { device = "/dev/disk/by-label/data"; fsType = "ext4"; }; Mount points are created automatically if they don’t already exist. For , it’s best to use the topology-independent device aliases in /dev/disk/by-label and /dev/disk/by-uuid, as these don’t change if the topology changes (e.g. if a disk is moved to another IDE controller). You can usually omit the file system type (), since mount can usually detect the type and load the necessary kernel module automatically. However, if the file system is needed at early boot (in the initial ramdisk) and is not ext2, ext3 or ext4, then it’s best to specify to ensure that the kernel module is available.
LUKS-encrypted file systems NixOS supports file systems that are encrypted using LUKS (Linux Unified Key Setup). For example, here is how you create an encrypted Ext4 file system on the device /dev/sda2: $ cryptsetup luksFormat /dev/sda2 WARNING! ======== This will overwrite data on /dev/sda2 irrevocably. Are you sure? (Type uppercase yes): YES Enter LUKS passphrase: *** Verify passphrase: *** $ cryptsetup luksOpen /dev/sda2 crypted Enter passphrase for /dev/sda2: *** $ mkfs.ext4 /dev/mapper/crypted To ensure that this file system is automatically mounted at boot time as /, add the following to configuration.nix: boot.initrd.luks.devices = [ { device = "/dev/sda2"; name = "crypted"; } ]; fileSystems."/".device = "/dev/mapper/crypted";
X Window System The X Window System (X11) provides the basis of NixOS’ graphical user interface. It can be enabled as follows: services.xserver.enable = true; The X server will automatically detect and use the appropriate video driver from a set of X.org drivers (such as vesa and intel). You can also specify a driver manually, e.g. services.xserver.videoDrivers = [ "r128" ]; to enable X.org’s xf86-video-r128 driver. You also need to enable at least one desktop or window manager. Otherwise, you can only log into a plain undecorated xterm window. Thus you should pick one or more of the following lines: services.xserver.desktopManager.kde4.enable = true; services.xserver.desktopManager.xfce.enable = true; services.xserver.windowManager.xmonad.enable = true; services.xserver.windowManager.twm.enable = true; services.xserver.windowManager.icewm.enable = true; NixOS’s default display manager (the program that provides a graphical login prompt and manages the X server) is SLiM. You can select KDE’s kdm instead: services.xserver.displayManager.kdm.enable = true; The X server is started automatically at boot time. If you don’t want this to happen, you can set: services.xserver.autorun = false; The X server can then be started manually: $ systemctl start display-manager.service
NVIDIA graphics cards NVIDIA provides a proprietary driver for its graphics cards that has better 3D performance than the X.org drivers. It is not enabled by default because it’s not free software. You can enable it as follows: services.xserver.videoDrivers = [ "nvidia" ]; You may need to reboot after enabling this driver to prevent a clash with other kernel modules. On 64-bit systems, if you want full acceleration for 32-bit programs such as Wine, you should also set the following: service.xserver.driSupport32Bit = true;
Touchpads Support for Synaptics touchpads (found in many laptops such as the Dell Latitude series) can be enabled as follows: services.xserver.synaptics.enable = true; The driver has many options (see ). For instance, the following enables two-finger scrolling: services.xserver.synaptics.twoFingerScroll = true;
Networking
Secure shell access Secure shell (SSH) access to your machine can be enabled by setting: services.openssh.enable = true; By default, root logins using a password are disallowed. They can be disabled entirely by setting services.openssh.permitRootLogin to "no". You can declaratively specify authorised RSA/DSA public keys for a user as follows: users.extraUsers.alice.openssh.authorizedKeys.keys = [ "ssh-dss AAAAB3NzaC1kc3MAAACBAPIkGWVEt4..." ];
IPv4 configuration By default, NixOS uses DHCP (specifically, dhcpcd) to automatically configure network interfaces. However, you can configure an interface manually as follows: networking.interfaces.eth0 = { ipAddress = "192.168.1.2"; prefixLength = 24; }; (The network prefix can also be specified using the option subnetMask, e.g. "255.255.255.0", but this is deprecated.) Typically you’ll also want to set a default gateway and set of name servers: networking.defaultGateway = "192.168.1.1"; networking.nameservers = [ "8.8.8.8" ]; Statically configured interfaces are set up by the systemd service interface-name-cfg.service. The default gateway and name server configuration is performed by network-setup.service. The host name is set using : networking.hostName = "cartman"; The default host name is nixos. Set it to the empty string ("") to allow the DHCP server to provide the host name.
IPv6 configuration IPv6 is enabled by default. Stateless address autoconfiguration is used to automatically assign IPv6 addresses to all interfaces. You can disable IPv6 support globally by setting: networking.enableIPv6 = false;
Firewall NixOS has a simple stateful firewall that blocks incoming connections and other unexpected packets. The firewall applies to both IPv4 and IPv6 traffic. It can be enabled as follows: networking.firewall.enable = true; You can open specific TCP ports to the outside world: networking.firewall.allowedTCPPorts = [ 80 443 ]; Note that TCP port 22 (ssh) is opened automatically if the SSH daemon is enabled (). UDP ports can be opened through . Also of interest is networking.firewall.allowPing = true; to allow the machine to respond to ping requests. (ICMPv6 pings are always allowed.)
Wireless networks NixOS will start wpa_supplicant for you if you enable this setting: networking.wireless.enable = true; NixOS currently does not generate wpa_supplicant's configuration file, /etc/wpa_supplicant.conf. You should edit this file yourself to define wireless networks, WPA keys and so on (see wpa_supplicant.conf(5)). If you are using WPA2 the wpa_passphrase tool might be useful to generate the wpa_supplicant.conf. $ wpa_passphrase ESSID PSK > /etc/wpa_supplicant.conf After you have edited the wpa_supplicant.conf, you need to restart the wpa_supplicant service. $ systemctl restart wpa_supplicant.service
Ad-hoc configuration You can use to specify shell commands to be run at the end of network-setup.service. This is useful for doing network configuration not covered by the existing NixOS modules. For instance, to statically configure an IPv6 address: networking.localCommands = '' ip -6 addr add 2001:610:685:1::1/64 dev eth0 '';
Linux kernel You can override the Linux kernel and associated packages using the option . For instance, this selects the Linux 3.10 kernel: boot.kernelPackages = pkgs.linuxPackages_3_10; Note that this not only replaces the kernel, but also packages that are specific to the kernel version, such as the NVIDIA video drivers. This ensures that driver packages are consistent with the kernel. The default Linux kernel configuration should be fine for most users. You can see the configuration of your current kernel in /run/booted-system/kernel-modules/config. If you want to change the kernel configuration, you can use the feature (see ). For instance, to enable support for the kernel debugger KGDB: nixpkgs.config.packageOverrides = pkgs: { linux_3_4 = pkgs.linux_3_4.override { extraConfig = '' KGDB y ''; }; }; extraConfig takes a list of Linux kernel configuration options, one per line. The name of the option should not include the prefix CONFIG_. The option value is typically y, n or m (to build something as a kernel module). Kernel modules for hardware devices are generally loaded automatically by udev. You can force a module to be loaded via , e.g. boot.kernelModules = [ "fuse" "kvm-intel" "coretemp" ]; If the module is required early during the boot (e.g. to mount the root file system), you can use : boot.initrd.extraKernelModules = [ "cifs" ]; This causes the specified modules and their dependencies to be added to the initial ramdark. Kernel runtime parameters can be set through , e.g. boot.kernel.sysctl."net.ipv4.tcp_keepalive_time" = 120; sets the kernel’s TCP keepalive time to 120 seconds. To see the available parameters, run sysctl -a.