Skip to content

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):

  1. Select the node
  2. Navigate to System > Network
  3. Edit the Linux Bridge
  4. Enable VLAN aware and click OK
  5. Enter the VLAN Tag in the network device settings of the NixOS VM (under Hardware)

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:

  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:

    • openssl: Useful for generating certificates
    • 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 this would be better for my SSD

  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. 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 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 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

/etc/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?
}

Favorites

Comments

    No comments found for this note.

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

    Copyright 2021- Fiction Becomes Fact