Background

I’ve been experimenting with NixOS and running Nix on macOS, and I’ve been documenting some of the problems and solutions I’ve encountered in the process.

NixOS

Global configuration

Global configuration paths for NixOS: /etc/nixos/configuration.nix and /etc/nixos/hardware-configuration.nix.

Apply the updated global configuration.

1
2
3
nixos-rebuild switch
# or
nixos-rebuild switch --upgrade

Updating major releases

If you want to update NixOS 21.11 to 22.05:

1
2
3
nix-channel --list
nix-channel --add https://nixos.org/channels/nixos-22.05 nixos
nixos-rebuild switch --upgrade

Consider changing or not changing system.stateVersion in /etc/nixos/configuration.nix.

Common configurations

Commonly used NixOS configurations.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
# Enable XFCE
services.xserver.enable = true;
services.xserver.desktopManager.xfce.enable = true;

# System wide packages
environment.systemPackages = with pkgs; [
  xxx
];

# Fish shell
programs.fish.enable = true;
users.users.xxx = {
  shell = pkgs.fish;
}

# Command not found
programs.command-not-found.enable = true;

# Steam gaming
nixpkgs.config.allowUnfree = true;
programs.steam.enable = true;

# NOPASSWD for sudo
security.sudo.wheelNeedsPassword = false;

# QEMU guest
services.qemuGuest.enable = true;
services.spice-vdagentd.enable = true;

# XRDP
services.xrdp.enable = true;
services.xrdp.defaultWindowManager = "xfce4-session";
services.xrdp.openFirewall = true;

# OpenSSH server
services.openssh.enable = true;

# Udev rules for Altera USB Blaster
services.udev.packages = with pkgs; [
  usb-blaster-udev-rules
];

Home Manager

Home Manager describes the program that users see by default, while NixOS is configured for all users.

Configuration files

Configuration file: ~/.config/nixpkgs/home.nix

Application configuration file.

1
home-manager switch

Commonly used configurations

Commonly used Home Manager configurations.

1
2
3
4
5
6
7
# Allow unfree
nixpkgs.config.allowUnfree = true;

# User wide packages
home.packages = with pkgs; [
  xxx
];

Overriding Dependency Versions

Set the JDK version that the JVM program depends on.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
# Maven with java 11
home.packages = with pkgs; [
  (maven.override { jdk = jdk11; })
];

# Many packages with the same JDK
home.packages = 
  let java = pkgs.jdk11; in
  with pkgs; [
    (maven.override { jdk = java; })
    (sbt.override { jre = java; })
  ];

The specific naming of parameters depends on the beginning of the corresponding package on nixpkgs.

Utilities

nixpkgs-fmt

nixpkgs-fmt is used to format Nix code.

search.nixos.org

search.nixos.org You can search for various packages on nixpkgs and also see the different platform support. The downside is that you can’t see if the packages are unfree or broken, and some darwin os-specific packages are not shown.

Packaging

It is easy to write default.nix to package your project.

CMake

For a simple cmake program, you can write default.nix in the following format: default.nix.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
with import <nixpkgs> {};

stdenv.mkDerivation {
  name = "xyz";
  version = "1.0";

  src = ./.;

  nativeBuildInputs = [
    cmake
  ];

  buildInputs = [
    xxx
    yyy
  ];
}

You can use the nix-build command to build, and the result will create a result softlink in the current directory, which is the installation directory.

Since nix-build also creates a build directory, it is recommended to use a different name when developing to prevent conflicts.

Qt

For Qt projects, the implementation is a little more complicated because there are different big versions of Qt, so it has to be split into two files, first default.nix: default.nix.

1
2
3
with import <nixpkgs> {};

libsForQt5.callPackage ./xxx.nix { }

Here it means compiling with qt5, then when writing xxx.nix, the libraries passed in such as qtbase are the version of qt5.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
{ stdenv, qtbase, wrapQtAppsHook, cmake }:

stdenv.mkDerivation {
  name = "xxx";
  version = "1.0";

  src = ./.;

  nativeBuildInputs = [
    cmake
    wrapQtAppsHook # must-have for qt apps
  ];

  buildInputs = [
    qtbase
  ];
}

In actual testing, we found that the running program may report Could not initialize GLX error, which can be solved by adding environment variables via wrapProgram.

1
2
3
4
5
  # https://github.com/NixOS/nixpkgs/issues/66755#issuecomment-657305962
  # Fix "Could not initialize GLX" error
  postInstall = ''
    wrapProgram "$out/bin/xxx" --set QT_XCB_GL_INTEGRATION none
  '';

Development environment

In addition to packaging, the packages needed for the development environment are usually defined in shell.nix.

1
2
3
4
5
6
7
8
{ pkgs ? import <nixpkgs> {}
}:

pkgs.mkShell {
  buildInputs = with pkgs; [
    cmake
  ];
}

Then you can use nix-shell to enter the development environment. If you don’t want outside environment variables to be passed in, you can use nix-shell --pure.