
Declarative and Functional
NixOS is a Linux distribution built around two key principles:
- Declarative system configuration (or, more accurately, state) description.
- Functional package manager that allows rollbacks and parallel application installations.
Unlike other distributions, NixOS doesn’t require users to go through a lengthy series of steps to set up the system they want: installing the system, bootloader, and packages, adding users, configuring settings, and so on.
Instead, NixOS suggests defining the desired state of the system in a special configuration file. This file lists everything from the packages to whether SSH login with a password is enabled. Then, you just need to run a single command, and regardless of the current state of the system, the package manager will align it with the specified requirements.
In other words, if you need a system with Apache, PHP, MySQL, SSH, and some additional configurations, you simply describe everything in a single configuration file and then execute a command to deploy the system. Whether it’s a freshly installed OS or an existing one, you’ll get an identical system with the same set of packages and configurations.
All of this is made possible by the Nix package manager. In traditional Linux distributions, a package manager spreads a package’s files across the system during installation: executables in /
, libraries in /
, and other components in /
. As a result, you can end up with issues like leftover orphan files from failed updates or removals, dependency nightmares (such as when two applications require different versions of /
), and a high risk of breaking the entire system with a botched update.
The Nix package manager places all installed packages in their own subdirectories within the /
directory. For example, the installed Git package will be located in a directory like /
, where the set of numbers is a hash generated from the build environment of the package: source files, dependency tree, compiler flags, and more. This allows Nix to install not only two different versions of the same application simultaneously but also two distinct builds.
Thanks to its ability to install different versions and builds of packages while keeping them separate from system directories, NixOS solves almost all the common issues faced by traditional package managers—from system inconsistency after a failed update to dependency hell. This mechanism also allows users to roll back the system to a previous state and create multiple different profiles (snapshots) of the system, which can be switched between without rebooting the machine. Want to turn your home computer into a server with a single command? That’s not a problem with NixOS. You can even take a NixOS configuration file to another machine and deploy an identical system with the exact same set of packages.
NixOS allows software to be installed not just by the root user, but also by regular users; in this case, the package will be installed in the user’s home directory. It also features a built-in garbage collector that automatically removes all dependency packages if they are no longer needed by anyone.
Installation
NixOS doesn’t have an installer, but if you’ve ever installed Arch Linux, you shouldn’t have any issues. Start by downloading the latest version of NixOS from the official website and create a bootable USB drive with it.
$ wget https://d3g5gsiof5omrk.cloudfront.net/nixos/18.03/nixos-18.03.133192.45f52f765cd/nixos-graphical-18.03.133192.45f52f765cd-x86_64-linux.iso
$ sudo dd if=nixos-graphical-18.03.133192.45f52f765cd-x86_64-linux.iso of=/dev/sdX
Then restart the machine and boot from the USB drive. NixOS will greet you with a command-line prompt.
The first thing we need to do is prepare the disk for installation. The easiest way to do this is by using ‘parted’ (in this example, we create one large ext3 partition on a disk with DOS-style partitioning).
$ parted /dev/sda
(parted) mklabel msdos
(parted) mkpart primary ext4 0% 100%
(parted) quit
We will be installing the system on an encrypted drive, so we’ll start by initializing the encryption:
$ export NIXOS_NAME="name" # Choose your own name$ cryptsetup luksFormat /dev/sda1
$ cryptsetup luksOpen /dev/sda1 ${NIXOS_NAME}
Next, mount the disk to the /mnt directory:
$ mkfs.ext4 -L ${NIXOS_NAME} /dev/mapper/${NIXOS_NAME}
$ mount /dev/mapper/${NIXOS_NAME} /mnt
Now, let’s update the repositories:
$ nix-channel --update
And we generate default configuration files:
$ nixos-generate-config --root /mnt
The command will save two files to disk: configuration.
and hardware-configuration.
. The first one is the system state description file that we will work with moving forward. The second file doesn’t need to be changed—it’s generated automatically based on the hardware on which NixOS is installed.
Finally, we install the system and reboot:
$ nixos-install

Configuration.nix
The configuration.
file is the core of the distribution. It allows users to specify the entire desired or necessary configuration (state) of the system, from users and packages to fonts, and it can be modified at any time. The system will appear exactly as the user defines it in this file.
Of course, you could take the easy route and find a ready-made configuration on GitHub to use. However, this isn’t very beneficial in the long run because eventually, you’ll need to understand it all yourself. Plus, you risk downloading something undesirable instead of a proper configuration.
To apply the configuration changes, you need to execute the command
$ nixos-rebuild switch
and restart.
Before that, you can test the configuration’s functionality with the command
$ nixos-rebuild test

Choosing a Bootloader
Choose life, choose family, choose a bootloader.
If you’ve installed the system on a disk with DOS partitioning (as described in the article), you need to add the following lines to configuration.
:
# Enable GRUBboot.loader.grub.enable = true;# We need GRUB 2boot.loader.grub.version = 2;# The bootloader should be installed on /dev/sdaboot.loader.grub.device = "/dev/sda";# Enable support for encrypted disksboot.loader.grub.enableCryptodisk = true;
For UEFI systems, just two lines are sufficient:
boot.loader.systemd-boot.enable = true;# Allow EFI to make changesboot.loader.efi.canTouchEfiVariables = true;
Hostname and Timezone Setup
Here’s the basic idea:
networking.hostName = "mymachine";
time.timeZone = "Europe/Moscow";
Let’s go ahead and activate NetworkManager as well:
networking.networkmanager.enable = true;
Creating Users
Let’s add a user named vasya:
users.users.vasya = {
isNormalUser = true;
# Additional information description = "Userov User";
# Groups the user belongs to extraGroups = [ "networkmanager" ];
};
By default, the user will not have a password, so immediately after logging in under their username, you should run the passwd
command to set a password.
Installing Packages
Frequently used applications like htop, Thunderbird, and Vim can be installed without much hassle. You simply list what you want to install, save it, execute nixos-rebuild
, and you’re good to go.
environment.systemPackages = with pkgs; [ htop thunderbird vim]
These packages can be installed not only by listing their names in the configuration file but also by using a command.
$ nix-env -iA "package_name"
The command can be executed with or without root privileges. In the first scenario, the package is installed system-wide and becomes available to all users, whereas in the second one, it’s only available to the user who installed it. Regardless, all packages are stored in /
, so if two users install the same package independently, it is downloaded only once, and users access it via symbolic links.
Symbolic links are not overwritten during the reinstallation or update of a package. This allows users to install multiple versions of the same packages without any conflicts.
Package names can sometimes be tricky, so it’s best to verify their spelling here.

Installing Proprietary Packages
Working with proprietary packages in NixOS isn’t straightforward. You can allow any proprietary packages by using the command export
, but it’s better to only permit the ones you actually need while keeping all others restricted. Here’s an example of how to do that:
{ allowUnfreePredicate = (pkg: elem (builtins.parseDrvName pkg.name).name [ "package_name_1" "package_name_2" ]);}
GUI
Like any other Linux distribution, NixOS allows you to choose your graphical user interface (GUI). You can achieve this with a setup similar to the following:
services.xserver = {
# Enable the X server enable = true;
# Select the desktop environment desktopManager.gnome3.enable = true;
# and/or the window manager windowManager.i3.enable = true;
};
Additional Configurations
NixOS offers a wide range of configuration options that can be specified in the configuration.nix file. You can learn about them using the nixos-option
command:
$ nixos-option
This attribute set contains:
assertions
boot
containers
ec2
environment
fileSystems
fonts
gnu
gtkPlugins
hardware
...
But these are just sections of the settings; you can dig even deeper:
$ nixos-option services
This attribute set contains:
accounts-daemon
acpid
activemq
...
And even deeper:
$ nixos-option services.openssh
This attribute set contains:
allowSFTP
authorizedKeysFiles
challengeResponseAuthentication
enable
extraConfig
You can also use the online options search tool.

Rollbacks
Just like with packages, configurations are not overwritten. Instead, new ones are created to allow easy switching between them and painless rollbacks. All the versions of the file applied with the nixos-rebuild
command are stored in GRUB. During boot, you can select not the default or latest version, but any preferred one, and subsequently set it as the default.

The command allows you to view a list of available configurations.
$ ls -l /nix/var/nix/profiles/system-*-link
You can roll back to the previous configuration without rebooting by using a command.
$ nixos-rebuild switch --rollback
or to an even earlier team version
$ /nix/var/nix/profiles/system-N-link/bin/switch-to-configuration switch
where N
is the version number of the config. To load this config by default, use the command
$ /run/current-system/bin/switch-to-configuration boot
Updates
NixOS can be updated via channels. A channel functions similarly to a package repository. Just like repositories, channels can be stable or unstable (for the adventurous). There are also small channels—essentially server editions of repositories that offer rapid security updates and a limited selection of server-only software for the x86_64 platform. You can view available channels at this link.
To switch to a channel, only two commands are needed:
$ nix-channel --add link
$ nixos-rebuild switch --upgrade
Updates can be scheduled to download automatically. To do this, simply add a line to the configuration file.
system.autoUpgrade.enable = true;
To ensure that package updates are compatible with the system version, a specific line of code is also needed.
system.stateVersion = "18.03";
where 18.
is the system version.
Conclusions
Despite its unconventional nature, NixOS is quite user-friendly: it doesn’t interfere with your work, avoids cluttering your system, and has a strong focus on security. With time, you get used to its quirks, and using NixOS as a daily desktop operating system can be very convenient.

2022.01.12 — First contact. Attacks against contactless cards
Contactless payment cards are very convenient: you just tap the terminal with your card, and a few seconds later, your phone rings indicating that…
Full article →
2022.04.04 — Fastest shot. Optimizing Blind SQL injection
Being employed with BI.ZONE, I have to exploit Blind SQL injection vulnerabilities on a regular basis. In fact, I encounter Blind-based cases even more frequently…
Full article →
2022.06.01 — Cybercrime story. Analyzing Plaso timelines with Timesketch
When you investigate an incident, it's critical to establish the exact time of the attack and method used to compromise the system. This enables you to track the entire chain of operations…
Full article →
2022.02.15 — First contact: How hackers steal money from bank cards
Network fraudsters and carders continuously invent new ways to steal money from cardholders and card accounts. This article discusses techniques used by criminals to bypass security…
Full article →
2023.02.13 — First Contact: Attacks on Google Pay, Samsung Pay, and Apple Pay
Electronic wallets, such as Google Pay, Samsung Pay, and Apple Pay, are considered the most advanced and secure payment tools. However, these systems are also…
Full article →
2022.02.09 — F#ck da Antivirus! How to bypass antiviruses during pentest
Antiviruses are extremely useful tools - but not in situations when you need to remain unnoticed on an attacked network. Today, I will explain how…
Full article →
2022.02.09 — Kernel exploitation for newbies: from compilation to privilege escalation
Theory is nothing without practice. Today, I will explain the nature of Linux kernel vulnerabilities and will shown how to exploit them. Get ready for an exciting journey:…
Full article →
2022.02.09 — Dangerous developments: An overview of vulnerabilities in coding services
Development and workflow management tools represent an entire class of programs whose vulnerabilities and misconfigs can turn into a real trouble for a company using such software. For…
Full article →
2022.06.01 — Log4HELL! Everything you must know about Log4Shell
Up until recently, just a few people (aside from specialists) were aware of the Log4j logging utility. However, a vulnerability found in this library attracted to it…
Full article →
2022.01.12 — Post-quantum VPN. Understanding quantum computers and installing OpenVPN to protect them against future threats
Quantum computers have been widely discussed since the 1980s. Even though very few people have dealt with them by now, such devices steadily…
Full article →