DEV Community

Cover image for 100 days of NixOS
domagoj miskovic
domagoj miskovic

Posted on • Updated on

100 days of NixOS

NixOS operating system is a GNU/Linux distribution based on the Nix Package manager. NixOS is a Linux distribution same as Ubuntu is a Linux distribution. A distribution itself is a customized collection of software and tools packaged together with the Linux kernel. The kernel itself acts as a layer between the user-space where processes or programs live and the hardware, memory, cpu, devices and network devices that programs need to access to do stuff and move bits and bytes around.

How does the Nix package manager work and what is so special about the Nix package manager? Why should one care about it? There is a lovely intro with nice pictures about Nix and it's ecosystem on https://nixos.org/explore.html

This first day of 100 days of code has a bit of story telling and then some installation details that will come a bit after. I will try to express some things I feel inspired about. I am a beginner and have very limited knowledge and experience of NixOS system except exploring it for less than a year with breaks. So this purely comes from my own experience and the love I feel for its design principles and the declarative approach.

NixOS system together with the Nix package manager and the Nix programming language is a provisioning kind of operating environment. What does this really mean? Essentially, to put it simply, you are not supposed to actually do anything. You are supposed to define your actions using a lazy functional domain specific language called Nix to do the stuff for you. You write the wishes and instructions as functional code declarations or recipes and then the system does what you ask it to do. Now, the real difference lies in the fact that you are not simply writing down the commands, automating the steps that you would have to do by hand like in other distros or even on Windows. With Nix you are writing what and not how. You are actually using a functional language to express the way packages are built, the way your entire system will be. How cool is that?

Yes, there are package managers like Ubuntu’s apt-get and apt through which we install software by directly interacting with the manager, executing commands in real time such as sudo apt install some-program and sudo apt remove that program. But what actually happens when something goes wrong? How do you go back? What happens when your version of program does not play well with the rest of the system? What happens when something breaks and you wish you go back? What happens when you simply would like to try out something within a container like environment? With other package managers I am manually entering the commands. Even with an Ansible like playbook I would still have to write apt install. I would still have to write the how.

With Nix you are defining the system and the system then takes care of the commands and the how.

For example, how do you declare a new user into the system? What is a user in this case?

A User is a human being who operates a computer in the real world. If a user writes and compiles a computer program, the program, inhabiting the micro-civilization in the computer world, often looks exactly like his or her user counterpart in the real world, though exceptions exist for data pushers and programs written by multiple users. Users normally communicate with their programs by speaking or typing commands into their computers; on the other side of the screen, programs receive these impulses as "calls" which summon them to I/O towers to trade information with the other world. Many programs regard their users, both those who write programs and those who simply use them, as akin to gods, while others have been known to reject, deny, or attempt to manipulate them.

# Define a user account. Don't forget to set a password with ‘passwd’.
  users.users.dom = {
    createHome = true;
    isNormalUser = true;
    extraGroups = [ "wheel" "video" "audio" "disk" "networkmanager" ]; # Enable ‘sudo’ for the user.
    group = "users";
    home = "/home/dom";
    initialPassword = "nixos";
  };
Enter fullscreen mode Exit fullscreen mode

And here we have a user:

user

Ok I love Tron and I fight for the users. Now,
this user declaration is from the configuration.nix file that defines my current freshly installed NixOS system. It is a simple bare bones configuration file written in a text editor. The beauty is I can write it all there, all the apps, services, the users, the settings, and so on, and the nixos will rebuild the system with these specifications. I just want to boot into a usable system and then work from there.

There are many complex nix configuration setups and some of those are simply amazing, integrating git actions, binary caches, home manager, but I wanted this time go from the vanilla beginning again.

So in any other Linux system you might add a user with:

# useradd -m -G additional_groups -s login_shell username

Yes, in Ubuntu maybe that is not needed, maybe you could click something and click your way through and setup another user. In Windows it is similar. There are some graphical interfaces from where you create a user. But with nix you simply declare it in a file as code, and then the system creates the user for you.

There is no work to be done except thinking about it and writing it down. You are also not learning or going through a variety of ways of how one can create, what one needs to click and so on, no, you are simply declaring what you want. This is very profound. There are much more complex examples like setting a database server with a user and installing for example python too. But again, one is declaring that without writing commands like install this. This is simply profound. This code can then be shared and reproduced with other users without worrying of the current state of other systems.


Here I begin again the 100 days of NixOS challenge. My previous attempt was at https://hackernoon.com/100-days-of-code-death-of-summer-on-the-island-of-nixos-gd1a3ws8 and also I documented some of my explorations on my github page at https://dooygoy.github.io/nixos-zoo/

Then I became a parent a year and a half ago and so after a while my laptop ended up with a windows 10 installed instead. The background is that I am not an IT professional, or as Big Pun would maybe have said it, I am not a player, I just love IT a lot.

Big Pun

Learning about parenting was hard at times, so I have stopped exploring Haskell and categories, and NixOS too after being with it for less than a year, but I came back just now. What changed? Well I learned as a parent that it is OK to learn in small bits, to find time when there is some, and keep learning without stressing how there is not much time.

NixOS ecosystem is a huge time investment but no worry, it is worth it, and while learning it, it might just happen that some other things become more clear and less confusing. The same thing happens with Haskell and learning about category theory. Although I ended up not actually implementing anything in Haskell, and most of my time I just wrote simple bits of code and tried to visualize functional structures, recently when I read some python code it made much sense to me, it seemed simple if I may say. Generally, learning functional programming helps in thinking about processes in a relaxed, space-like abstract level. You are not so concerned with the implementation but the meaning of it all, and what are the things that make the problem at hand. Something similar happens with NixOS. NixOS does seem hard and has a steep learning curve, but it also has a huge payoff in terms of understanding what is actually happening with the system. With Archlinux I was involved with the configuration of the system but it seemed more manual to me at times. I didn't automate anything back then but the NixOS invitation seemed much more appealing, something struck me the first time I glimpsed into the depths of the Nix ocean. Instant rollbacks to any previous time generation, rolling a virtual machine in an instant and bunch of other things I do not even understand yet.

Once you taste the fully declarative approach of the Nix package manager that makes the NixOS, it comes to your mind from time to time this provisional idea, and then it erases all your darling distributions and operating systems that you interacted with before. It comes to your mind when you download a phone app, or need to reinstall a package, or need to wait for a windows update that tries to talk you too. Suddenly, with NixOS, it is all there in the open, all expressed as nix expressions.

Well, what I really mean to say is that NixOS is something you secretly longed for if you ever spent time tinkering with archlinuxes and windowses of the world, but you still don’t know it. There is this feeling once you get the subtle idea of NixOS you realize, oh my, I can’t go back after this. I would only fool myself.

And now I stop the romantic ouverture and begin with the concrete steps I made during the installation.

  • Since I am on Windows 10 I use the program called Rufus to create a USB bootable stick with NixOS 21.11 GNOME edition. I burn it in dd mode.

  • I disable secure boot option in BIOS setup since this is still not fully resolved with NixOS.

  • NixOS boots into Gnome desktop environment! Yay! My external wifi usb dongle D-Link DWA-121 is instantly recognized. Awesome! I can connect to my wifi and begin the installation.

  • I start GParted and erase all windows partitions. Then I exit the program.

  • I check my disks with the lsblk command in the terminal and there it shows my disk.

  • I used to have NixOS with zfs but I haven't learned anything about the filesystem. This time I go for the encrypted Btrfs root and unecrypted 10G of swap partition and a boot partition. I follow this nixos on encrypted btrfs. This time I would like to learn more about filesystems too.

  • I am surprised at how fast the installation goes, I follow all the steps and then I simply add a user and some minor settings. I nixos-install and then it boots right into NixOS! Wow!

  • But now, now the adventure truly begins!

Here is my simple configuration.nix


    # Edit this configuration file to define what should be installed on
    # your system.  Help is available in the configuration.nix(5) man page
    # and in the NixOS manual (accessible by running ‘nixos-help’).

    { config, pkgs, ... }:

    {
      imports =
        [ # Include the results of the hardware scan.
          ./hardware-configuration.nix
        ];

      # Use the systemd-boot EFI boot loader.
      boot.loader.systemd-boot.enable = true;
      boot.loader.efi.canTouchEfiVariables = true;

      nixpkgs.config.allowUnfree = true;

      networking.hostName = "lapchi"; # Define your hostname.
      # networking.wireless.enable = true;  # Enables wireless support via wpa_supplicant.
      networking.networkmanager.enable = true;

      # Set your time zone.
      time.timeZone = "Europe/Zagreb";

      # The global useDHCP flag is deprecated, therefore explicitly set to false here.
      # Per-interface useDHCP will be mandatory in the future, so this generated config
      # replicates the default behaviour.
      networking.useDHCP = false;
      networking.interfaces.wlp0s20f0u2.useDHCP = true;

      # Configure network proxy if necessary
      # networking.proxy.default = "http://user:password@proxy:port/";
      # networking.proxy.noProxy = "127.0.0.1,localhost,internal.domain";

      # Select internationalisation properties.
      i18n.defaultLocale = "en_US.UTF-8";
      console = {
        font = "ter-i32b";
        packages = with pkgs; [ terminus_font ];
        keyMap = "us";
      };

      # Enable the X11 windowing system.
      services.xserver.enable = true;
      services.xserver.libinput.enable = true;

      # Enable the GNOME Desktop Environment.
      services.xserver.displayManager.gdm.enable = true;
      services.xserver.desktopManager.gnome.enable = true;

      # Configure keymap in X11
      services.xserver.layout = "us";
      # services.xserver.xkbOptions = "eurosign:e";

      # Enable CUPS to print documents.
      # services.printing.enable = true;

      # Enable sound.
      sound.enable = true;
      hardware.pulseaudio.enable = true;

      # Enable touchpad support (enabled default in most desktopManager).
      # services.xserver.libinput.enable = true;

      # Define a user account. Don't forget to set a password with ‘passwd’.
      users.users.dom = {
        createHome = true;
        isNormalUser = true;
        extraGroups = [ "wheel" "video" "audio" "disk" "networkmanager" ]; # Enable ‘sudo’ for the user.
        group = "users";
        home = "/home/dom";
        initialPassword = "nixos";
      };

      # List packages installed in system profile. To search, run:
      # $ nix search wget
      environment.systemPackages = with pkgs; [
        vim # Do not forget to add an editor to edit configuration.nix! The Nano editor is also installed by default.
        wget
        firefox
        tree unzip unrar
      ];

      # Some programs need SUID wrappers, can be configured further or are
      # started in user sessions.
      # programs.mtr.enable = true;
      # programs.gnupg.agent = {
      #   enable = true;
      #   enableSSHSupport = true;
      # };

      # List services that you want to enable:

      # Enable the OpenSSH daemon.
      # services.openssh.enable = true;

      # Open ports in the firewall.
      # networking.firewall.allowedTCPPorts = [ ... ];
      # networking.firewall.allowedUDPPorts = [ ... ];
      # Or disable the firewall altogether.
      # networking.firewall.enable = false;

      # This value determines the NixOS release from which the default
      # settings for stateful data, like file locations and database versions
      # on your system were taken. It‘s perfectly fine and recommended to leave
      # this value at the release version of the first install of this system.
      # Before changing this value read the documentation for this option
      # (e.g. man configuration.nix or on https://nixos.org/nixos/options.html).
      system.stateVersion = "21.11"; # Did you read the comment?

    }

Enter fullscreen mode Exit fullscreen mode

Top comments (0)