Building a Philosophy Workstation with NixOS: Learning Home Manager and Configuring Sway with Wayland

A NixOS tutorial on using Network Manager, Home Manager, and setting up a Graphical Environment with Wayland and Sway

Oil painting of a man writing a letter using a quill.
Man Writing a Letter, by Gabriel Metsu. Image Source: Wikimedia Commons.

This tutorial is Part I in a series on configuring a default Minimal installation of NixOS to a state where productive philosophy can be done. We will start from a text-only environment that has minimal amenities (e.g. where the previous installation guide left off), and slowly build up our configuration.nix file until we have a graphical user interface, as well as all the applications that are conductive towards producing philosophy. In this series, we will complete the following:

  • Configure networking using NetworkManager (e.g nmcli)
  • Install low-level systems applications (e.g. git, wget, parted, and vim)
  • Learn about and use Home Manager to manage our user-level applications.
  • Install and configure the Sway display manager with the Wayland display server
  • Fine tune the Sway graphical environment, enabling buttons for brightness and volume control, and enabling audio (Part II).
  • Install and configure VSCodium, LaTeX, Firefox, and other tools needed to produce philosophy (Part II).

This tutorial is designed for the philosophy student who's familiar and comfortable with Linux in general, but new to NixOS. It may also be suitable for Computer Science students and Software Developers, with some modifications. It will walk you through the process step-by-step and provide ample links and explanations.

The ontology of this tutorial is dialectical in nature. Hence we will not provide a complete configuration (which would only be true opinion, and not knowledge) – but take the reader step-by-step through the process of building a NixOS workstation.

Installing and Using NetworkManager for WiFi Connections

The first, and one of the most important steps of any Linux installation, is to ensure that we have WiFi connectivity. After all, the Internet is one of the most direct embodiments of our Mitsein!

Adding NetworkManager to configurations.nix

There are many ways to manage Internet connectivity in Linux. We will use NetworkManager, a high-level command line interface that simplifies the process – as well as comes with many GUI interfaces which we can add later on. In your /etc/nixos/configuration.nix, add the appropriate options in the first level of nested brackets (right after imports and account configuration).

{ config, pkgs, ... }:

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

  # ...
  
  # Networking Configuration
  networking.hostName = "my-philosophy-workstation"; # Define your hostname.
  networking.networkmanager.enable = true;
  # networking.networkmanager.wifi.backend = "iwd";

  # 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.eno0.useDHCP = true;
  networking.interfaces.wls6.useDHCP = true;
};

The default configuration.nix file.

We will enable DHCP on both of our wired en0 as well as wireless wls6 interfaces – these options should already be there from your original installation.

Note how all of our options (e.g. such as hostname, networkmanager.enable, etc) are within the networking namespace. This makes sense, as they all relate to configuring the networking of your NixOS installation. Do you wonder what other options are available? NixOS has a wonderful options search on their website, where you can look up any options. This is a good resource to keep in mind as you progress through this guide!

Now run a quick rebuild to allow NixOS to install NetworkManager.

sudo nixos-rebuild --switch

Command used to "rebuild" the system after a configuration change.

Now Network Manager should be available on the terminal via the nmcli command. If you run sudo nmcli, you should receive a colorful status output, where it lists various devices that are available (such as ethernet, wifi, and the odd loopback interface).

Adding the networkmanager group to our User Account

Is using sudo (or root) a neccessary condition for accessing nmcli? Or is the need privilege escalation only contingent on our account's lack of permission, which can be remedied in other ways? In fact, you can add the networkmanager group to your user account, so you can access nmcli without needing elevated privileges. Simply modify the extraGroups directive in your users.users.[username] option, and add networkmanager as a group for your account:

  users.users.myUserAccount = {
    isNormalUser = true;
    shell = pkgs.zsh;
    extraGroups = [ "wheel" "networkmanager"];
  };

Connecting to Wifi using nmcli: Practical Steps

Now our configuration of NetworkManager is complete. All that remains is to learn the practical steps of connecting to wifi. I'll guide you through the commands that I use to connect to WiFI – but keep in mind that you'll always have more certain references at hand, through nmcli --help and man nmcli.

Scan for WiFi networks in a new location:

nmcli device wifi rescan

List available WiFi networks (these are the ones you just scanned!):

nmcli device wifi list

You should now see a colorful array of WiFI names (SSIDs), as well as their signal strength. Find the name of the network you wish to connect to, and then type:

nmcli device wifi connect WiFiName --ask

NetworkManager should ask for your password, and now you should be successfully connected to the network. You can test your connectivity using nmcli networking connectivity check, or even just by pinging a nearby server (i.e. ping 8.8.8.8).

Why do we invoke the command with the option --ask? Check out the Linux manual entry for nmcli in order to find out!

man nmcli

NetworkManager auto-magically saves all of your WiFi connections. In the future, when you return to a location where you have connected before, you don't need to enter the credentials again. Simply run the following in order to list all your previous connections:

nmcli connection show

And then run:

nmcli connection up <WiFi Name>

NetworkManager should actually connect to known networks automatically – the above instructions are useful as a backup.

Connecting to Enterprise, Education, and Other Networks: A Complication

The above workflow works well for simple networks, namely the sort that you'll find at a coffee shop, or a friend's apartment. That's because they utilise a form of authentication called WPA-Personal (or WPA2, WP3). That's the type of WiFi network where you only need to enter a password.

Unfortunately, most Universities, Colleges, and Institutions of Philosophy utilise a more sophisticated WiFi authentication method, called WPA-Enterprise. This is the type that requires an username and password, or even additional settings like domain.

NetworkManager supports these more complicated types of connections, but you'll need to setup a connection profile. I'll write a seperate guide detailing this more involved procedure at a later date.

(If you really need help with this step, feel free to email me, and I'll let you know how I made my connection profile)

Installing Low-level Systems Applications

Congratulations! If you have successfully completed the previous steps, you should be able to access the internet. We're still in the command line right now, but we can make it more friendly, by installing some familiar tools. Right after your networking configuration, add the following stanza:

  # List packages installed in system profile.
  environment.systemPackages = with pkgs; [
    vim
    parted
    git
    wget
  ];

Installing packages in the system profile

After a quick sudo nixos-rebuild --switch, these additional packages should become installed, and available system-wide. Now all users will get to access vim, parted, git, and wget. This means we'll have a powerful editor available in the command line, as well as a way to check out git repositories and download files. Any number of programs can be installed on Nix this way! In fact, the NixOS website has a search function for packages analogous to the options search. It's a good way to find out what programs are available!

Introduction to Home Manager

But wait, is it really a good idea to install all programs on a system-wide basis? Perhaps there are programs that should only be available for the user who installs it. Likewise, many of the more advanced programs like git or firefox comes with a dizzying array of configuration options that are personal (such as setting an account name or email address). Is there any way we can install and configure programs only for the individual user?

The NixOS ecosystem does provide a powerful tool for managing settings and programs (in other words, an environment) on a per-user basis. This tool is called Home Manager, and it has many sophisticated uses. For our purpose, we will only use it to manage programs and configuration files for our user. Here's how to use Home Manager in your configurations.nix.

Installing and Configuring Home Manager

First, install Home Manager following the instructions in it's manual. As with both Computing and Philosophy, we always follow upstream sources. You wouldn't read someone else's opinion about a Platonic dialogue, would you? After adding the Home Manager channel, we can then modify our configurations.nix accordingly, adding the following directives:

  # Begin home-manager directives
  home-manager.useUserPackages = true;
  home-manager.useGlobalPkgs = true;
 
  home-manager.users.myUserAccount = { pkgs, ... }: {
    # Everything inside here is managed by Home Manager!
    
    programs.home-manager = {
      enable = true;
    };
  };

Loading Home Manager

Why do we set home-manager.useUserPackages and home-manager.useGlobalPkgs to true? Check out the reasoning in the Home Manager documentation to find out. You may set them both to false, if you like – Philosophy is practiced by contrarians, after all.

You should have noticed by now that all directives in your configurations.nix file have different levels of scope, sort of like a domain name or Aristotelian category. That's because NixOS configuration is written in a programming language called the Nix Expression Language. It's closely related to Lisp and Scheme, which are other programming languages that are very keen on brackets, parantheses, and Philosophy. In practical terms, this means that you can group together, and re-arrange the Nix expressions as you wish. The above configuration can also be written like this, for example:

  # Begin home-manager directives
  home-manager = {
  	useUserPackages = true;
    useGlobalPackages = true;
    users.users.myUserAccount = { pkgs, ... }: {
      # Everything inside here is managed by Home Manager!
      programs.home-manager = {
        enable = true;
      };
      
    };
  };

As you work on and build up your configurations.nix, keep this perculiar property in mind. It'll help you re-order your configuration files as you amass more options in time.

How to use Home Manager to Install and Configure Programs for your User

Now that we've setup NixOS to use home-manager, we can begin installing programs and configuring their settings on a per-user basis. Make sure to have rebuilt your system (sudo nixos-rebuild --switch!), and follow along as we add some programs with Home Manager. Keep in mind that the following directives will be added within the home-manager.users.users.myUserAccount brackets (i.e. where the # Everything inside here is managed by Home Manager! comment lies). I'll let you know once we add files in the other bracket (where we originally installed the systems packages).

Installing using home.packages

As far as I can tell, there are two ways to install programs using Home Manager. Some programs like fortune or gnumake are simple, low-level, and do not have configuration options associated with them. These programs are installed just like the system-wide packages we installed earlier, but they will only be available for our current user:

home.packages = with pkgs; [ 
  fortune
  gnumake
];

Installing user-specific packages with Home Manager

From my observations, it seems that we use home.packages to install these programs, because they have not been yet "wrapped up" by Home Manager contributors in a form that exposes all of their settings. These programs might be so simple and low-level that they don't have any options to be made available in the first place.

Installing via a program directive

For more sophisticated programs, we install them by declaring a program directive, where we state that they are enabled (enable = true;). I do not know why a priori some programs are available this way, and others not – but assume it is because the Home Manager contributors have not packaged everything in this manner yet.

    programs.git = {
      # Install git
      enable = true;
      
      # Additional options for the git program
      package = pkgs.gitAndTools.gitFull; # Install git wiith all the optional extras
      userName = "myGitUsername";
      userEmail = "myGitEmailAddress@users.noreply.github.com";
      extraConfig = {
        # Use vim as our default git editor
        core.editor = "vim";
        # Cache git credentials for 15 minutes
        credential.helper = "cache";
      };
    };

Configuring user-specific packages with Home Manager

As you can see, programs that have been packaged this way have all of their configuration options exposed. This means we can use Home Manager to manage their settings for us. This is very powerful, because if you configure your programs with all their customisations using Home Manager, then you can replicate your setup anywhere by simply copying your configurations.nix file and running a sudo nixos-rebuild --switch. Imagine making a very fancy, sophisticated setup – and even putting your configuration file into version control. Wouldn't that be a dream?

Putting our two examples together, this is how the Home Manager snippet of your configurations.nix would look like, with both git and some packages installed:

  # Begin home-manager directives
  home-manager.useUserPackages = true;
  home-manager.useGlobalPkgs = true;
 
  home-manager.users.myUserAccount = { pkgs, ... }: {
    # Everything inside here is managed by Home Manager!
    programs.home-manager = {
      enable = true;
    };
    programs.git = {
      enable = true;      
      # Additional options for the git program
      package = pkgs.gitAndTools.gitFull; # Install git wiith all the optional extras
      userName = "myGitUsername";
      userEmail = "myGitEmailAddress@users.noreply.github.com";
      extraConfig = {
        # Use vim as our default git editor
        core.editor = "vim";
        # Cache git credentials for 15 minutes
        credential.helper = "cache";
      };
    };
    home.packages = with pkgs; [ 
      fortune
      gnumake
    ];
  };

The configurations.nix file with Home Manager

Remember to rebuild you configuration, using nixos-rebuild --switch!

Finding More Home Manager Programs

How do we know what programs can be installed using the programs.[name].enable directive? For a given program like program.git, how do we find all of it's options? Regular NixOS has it's options search, and package search, as we've seen earlier. Does Home Manager possess a similar search function?

Yes, it does! There is the Home Manager Options Search, a community project maintained by other Home Manager users. It works the same way like the options and package search for regular NixOS.

Home Manager's manual also has an appendix which lists all the programs that it has packaged, along with their options. You can visit the link above and take a look! It's not as easy to navigate, but you can simply use Ctrl+F to search within the page.

For any new program that you want to install in your Home Manager, a good workflow seems to be:

  1. Search via the Options Search or look through the manual appendix, to see if it's available as a packaged program that you can enable, and set it's configuration options.
  2. If it is not within the Appendix, find it in NixOS's packages search, and then simply add it in the home.packages list.

I developed this workflow through a process of a trial and error. I'm still very new to NixOS myself, and this guide documents my understanding of Nix – I may be wrong in regards to best practices. If you know of a better way to find programs for Home Manager, please let me know!

Now you should be reasonably confident in adding new programs using Home Manager. Why not add tmux, and neovim? Here's how they might look like – this snippet is copied out of my configuration, but don't let my settings inform yours – check out their options in the Home Manager appendix and modify them your own way!

    programs.tmux = {
      enable = true;
      keyMode = "vi";
      shortcut = "a";
      terminal = "screen-256color";
      clock24 = true;
      shell = "/etc/profiles/per-user/myUserAccount/bin/zsh";
    };

    programs.neovim = {
      enable = true;
      vimAlias = true;
    }; 

Likewise, I installed the following packages. Do you recognise them?

    home.packages = with pkgs; [ 
      fortune 
      file
      htop
      exa
      zenith
      neofetch
      irssi
      gnumake
    ];

With all of these, you should have a pretty comfortable command-line environment with NixOS. Make sure to run nixos-rebuild --switch!

Installing and Configuring Sway with Wayland

Now that we are confident with configuring NixOS on the command line, let's move on towards installing and building a graphical environment. There are as many schools of Desktop environments as there are Philosophy – but for this tutorial, we will go with Sway. Sway is a tiling window manager that uses the Wayland display server.

Adding a display server and window manager to our NixOS installation would give us a graphical user interface, and allow us to take our first steps away from the analytic consoles and command-prompts of yesteryear – and move into the brave new world of Postmodernism!

There are various steps involved here. Many of which I have sourced from different guides and mailing-lists. I admit I do not understand all the configuration – unfortunately, I can only offer the appearance of knowledge here, and not knowledge itself. If my explanations are lacking in any way, or you have some clarifications – please reach out and let me know.

Enable Hardware Support for Wayland

First, we must enable some hardware support options for Wayland and Sway. OpenGL is a graphics API that our hardware uses to render accelerated graphics. If we do not enable this option, we will run into a bug and Sway will not run.

Please note that these directives go into the top-level (i.e. OUTSIDE) of your home-manager bracket. Place them on the same level as your other system-wide configuration.

  # Hardware Support for Wayland Sway
  hardware = {
    opengl = {
      enable = true;
      driSupport = true;
    };
  };

XDG Integration

Next, also in the top-level we must add the following XDG directives. XDG is a standard originating from freedesktop.org which formalises a set of common variables and methods for desktop applications to interact with each other. The following settings enable what's called a XDG Portal, which improves communication between bundled flatpak apps, and Wayland.

  xdg = {
    portal = {
      enable = true;
      extraPortals = with pkgs; [
        xdg-desktop-portal-wlr
        xdg-desktop-portal-gtk
      ];
    };
  };

Adding packages to HomeManager in Support of Sway

Now, it is time to go to the HomeManager level and add some packages that Sway will need to use. I found the list of these packages from the NixOS Wiki page on Sway, which you should also review as it contains additional information.

    home.packages = with pkgs; [ 
      # ...
      # All of the below is for sway
      swaylock
      swayidle
      wl-clipboard
      mako
      alacritty
      wofi
      waybar
    ];

Each of these packages serve a specific purpose.

  • swaylock is a idle screen locker.
  • swayidle is an idle timer.
  • wl-clipboard allows you to copy-paste (i.e. a clipboard).
  • mako is a "notification daemon," which creates little floating popups when you receive messages.
  • alacritty is a rust-based terminal. This is very important, as it is the terminal that you'll be using within the Sway graphical environment.
  • wofi is an application launch menu. It's like the dash on Mac OS, where you get to search for the name of a program, and run it. wofi is a Wayland descendent of rofi, which was a descendent of dmenu. Isn't Open Source interesting?
  • waybar is a status bar. Ultimately I did not use it, but uncomment it for your own configuration

Please be very careful with swaylock right now. If you run it in the command line, you will get locked out of your computer. Even if you type your password correctly, it will not let you in. That's because swaylock must be properly configured to accept your password and authenticate to your computer using PAM, which is like an API but for authentication. In order to properly get swaylock working, add the following directive to your system-wide configuration (i.e. not inside home-manager!):

  # Allow swaylock to unlock the computer for us
  security.pam.services.swaylock = {
    text = "auth include login";
  };

Later on, once you are inside Sway – you'll be able to lock your computer by running swaylock on any terminal!

Adding and Configuring Sway with Wayland as a Program in Home Manager

Now we are ready to add and configure sway with Wayland. The instructions here are sourced from the NixOS Sway wiki page, once more. The following snippet goes inside your Home Manager (i.e. on the same level as programs.git).

This is a lot of configuration, and it's not easy to understand at first. So just like with reading Hegel, we'll go through it line-by-line and unpack it.

    # Use sway desktop environment with Wayland display server
    wayland.windowManager.sway = {
      enable = true;
      wrapperFeatures.gtk = true;
      # Sway-specific Configuration
      config = {
        terminal = "alacritty";
        menu = "wofi --show run";
        # Status bar(s)
        bars = [{
          fonts.size = 15.0;
          # command = "waybar"; You can change it if you want
          position = "bottom";
        }];
        # Display device configuration
        output = {
          eDP-1 = {
            # Set HIDP scale (pixel integer scaling)
            scale = "1";
	      };
	    };
      };
      # End of Sway-specificc Configuration
    };

Configuring the sway desktop environment with wayland

First, we enable sway. We also enable the wrapperFeatures for gtk, because it lets applications decorate their windows using the gtk toolkit, which is a common library used to make GUIs for programs.

Next, we begin the sway-specific configuration in config. Sway has a large number of configuration options, all of which are documented in the Home Manager manual's appendix. If you have time, I highly recommend browsing it – but the following three are the most important:

We set the default terminal as alacritty, otherwise you'll be faced with a horribly ugly default terminal. We'll also set the application launcher menu to wofi. We specifically call wofi with the --show run option, since that's for listing applications and running them. wofi has plenty of other options, such as --show ssh for starting an SSH menu – but that's not what we need. The bars option is a list [ ] of multiple brackets { }, each of which contains the configuration for a separate status bar. We will only need one status bar, which will be located towards the bottom of the screen. My configuration does not use waybar, but you are welcome to uncomment it for yours.

Finally, we set some settings for our laptop's default display eDP-1. Specifically, we set the pixel-scaling factor to 1. If you have a high-resolution screen, you may wish to set it to 2 (so that the text is not too small).

Adding and Configuring Alacritty with Themes

Finally, the last thing we need to do is to configure our graphical terminal emulator, alacritty. Although the configuration looks difficult at first, you can see that it is all entirely contained within the settings brackets. The options are documented in Home Manager's manual, once again.

    # Rust-based terminal emulator
    programs.alacritty = {
      enable = true;
      settings = {
        env.TERM = "alacritty";
        window = {
          decorations = "full";
          title = "Alacritty";
          dynamic_title = true;
          class = {
            instance = "Alacritty";
            general = "Alacritty";
          };
        };
        font = {
          normal = {
            family = "monospace";
            style = "regular";
          };
          bold = {
            family = "monospace";
            style = "regular";
          };
          italic = {
            family = "monospace";
            style = "regular";
          };
          bold_italic = {
            family = "monospace";
            style = "regular";
          };
          size = 14.00;
        };
        colors = {
          primary = {
            background = "#1d1f21";
            foreground = "#c5c8c6";
          };
        };
      };
    };

Configuring alacritty, a rust-based terminal emulator for Sway and Wayland.

The most important setting here is probably the size setting – it determines how big the text is in the terminal. If you have a high-resolution screen, the default size will probably be too small. As you build up your configuration.nix file, this would be a good place to theme and make a really nice-looking terminal.

Rebuilding NixOS and Running Sway

Now everything should be all done! If your configuration file is correct (make sure that the top-level and home-manager-level directives are in their proper places), you should be able to rebuild your system and run now.

sudo nixos-rebuild --switch

After this is done, I recommend restarting your computer, just in case.

sudo reboot now

After logging in (making sure to select the newest version of you NixOS install from the boot manager), you will be greeted once again by a command line environment. But don't fear! All you need to do now is to run:

sway

And you should be in a graphical environment! Congratulations, you have successfully transcended the command prompt, and reached a transcendental aesthetic!

Help! I'm Stuck! How do I... get around in Sway?!

If you're like me, and you have never used a tiling window manager before, you probably have no idea how to get around. At all. And even worse, when you search for "sway documentation", you'll find no instructions on how to control the Window Manager!

Fortunately, Sway is based on i3, a venerable tiling Manager hailing from the Archlinux days. And it has both excellent documentation on it's project site, as well as a quick-reference cheat sheet to help you get around. Print out a copy and keep it with you – you'll become an expert in tiling window managers in no time!

Conclusion of Part I

You should now have a graphical environment in NixOS, that you put together yourself. It's still a very bare and basic space, but should serve as a good foundation for further customisation. In Part II of this guide, we will take this foundation and build upon it further. Specifically, we will install and configure FireFox together, as well as other applications like Element (Matrix Client) and Signal Desktop. I will also show you how to configure audio using the Pipewire service, and most importantly – install LaTeX and VSCodium.

I hope you enjoyed my tutorial. If you have any questions or comments, feel free to contact me – I would love to know if my guide has helped you in any way. I also write about Philosophy in my own time – let me know of your thoughts!