NixOS server configuration and switch to Podman
Introduction
II was on the lookout for a lightweight Linux distro to replace Ubuntu, and I discovered NixOS. It’s clean and allows centralized configuration for users, groups, packages, and more. The ease of switching configurations and performing rollbacks is a game changer.
Additionally, I switched from Docker to Podman because of its open-source, daemonless architecture—read more about it here.
How To
Virtual Machine installation
Within Proxmox, I added NixOS as a VM by downloading the Graphical ISO image (Plasma Desktop, 64-bit Intel/AMD).
I uploaded the ISO to the Proxmox local storage, selected SHA-256 as the hash algorithm, and verified the checksum from the NixOS download site.
To place NixOS in the server VLAN, I adjusted the following settings within the node (pve):
- Select the node
 - Navigate to 
System>Network - Edit the Linux Bridge
 - Enable 
VLAN awareand clickOK - Enter the 
VLAN Tagin the network device settings of the NixOS VM (underHardware) 
Finally, I connected to the Plasma Desktop GUI via the console and completed the installation steps.
NixOS configuration
Here is the configuration.nix file located at /etc/nixos, with my additional custom configurations:
- 
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 exampleunstable.cockpitas package or just usecockpitto use the standard channel. I’ve made a separate note for this - 
cockpit-apps: Custom packages for Cockpit - 
fs.inotify settings: For optimization for Syncthing, among others. - 
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 - 
Extra
user options. This is self-evident - 
allow unfree packages: Could be useful (or not) - 
systempackages, including:openssl: Useful for generating certificatescifs-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
 - 
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 usecat /etc/current-system-packages - 
nix daemon config: Automatic garbage collection and store optimization. Very handy - 
openssh serviceis enabled - 
security.sudo.wheelNeedsPassword: Run sudo without a password. This may not always be recommended - 
services.fstrimis enabled. I was told this would be better for my SSD - 
services.qemuGuest.enable: Guest agent for Proxmox. Don’t forget to set theQEMU Guest AgenttoEnabledin theOptionsof the NixOS VM - 
cifs mount: So that I can access files on my fileserver that runs as a container within Proxmox. ReplaceIPand and enter your username and password in thesmb-secretsfile - 
services.cockpit: Cockpit is a web-based GUI to administer servers 
Podman specific
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.virtualisation: Podman is set as backend. I have set up the containers with separate nix files (for example Homer, Caddy, MariaDB and phpMyAdmin).systemd.services.create-podman-network: 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 replacexxwith my VLAN tag.
Desktop environment specific
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.environment.plasma5.excludePackages: Not all applications that come pre-installed with the KDE Plasma desktop environment are desirable for me.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 NixOS configurations
After modifying the configuration.nix, run sudo nixos-rebuild switch to switch between configurations. If it doesn’t work as desired, you can revert with sudo nixos-rebuild switch --rollback. You can read more about updating and upgrading NixOS here.
Each switch adds a generation, viewable with sudo nix-env -p /nix/var/nix/profiles/system --list-generations. You can delete old generations using sudo nix-env --profile /nix/var/nix/profiles/system --delete-generations old.
Full NixOS 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, ... }:
let# add unstable channel declaratively# https://stackoverflow.com/questions/48831392/how-to-add-nixos-unstable-channel-declaratively-in-configuration-nix  unstableTarball =    fetchTarball      https://github.com/NixOS/nixpkgs/archive/nixos-unstable.tar.gz;
  cockpit-apps = pkgs.callPackage packages/cockpit/default.nix { inherit pkgs; };in{  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.    ./hardware-configuration.nix
  (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_IDENTIFICATION = "nl_NL.UTF-8";    LC_MEASUREMENT = "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;  };
  # https://nixos.wiki/wiki/KDE  environment.plasma5.excludePackages = with pkgs.libsForQt5; [    elisa    gwenview    okular    oxygen    khelpcenter    konsole    plasma-browser-integration    print-manager  ];
  # 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 = "127.0.0.1";    userMappingXml = ./guacamole/user-mapping.xml;    package = pkgs.unstable.guacamole-server;  };
  services.guacamole-client = {    enable = true;    enableWebserver = true;    settings = {      guacd-port = 4822;      guacd-hostname = "127.0.0.1";    };    package = pkgs.unstable.guacamole-client;  };
  # Allow unfree packages - by default NixOS doesn't allow installing software with unfree licenses  nixpkgs.config.allowUnfree = true; #
  programs.firefox.enable = false; # Install firefox.
  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     htop     curl     openssl     cifs-utils     gnome.gedit # simple text editor to replace okular (KDE)
     unstable.cockpit     cockpit-apps.podman-containers     #cockpit-apps.virtual-machines # replaced by quickemu
     unstable.quickemu     unstable.quickgui  ];
  # this creates /etc/current-system-packages with a list of all packages and their version  # https://www.reddit.com/r/NixOS/comments/fsummx/how_to_list_all_installed_packages_on_nixos/  environment.etc."current-system-packages".text =    let      packages = builtins.map (p: "${p.name}") 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;  };
  # Uncomment when using the guest agent for proxmox or XCP-ng  # services.qemuGuest.enable = true; # Proxmox  # services.xe-guest-utilities.enable = true; # XCP-ng
  # Caddy  services.caddy.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\nhomer.home.arpa {\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/config.user.inc.php || 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;      };    };  };
  systemd.services.create-podman-network = 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 https://nixos.org/nixos/options.html).  system.stateVersion = "22.11"; # Did you read the comment?
}
No comments found for this note.
Join the discussion for this note on Github. Comments appear on this page instantly.