This is my compact version of NixOS Configuration chapter that can be found on the NixOS Manual. I’ve added and modified a few things.
NixOS Configuration File
The NixOS configuration file generally looks like this:
{ config, pkgs, ... }:
{ option definitions
}
The first line ({ config, pkgs, ... }:
) denotes that this is actually a function that takes at least the two arguments config
and pkgs
. The function returns a set of option definitions ({ ... }
). These definitions have the form name = value
, where name
is the name of an option and value
is its value. For example,
{ config, pkgs, ... }:
{ services.httpd.enable = true;
services.httpd.adminAddr = "alice@example.org";
services.httpd.documentRoot = "/webroot";
}
Options have various types of values. The most important are:
-
Strings
networking.hostName = "dexter";
-
Booleans
networking.firewall.enable = true;
-
Integers
boot.kernel.sysctl."net.ipv4.tcp_keepalive_time" = 60;
-
Sets (name/value pairs enclosed in braces)
services.httpd = { enable = true; adminAddr = "alice@example.org"; documentRoot = "/webroot"; };
You can use
//
operator if you need to merge two attribute sets. For example:let exampleOrgCommon = { hostName = "example.org"; documentRoot = "/webroot"; adminAddr = "alice@example.org"; enableUserDir = true; }; in { services.httpd.virtualHosts = [ exampleOrgCommon (exampleOrgCommon // { enableSSL = true; sslServerCert = "/root/ssl-example-org.crt"; sslServerKey = "/root/ssl-example-org.key"; }) ]; }
-
Lists
boot.kernelModules = [ "fuse" "kvm-intel" "coretemp" ];
-
Packages (Usually, the packages you need are already part of the Nix Packages collection, which is a set that can be accessed through the function argument
pkgs
.)environment.systemPackages = [ pkgs.thunderbird pkgs.emacs ]; services.postgresql.package = pkgs.postgresql_10;
If you need to add a custom package, take a look at Adding Custom Packages chapter.
Ref: NixOS Manual
Abstraction
Functions provide another method of abstraction. For instance, suppose that we want to generate lots of different virtual hosts, all with identical configuration except for the hostname. This can be done as follows:
{
services.httpd.virtualHosts =
let
makeVirtualHost = name:
{ hostName = name;
documentRoot = "/webroot";
adminAddr = "alice@example.org";
};
in
[ (makeVirtualHost "example.org")
(makeVirtualHost "example.com")
(makeVirtualHost "example.gov")
(makeVirtualHost "example.nl")
];
}
Here, makeVirtualHost
is a function that takes a single argument name and returns the configuration for a virtual host. That function is then called for several names to produce the list of virtual host configurations.
This works because attribute values are expressions (as opposed to attribute keys).
See Abstractions chapter
how we can improve this even further (hint: by using map
function).
WordPress on NixOS
Let’s take a look at a real world example of a configuration.nix
that deploys 2 WP blogs.
Keep in mind that when we install NixOS the /etc/nixos/configuration.nix
file will already
contain some configuration. It might look something like this:
{ pkgs, ... }: {
imports = [
./hardware-configuration.nix
];
boot.loader.timeout = 60;
boot.loader.grub.device = "/dev/vda";
i18n.consoleUseXkbConfig = true;
services.xserver.xkbVariant = "dvp";
networking = {
useDHCP = false;
nameservers = [ "1.1.1.1" "1.0.0.1" ];
defaultGateway = "185.186.999.1";
interfaces.ens3 = {
ipv4.addresses = [{
address = "185.186.999.996";
prefixLength = 24;
}];
};
};
services.openssh = {
enable = true;
permitRootLogin = "yes";
};
users.users.root.initialPassword = "secret";
system.stateVersion = "19.09";
}
We want to keep this configuration and just add ours on top of this.
{ pkgs, ... }:
let
# For shits and giggles, let's package the responsive theme
responsiveTheme = pkgs.stdenv.mkDerivation {
name = "responsive-theme";
# Download the theme from the wordpress site
src = pkgs.fetchurl {
url = "http://wordpress.org/themes/download/responsive.1.9.7.6.zip";
sha256 = "1g1mjvjbx7a0w8g69xbahi09y2z8wfk1pzy1wrdrdnjlynyfgzq8";
};
# We need unzip to build this package
buildInputs = [ pkgs.unzip ];
# Installing simply means copying all files to the output directory
installPhase = "mkdir -p $out; cp -R * $out/";
};
# WordPress plugin 'akismet' installation example
akismetPlugin = pkgs.stdenv.mkDerivation {
name = "akismet-plugin";
# Download the theme from the wordpress site
src = pkgs.fetchurl {
url = "https://downloads.wordpress.org/plugin/akismet.3.1.zip";
sha256 = "sha256:1wjq2125syrhxhb0zbak8rv7sy7l8m60c13rfjyjbyjwiasalgzf";
};
# We need unzip to build this package
buildInputs = [ pkgs.unzip ];
# Installing simply means copying all files to the output directory
installPhase = "mkdir -p $out; cp -R * $out/";
};
makeWPConfig = { name, root, aliases }: {
"${name}" = {
database = {
host = "localhost";
name = name;
passwordFile = pkgs.writeText "wordpress-dbpass" "secret";
createLocally = true;
};
themes = [ responsiveTheme ];
plugins = [ akismetPlugin ];
virtualHost = {
adminAddr = "admin@localhost";
serverAliases = aliases;
documentRoot = root;
};
};
};
in {
networking.firewall.enable = true;
networking.firewall.allowedTCPPorts = [ 80 443 ];
services.httpd.adminAddr = "admin@example.com";
services.wordpress = map makeWPConfig [
{
name = "foo";
root = "/var/www/foo_com";
aliases = [ "foo.com" "www.foo.com" ];
}
{
name = "bar";
root = "/var/www/bar_com";
aliases = [ "bar.com" "www.bar.com" ];
}
];
}
services.wordpress
doesn’t allow us to have different database users with createLocally
option set to true
. If we want to use different database users per WP instance we need
to configure them ourselves and set the createLocally
to false.
Here are all the services.wordpress
options.
Ref:
- NixOS-19.09 and WordPress
- [Nix-dev] wordpress
- Take a look at nixcloud-webservices
for more nix webservices.
Modularity
The NixOS configuration mechanism is modular. If your configuration.nix
becomes too big, you can split it into multiple files.
Modules have exactly the same syntax as configuration.nix
. In fact, configuration.nix
is itself a module. You can use other modules by including them from configuration.nix
, e.g.:
{ config, pkgs, ... }:
{ imports = [ ./vpn.nix ./kde.nix ];
services.httpd.enable = true;
environment.systemPackages = [ pkgs.emacs ];
...
}
Here, we include two modules from the same directory, vpn.nix
and kde.nix
. The latter might look like this:
{ config, pkgs, ... }:
{ services.xserver.enable = true;
services.xserver.displayManager.sddm.enable = true;
services.xserver.desktopManager.plasma5.enable = true;
}
When multiple modules define an option, NixOS will try to merge the definitions.
With multiple modules, it may not be obvious what the final value of a configuration option is. The command nixos-option
allows you to find out:
$ nixos-option services.xserver.enable
true
$ nixos-option boot.kernelModules
[ "tun" "ipv6" "loop" ... ]
Interactive exploration of the configuration is possible using nix repl
, a read-eval-print loop for Nix expressions. A typical use:
$ nix repl '<nixpkgs/nixos>'
nix-repl> config.networking.hostName
"ubuntu-s-1vcpu-1gb-fra1-01"
So if we take another look at our configuration.nix
where we defined WP services we can
apply some modularity to it. Let’s take out the WP configuration and put it in a new file
named wp.nix
. Our configuration.nix
will now look like this:
{ pkgs, ... }: {
imports = [
./hardware-configuration.nix
./wp.nix
];
boot.loader.timeout = 60;
...
}
And our wp.nix
config file can stay exactly the same (as defined above). We don’t need
to manually merge thous two configuration files and the structure will be cleaner and
easier to maintain.
If you need to take a quick look at nix syntax you can find it here: Syntax Summary.