Getting started
Why...?
Let's get the elephant in the room out of the way - NixOS already has support for remote host management. The nixos-rebuild command can rebuild and deploy NixOS configuration to remote hosts. There are also many solutions for managing multiple NixOS hosts, like deploy-rs, colmena and others. And this is just another one, tailored for integration with environments managed by DebOps.
The debops.nixos role is a very simple way to manage NixOS
configuration: push a set of config files to the remote host via Ansible, run
the nixos-rebuild switch command on the remote host and you're
finished. The strength of this solution is the ability to integrate NixOS hosts
with your existing Debian/Ubuntu environment managed by Ansible/DebOps. You can
use Jinja to generate NixOS configuration, you can leverage the multi-level
Ansible inventory to create Nix configurations split between groups and hosts,
you can lookup passwords and other data managed by the debops.secret
role or even generate your own passwords and store them easily using the
lookup("password") Ansible lookup.
It's a great entry point into the NixOS world for Ansible and DebOps veterans. :)
N.B.: The Nix package manager is available in Debian and Ubuntu
distributions as nix-bin and nix-setup-systemd packages. It can be used
to setup and manage Nix-based environments on existing Debian/Ubuntu hosts.
This role is not designed with this setup in mind.
NixOS requirements
The host should have NixOS installed already before it can be configured. You can download and use an ISO-based installed from the official NixOS download page. An alternative way for installation is to use the iPXE boot menu configured with the debops.ipxe role (in tandem with the debops.dnsmasq or the debops.tftpd roles) to boot the NixOS installer available via the netboot.xyz service. In the iPXE boot menu, use "Netboot.xyz Boot Menu", "Linux Network Installs", "NixOS" and select the preferred release. This will download and boot the basic installer, in which you can set up and install the OS (check NixOS Installation Guide for instructions).
To be able to use Ansible on a NixOS host, it requires an installed and enabled
OpenSSH server, installed python3 package and a way to either elevate
privileges to the UNIX root account (for example via sudo), or
to provide access to the root account over SSH. The example
configuration.nix configuration file provided in the
nixos__default_configuration variable includes configuration which
sets up the required environment.
Different ways to configure NixOS
The role provides multiple ways to configure a NixOS host. Different methods are executed in the described order (git-based configuration, YAML-based configuration, custom templates). With careful planning, they can be used together to achieve different things.
git-based configuration
Users can clone one or more git repositories to the
/etc/nixos/ directory or its subdirectories to provide Nix
configuration files on the host (this is the fastest method from the available
ones). The repositories are configured using the nixos__*_repositories
variables, documented in the nixos__repositories section of the
documentation.
The /etc/nixos/ directory requires special consideration if it will be
managed using a git repository. Since git itself does not
permit cloning a repository to a non-empty directory, the debops.nixos
role can automatically archive the configuration directory, remove it, clone
the specified git repository and re-sync the original configuration
into place, by default ensuring that files committed in the repository are not
overwritten. This allows, for example, to keep the
/etc/nixos/hardware-configuration.nix file outside of the
git repository to ensure that per-host hardware configuration is not
overwritten.
To enable this, users should set the nixos__git_resync variable on
the command line on first clone of the repository:
debops run nixos -e 'nixos__git_resync=true'
After the repository is cloned, the variable doesn't need to be set anymore.
YAML-based configuration in Ansible inventory
NixOS configuration files can be defined in Ansible inventory variables using
YAML dictionaries. This is done using the nixos__*_configuration variables,
which are documented in the nixos__configuration section of the
documentation. The role uses the Universal Configuration system to
manage YAML-based configuration entries.
This method of configuration can leverage the multiple levels of Ansible
inventory hierarchy (all hosts, multiple host groups, specific hosts) to mix
and match NixOS configuration files on multiple hosts in a cluster. Files can
be created on subdirectories of the /etc/nixos/ directory, can be
generated or removed conditionally, and use Jinja expressions to generate Nix
code with access to Ansible variables and lookup plugins.
Custom templates in ansible/views/<view>/nixos/ subdirectory
The role can use the community.general.filetree Ansible lookup plugin to
generate NixOS configuration files based on Jinja templates stored in the
ansible/views/<view>/nixos/ subdirectory of the DebOps project
directory (similar system to the one utilized by the debops.resources
role). This functionality is documented in the nixos__templates
section of the documentation.
NixOS configuration stored and managed this way is easiest to edit using a preferred text editor with Nix syntax highlighting. The directory structure allows for multiple levels of hierarchy based on Ansible inventory hierarchy (all hosts, multiple groups of hosts, specific hosts). One downside is inability to control when the files are removed from remote hosts, they have to be removed manually or via other means.
Example inventory
To configure a NixOS host using Ansible, you need to add it in the Ansible inventory under a special group, not managed by the main DebOps playbooks:
[debops_nixos_hosts]
hostname ansible_host=hostname.example.org
Use one or more of the methods outlined above to prepare the NixOS configuration files.
Example playbook
The debops.nixos role is NOT included in the main DebOps playbooks. It
has its own nixos.yml playbook which can be run separately from the
rest of the other playbooks. To use it, run the command:
debops run nixos
Or, in a check mode, with a specific host:
debops check nixos -l hostname
To apply changes on the whole infrastructure at once, you can use the command:
debops run site nixos
If you are using this role without DebOps, here's an example Ansible playbook that uses the debops.nixos role:
---
- name: Manage NixOS hosts using DebOps
collections: [ 'debops.debops' ]
hosts: [ 'debops_nixos_hosts' ]
become: True
environment: '{{ inventory__environment | d({})
| combine(inventory__group_environment | d({}))
| combine(inventory__host_environment | d({})) }}'
tasks:
- name: Configure NixOS system
ansible.builtin.import_role:
name: 'debops.debops.nixos'
tags: [ 'role::nixos', 'skip::nixos' ]
Other resources
List of other useful resources related to the debops.nixos Ansible role:
Official NixOS Manual
Unofficial NixOS Wiki maintained by users
Collection of example NixOS configurations on NixOS Wiki
Comparison of Debian and NixOS management commands
Beginners Guide to NixOS & Flakes, an unofficial book for beginners