Author: Andrew Pan
Last Updated: Wed, Aug 11, 2021NixOS is a purely functional Linux distribution built on top of the Nix package manager. NixOS, and its module system, make it possible to build fully reproducible systems such that a configuration on a given machine, deployed on any other machine, will yield the same system state.
The public Vultr ISO library contains NixOS 20.09. If you are looking for a later version, you can add a NixOS ISO to Vultr. Choose a minimal
x86_64
distribution from https://channels.nixos.org/. For example, version 21.05 would use this link:
https://channels.nixos.org/nixos-21.05/latest-nixos-minimal-x86_64-linux.iso
Go to the Vultr dashboard to deploy an instance. Choose any server plan and location.
For the Server Type selection, you have two options.
If you uploaded an ISO, you can find it in the Upload ISO tab.
Otherwise, find Vultr's NixOS in the ISO Library.
Select your desired version of NixOS, configure the server settings, and click Deploy Now.
After the VPS has finished launching, you should see it in your portal. Next, click the VPS entry and open the console (either a console icon or a 3-dot menu entry depending on your screen layout.)
You should be automatically logged in as the nixos
account.
If you prefer SSH instead of the Vultr Web Console, it's possible to temporarily authorize a SSH key for the nixos
account during the installation by adding it to /home/nixos/.ssh/authorized_keys
.
If you have your public SSH key uploaded to GitHub, for example, you can do the following:
$ mkdir ~/.ssh
$ curl -L https://github.com/<username>.keys >~/.ssh/authorized_keys
Log in via SSH as the nixos
user.
Run sudo -s
to get a root shell for the following steps.
Partition the disk using the parted
CLI.
# parted /dev/vda
You should get output similar to this:
GNU Parted 3.4
Using /dev/vda
Welcome to GNU Parted! Type' help' to view a list of commands.
(parted)
If you run an instance with less than 4 gigabytes of RAM, you will need a swap partition to complete the installation process successfully. In that case, use the following commands in the parted
CLI.
# mklabel msdos
# mkpart primary 1MiB -1GiB
# mkpart primary linux-swap -1GiB 100%
Otherwise, create only the primary partition.
# mklabel msdos
# mkpart primary 1MiB 100%
Once you're done with parted
, issue the quit
command. You should see the regular root prompt, similar to [root@nixos:/home/nixos]#
.
Format your new partition(s) with a filesystem.
# mkfs.btrfs -L root /dev/vda1
It's possible to use ext4 instead of btrfs:
# mkfs.ext4 -L root /dev/vda1
As of Linux 5.0, Btrfs is slower for most workloads than Ext4, but it has features that Ext4 lacks, such as snapshotting, enabled by its copy-on-write design. The decision is up to you.
If you elected to add a swap partition, initialize it with the following.
# mkswap -L swap /dev/vda2
# swapon /dev/vda2
Mount the disk.
# mount /dev/disk/by-label/root /mnt
Now that you've gotten past booting and disk formatting, it's time to make decisions about configuration.
Nix Channels are the original distribution mechanism for NixOS upgrades. WIth Nix Channels, a few key points are worth considering.
Version control of configuration files is not required
There is no "lockfile". A system configuration from two NixOS machines on the same channel version of NixOS may have different package versions and modules
Upgrades are handled by nix-channel --upgrade
Nix Flakes is the next generation of the Nix package manager. In contrast to Nix Channels, Nix Flakes have the following properties.
Version control of your configuration is required; typically Git.
Nixpkgs versions are pinned via a lockfile, flake.lock
. When distributing a Flake-based system configuration across multiple machines, you are guaranteed always to get identical package versions and module behavior
Upgrades are handled by commands such as nix flake update
Nix Flakes comes with the next generation Nix CLI, which consolidates and simplifies Nix-related commands
With either channels or flakes, upgrades do not apply until a rebuild is triggered. For Channels, this entails running nixos-rebuild switch
, while with Flakes, it's as simple as nixos-rebuild switch
with the --flake
flag.
However, as Nix Flakes is unstable, its behavior is subject to change.
Your decision boils down to two key points:
For maximum stability and if you don't mind slightly different software versions and occasional breakage across multiple virtual machines, use Nix Channels.
If you like experimenting and want perfect reproducibility across multiple machines, use Nix Flakes.
This guide provides a common configuration. A starter Flake atop of that configuration is also provided.
Use this command to generate configuration derived from hardware information and your mounted disks.
# nixos-generate-config --root /mnt
Edit the newly generated configuration.
# nano /mnt/etc/nixos/configuration.nix
Change the following values. They may exist already in the configuration commented out. Feel free to either uncomment them as is or add new statements. Read the comments carefully.
# Set a hostname. Replace machineName with your desired name.
networking.hostName = "machineName";
# Set the boot device.
boot.loader.grub.device = "/dev/vda";
# Enable SSH server.
services.openssh.enable = true;
# Disable SSH root login.
services.openssh.permitRootLogin = "no";
# Using an SSH key for authentication is STRONGLY RECOMMENDED.
# Password authentication is known to be less secure and
# leaves you vulnerable to brute-force attacks and drive-by
# password scanning by botnets.
# Comment this out to enable password authentication.
# If you are using the web console thus far to configure
# your VM, comment this out until after installation.
services.openssh.passwordAuthentication = false;
# Manage users declaratively.
users.mutableUsers = false;
# Add a user.
users.users.nixos = {
isNormalUser = true;
# Set an initial password. This will be overridden.
initialPassword = "ChangeMeVultr";
# Add a hashed password, overrides initialPassword.
# See below.
# hashedPassword = "";
extraGroups = [ "wheel" ];
openssh.authorizedKeys.keys = [
# Authorize the SSH public key from `key.pub`.
# Remove this statement if you use password
# authentication.
(builtins.readFile ./key.pub)
];
};
A key.pub
with an SSH public key should be placed within the /mnt/etc/nixos/
directory alongside configuration.nix
. If you previously added your SSH key to authorized_keys
, you can copy it over.
# cp /home/nixos/.ssh/authorized_keys /mnt/etc/nixos/key.pub
If you are working in the web console, skip this step until after installation. It's impossible to copy the output of mkpasswd
from the Vultr console.
It's possible to store the password for the user accounts in plaintext in the configuration file with the initialPassword
attribute. Still, it is strongly recommended that you store it in hashed form. This makes it easier to version control your configuration in the future, as passwords cannot be recovered from their properly hashed versions.
To generate a hashed password, run the following.
# nix-shell -p mkpasswd --run 'mkpasswd -m sha-512'
This invocation temporarily downloads the mkpasswd
utility and runs a command, mkpasswd -m sha-512
in an environment with the utility in its PATH
. The utility will remain available until the next garbage collection.
Uncomment the hashedPassword
assignment and paste the output of mkpasswd
into the string.
Ensure that you are still in a root shell.
# id
uid=0(root) gid=0(root) groups=0(root)
Rerun sudo -s
if this is not the case.
Set up the shell environment necessary for flakes.
# nix-shell -p nixUnstable git
Write this file to /mnt/etc/nixos/flake.nix
.
{
description = "system configuration flake";
inputs = {
# Replace this with any nixpkgs revision you want to use.
# See a list of potential revisions at
# https://github.com/NixOS/nixpkgs/branches/active
nixpkgs.url = "nixpkgs/release-21.05";
};
outputs = inputs@{ self, nixpkgs }:
let
lib = nixpkgs.lib;
nixConf = pkgs: {
environment.systemPackages = [ pkgs.git ];
nix = {
package = pkgs.nixFlakes;
extraOptions = ''
experimental-features = nix-command flakes
'';
autoOptimiseStore = true;
gc = {
automatic = true;
dates = "weekly";
};
};
};
in
{
# Replace machineName with your desired hostname.
nixosConfigurations.machineName = nixpkgs.lib.nixosSystem rec {
system = "x86_64-linux";
modules = [
(nixConf nixpkgs.legacyPackages.${system})
./configuration.nix
];
};
};
}
Flakes require version control. Initialize a Git repository in /mnt/etc/nixos
.
# cd /mnt/etc/nixos
# git init .
All files referenced by the flake must be tracked by Git.
Track all existing nix files in the directory.
git add *.nix
Run this if you added an SSH key:
git add key.pub
If you elected not to use a Flake, run the following command.
nixos-install --no-root-passwd
The root password is unneeded because most operations should be run as the nixos
user. Root access can be gained with sudo
and the nixos
user's password.
If you used a Flake, run this command instead. Be aware that if your instance is small, initial Flake installation can take a long time. It is possible at this stage to install a Channel-based system instead by running the previously given command.
nixos-install --no-root-passwd --flake /mnt/etc/nixos#machineName
As with the flake.nix
customization above, replace machineName
in the invocation with the hostname you chose.
If errors arise during nixos-install
, the configuration.nix
file can be edited to correct the errors. nixos-install
can be run as many times as necessary.
Select your server.
Click the Settings tab.
Find the Custom ISO sidebar in the tab.
Remove the ISO. This will reboot the instance.
SSH into the machine after it boots.
Set up your hashed password now if you haven't and want to. To do that, you should be able to run sudo -s
to get a root shell. The password is what you set for initialPassword
, such as ChangeMeVultr
.
It's important to maintain systems and keep them up to date after installation. By updating, you receive patches for security issues and bugs.
All update operations should be run as root, either with sudo
or in a root shell with sudo -s
. This guide will assume the latter.
Change to /etc/nixos
.
# cd /etc/nixos
Update pinned Nixpkgs revision
# nix flake update
Generate and switch to a new system from the new Nixpkgs revision. Replace machineName with your hostname.
# nixos-rebuild switch --flake .#machineName
Change to /etc/nixos
.
# cd /etc/nixos
Update NixOS.
# nix-channel --update
Generate and switch to a new system from the new Nixpkgs revision.
# nixos-rebuild switch
Create a snapshot of your NixOS installation. From now on, you can deploy NixOS from a snapshot.