NixOS - Server Configuration and Switch to Podman

For some time now I have been looking for an interesting lightweight linux distribution that could replace Ubuntu.

Now I’ve found NixOS and I have no idea why it hadn’t crossed my path sooner. So far I am very happy with it, it feels very clean. With NixOS you can configure everything in one place. Think for example of users and groups, a few nix packages such as wget, htop, curl, openssl etc. Or, for example, mounting cifs directories or adding docker/podman containers. What makes this very powerful is that you can easily switch to a new configuration and possibly perform a rollback again.

I also decided to switch from Docker to Podman. Why? Because Podman is open source and has a daemonless architecture, you can read more about it here. This means that I will adjust my notes on this digital garden accordingly.

Installing NixOS with LUKS disk encryption is described in another note.


Within Proxmox I added NixOS as a VM by downloading the Graphical ISO image (Plasma Desktop, 64-bit Intel/AMD).

First I uploaded the ISO to the Proxmox local storage and selected SHA-256 as the hash algorithm. The checksum can be found next to the download button on the NixOS download site.

I also thought it would be nice to place NixOS in the new server VLAN. For that I had to adjust the following setting within the node (pve):

  1. Select the node
  2. Select System > Network
  3. Edit the Linux Bridge
  4. Turn ON VLAN aware and click OK You can then enter the VLAN Tag at the network device of the NixOS VM (under Hardware).

Then I connected to the Plasma Desktop GUI via the console and went through the installation steps.


Below is the configuration.nix that you can find under /etc/nixos. I have supplemented the default configuration with:

  1. unstableTarball: Add the unstable channel declaratively which can be useful if you need services or packages with a higher version. Then you can use for example unstable.cockpit as package or just use cockpit to use the standard channel. I’ve made a separate note for this.
  2. cockpit-apps: Custom packages for Cockpit.
  3. . fs.inotify settings: For optimization for Syncthing, among others.
  4. DHCP: For the main networking interface. The MAC address comes from the VM and is given a reserved static IP address by the Unifi gateway.
  5. Extra user options. This is self-evident.
  6. allow unfree packages: Could be useful (or not).
  7. systempackages, including:
    1. openssl: Useful for generating certificates.
    2. cifs-utils: For the cifs mounts. The package may not even need to be added here, but it is then only useful to have for the man pages.
  8. environment.etc."current-system-packages".text: After switching configurations, a file is created where you can easily find all installed system packages and the corresponding versions! Just use cat /etc/current-system-packages.
  9. nix daemon config: Automatic garbage collection and store optimization. Very handy.
  10. openssh service is enabled.
  11. security.sudo.wheelNeedsPassword: Run sudo without a password. This may not always be recommended.
  12. services.fstrim is enabled. I was told that this would be better for an SSD.

    To verify TRIM support run: lsblk --discard. And check the values of DISC-GRAN (discard granularity) and DISC-MAX (discard max bytes) columns. Non-zero values indicate TRIM support.

  13. services.qemuGuest.enable: Guest agent for Proxmox. Don’t forget to set the QEMU Guest Agent to Enabled in the Options of the NixOS VM.
  14. cifs mount: So that I can access files on my fileserver that runs as a container within Proxmox. Replace IP and and enter your username and password in the smb-secrets file.
  15. services.cockpit: Cockpit is a web-based GUI to administer servers
Podman specific
  1. system.activationScripts: This will run a script to create folders needed for Podman containers. It will also create a Caddyfile for Caddy and the configuration for phpMyAdmin.
  2. virtualisation: Podman is set as backend. I have set up the containers with separate nix files (for example Homer, Caddy, MariaDB and phpMyAdmin).
  3. This ensures that the Podman macvlan network I use for all containers is always created if it doesn’t exist. This is very useful, for example when installing a new NixOS VM. Within the IP addresses I replace xx with my VLAN Tag.
Desktop environment specific
  1. services.xserver: This will setup KDE Plasma as desktop environment. Because this is a server configuration, the default settings are sufficient for me as described here. I will use the terminal more often.
  2. ` environment.plasma5.excludePackages`: Not all applications that come pre-installed with the KDE Plasma desktop environment are desirable for me.
  3. services.xrdp: Very basic settings for XDRP server so I can use remote desktop to connect to the KDE desktop. Guacamole can be used to access the desktop environment and CLI with a web browser.
Switching between configurations

After modifying the configuration.nix you just need to run: sudo nixos-rebuild switch to switch between configurations. If it doesn’t work the way you want, you can go back with sudo nixos-rebuild switch --rollback. Here you can read more about updating and upgrading NixOS. Each time you switch, a generation is added that you can view with sudo nix-env -p /nix/var/nix/profiles/system --list-generations. With sudo nix-env --profile /nix/var/nix/profiles/system --delete-generations old you can delete all old generations.

# 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, ... }:

# add unstable channel declaratively
  unstableTarball =

  cockpit-apps = pkgs.callPackage packages/cockpit/default.nix { inherit pkgs; };
  disabledModules = [ "services/web-apps/guacamole-client.nix" "services/web-apps/guacamole-server.nix" ]; # disable the stable channel version
  imports =
  [ # Include the results of the hardware scan.

	(unstableTarball + "/nixos/modules/services/web-apps/guacamole-client.nix")
    (unstableTarball + "/nixos/modules/services/web-apps/guacamole-server.nix")

  nixpkgs.config = {
    packageOverrides = pkgs: {
      unstable = import unstableTarball {
        config = config.nixpkgs.config;

  # Bootloader.
  boot.loader.grub.enable = true;
  boot.loader.grub.device = "/dev/sda";
  boot.loader.grub.useOSProber = true;

  boot.kernel.sysctl = {
    # Note that inotify watches consume 1kB on 64-bit machines.
    "fs.inotify.max_user_watches"   = 1048576;   # default:  8192
    "fs.inotify.max_user_instances" =    1024;   # default:   128
    "fs.inotify.max_queued_events"  =   32768;   # default: 16384

  # Enable networking
  networking.hostName = "nixos"; # Define your hostname. 
  networking.networkmanager.enable = true;
  networking.interfaces.ens18.useDHCP = true;

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

  # Select internationalisation properties.
  i18n.defaultLocale = "en_US.UTF-8";

  i18n.extraLocaleSettings = {
    LC_ADDRESS = "nl_NL.UTF-8";
    LC_MONETARY = "nl_NL.UTF-8";
    LC_NAME = "nl_NL.UTF-8";
    LC_NUMERIC = "nl_NL.UTF-8";
    LC_PAPER = "nl_NL.UTF-8";
    LC_TELEPHONE = "nl_NL.UTF-8";
    LC_TIME = "nl_NL.UTF-8";

  # Configure xserver
  services.xserver = {
    enable = true;
    layout = "gb"; # keymap in X11
    xkbVariant = "";

    displayManager = {
      sddm.enable = true;
    desktopManager.plasma5.enable = true;

  environment.plasma5.excludePackages = with pkgs.libsForQt5; [

  # Remote desktop protocol
  services.xrdp = {
    enable = true;
    defaultWindowManager = "startplasma-x11";

  # Configure console keymap
  console.keyMap = "uk";

  # Define a user account. Don't forget to set a password with ‘passwd’.
  users.users.beheer = {
    isNormalUser = true;
    description = "Beheer";
    extraGroups = [ "networkmanager" "wheel" "podman" ];
    packages = with pkgs; [];
    home = "/home/beheer";
    createHome = true;

  # Remote desktop protocol
  services.xrdp = {
    enable = true;
    defaultWindowManager = "startplasma-x11";
    #openFirewall = true;

  # Guacamole
  services.guacamole-server = {
    enable = true;
    host = "";
    userMappingXml = ./guacamole/user-mapping.xml;
    package = pkgs.unstable.guacamole-server;

  services.guacamole-client = {
    enable = true;
    enableWebserver = true;
    settings = {
      guacd-port = 4822;
      guacd-hostname = "";
    package = pkgs.unstable.guacamole-client;

  # Allow unfree packages - by default NixOS doesn't allow installing software with unfree licenses
  nixpkgs.config.allowUnfree = true; # 

  environment.systemPackages = with pkgs; [
  #  vim # Do not forget to add an editor to edit configuration.nix! The Nano editor is also installed by default.
     gnome.gedit # simple text editor to replace okular (KDE)
     #cockpit-apps.virtual-machines # replaced by quickemu

  # this creates /etc/current-system-packages with a list of all packages and their version
  environment.etc."current-system-packages".text =
      packages = (p: "${}") config.environment.systemPackages;
      sortedUnique = builtins.sort builtins.lessThan (lib.unique packages);
      formatted = builtins.concatStringsSep "\n" sortedUnique;
    in formatted;

  security.sudo.wheelNeedsPassword = false;

  # Good for SSD
  services.fstrim = {
    enable = true;

  # Enable the guest agent for proxmox
  services.qemuGuest.enable = true;

  # Cockpit
  services.cockpit = {
    enable = true;
    port = 9090;
    settings = {
      WebService = {
        AllowUnencrypted = true;

  # Samba Client - cifs mount
  fileSystems."/mnt/fileserver/backup" = {
    device = "//IP/backup";
    fsType = "cifs";
    options = let
      # this line prevents hanging on network split
      automount_opts = "x-systemd.automount,noauto,x-systemd.idle-timeout=60,x-systemd.device-timeout=5s,x-systemd.mount-timeout=5s";
     in ["${automount_opts},credentials=/etc/nixos/smb-secrets,file_mode=0777,dir_mode=0777,iocharset=utf8"];

  # Create directories for the containers
  system.activationScripts = {
    script.text = ''
      # Homer
      install -d -m 755 /home/beheer/homer/assets -o root -g root
      # Caddy
      install -d -m 755 /home/beheer/caddy/site -o root -g root
      install -d -m 755 /home/beheer/caddy/data -o root -g root
      install -d -m 755 /home/beheer/caddy/config -o root -g root
      test -f /home/beheer/caddy/Caddyfile || echo -e "#{\n#       debug\n#}\n\ {\n        tls internal\n        handle {\n                reverse_proxy 192.168.xx.xx:8080\n        }\n}\>
      # MariaDB
      install -d -m 755 /home/beheer/mariadb/config -o root -g root
      install -d -m 755 /home/beheer/mariadb/data -o root -g root
      # phpMyAdmin
      install -d -m 755 /home/beheer/phpmyadmin -o root -g root
      test -f /home/beheer/phpmyadmin/ || echo -e "<?php\n\n\$cfg['ShowPhpInfo'] = true; // Adds a link to phpinfo() on the home page\n\$cfg['SendErrorReports'] = 'never';" > /home/beh>

  virtualisation = {
    podman = {
      enable = true;
      # Create a `docker` alias for podman, to use it as a drop-in replacement
      dockerCompat = true;
      # Required for containers under podman-compose to be able to talk to each other.
      defaultNetwork.settings.dns_enabled = true; # release 23.05
      # defaultNetwork.dnsname.enable = true; # use with older releases

    oci-containers = {
      backend = "podman";

      containers = {
        homer = import ./containers/homer.nix;
        caddy = import ./containers/caddy.nix;
        mariadb = import ./containers/mariadb.nix;
        phpmyadmin = import ./containers/phpmyadmin.nix;
  }; = with config.virtualisation.oci-containers; {
    serviceConfig.Type = "oneshot";
    wantedBy = [ "${backend}-homer.service" "${backend}-caddy.service" ];
    script = ''
      ${pkgs.podman}/bin/podman network exists net_macvlan || \
        ${pkgs.podman}/bin/podman network create --driver=macvlan --gateway=192.168.xx.1 --subnet=192.168.xx.0/24 -o parent=ens18 net_macvlan

  # Nix daemon config
  nix = {
    # Automate garbage collection
    gc = {
      automatic = true;
      dates = "weekly";
      options = "--delete-older-than 7d";

    settings = {
      # Automate `nix store --optimise`
      auto-optimise-store = 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
  system.stateVersion = "22.11"; # Did you read the comment?


Read other notes


    No comments found for this note.

    Join the discussion for this note on this ticket. Comments appear on this page instantly.


    Notes mentioning this note

    • NixOS - Cockpit Setup
      Cockpit is a modern web-based graphical interface for servers. You can use it to administer servers and it has a...

    Notes Graph