NixOps Fundamentals
Date: Oct. 07, 2019
By: Robert Prije
Before covering the specific problems we encountered and solved with NixOps, we’ll go over some fundamentals of NixOps to familiarise you with the tools and background that you may need for later posts.
If you are already familiar with NixOps, or are comfortable finding what you need from the more comprehensive manual then you may want to skip this post. This post is not intended to duplicate the work in the manual, but rather condense the information relevant to the remaining blog posts for easy reference.
Overview of NixOps
NixOps provides a way to use the Nix programming language to define a NixOps “network specification”, which is a declarative description of a set of machines in the cloud. Once defined, NixOps converts the network specification into a “deployment” which is a combination of the network specification describing how the machines “should be” as well as the current state of those machines as last known by NixOps. Through the deployment, NixOps is capable of provisioning and decommissioning those machines, deploying updates to them, starting and stopping them, and accessing them via ssh. Such actions update the running state of the machines and NixOps’ internal record of that machine state whenever appropriate.
A network specification describes a set of resources including a mapping of machine names to NixOS modules. Any attribute name not already reserved for a resource is assumed to be the name of a machine. A network specification can be defined across multiple network files. If a machine shows up in multiple files, the mapped NixOS modules are merged. This means we can adopt the standard NixOps practice of separating a machine’s “logical” specification (the software that runs on the machine) from a machine’s “physical” specification (the provisioned hardware and cloud service the machine runs on).
For example, here is a logical specification describing just the software
running on a machine named example
:
{
network.description = "Example";
example =
{ pkgs, ... }:
{ environment.systemPackages = with pkgs; [ pkgs.dhall ];
};
}
Here is the physical specification for the same machine describing a virtual machine running under VirtualBox:
{
example =
{ config, pkgs, ... }:
{ deployment.targetEnv = "virtualbox";
deployment.virtualbox.memorySize = 1024; # megabytes
deployment.virtualbox.vcpu = 2; # number of cpus
};
}
The actual deployment is stored in an SQLite
database. This database is located, by default, in the user’s home directory
under ~/.nixops
. This deployment can be exported to JSON format for backups or
transferring to other machines and imported again from JSON format.
The deployment does not actually store the contents of the network specification, only the file paths containing the network specification. This means if the files are updated, NixOps can redeploy the specification from the changes without needing to have the changes explicitly loaded. It also means that if the files are moved, NixOps must be notified of the change in path.
Multiple deployments can be defined each with their own network paths.
The deployment being targeted can be specified to the nixops
commands either
from the -d
argument or the NIXOPS_DEPLOYMENT
environment variable.
Most commands in the nixops
executable target the entire deployment rather
than specific machines. These commands often provide a --include
or
--exclude
argument for targetting a more constrained set of machines within
a deployment.
NixOps Commands
The following table lists a subset of the subcommands available from the
nixops
executable which are relevant to these blog posts or may assist
in debugging
Command | Description |
---|---|
create |
create a new deployment |
modify |
modify an existing deployment (for example, changing the network file paths) |
list |
list all known deployments |
info |
show the state of the deployment (including network files passed by create and arguments passed by set-args ) |
set-args |
persistently set arguments to the deployment specification |
show-arguments |
print the arguments to the network expressions |
deploy |
deploy the network configuration |
ssh |
login on the specified machine via SSH |
destroy |
destroy all resources in the specified deployment |
delete |
delete a deployment |
export |
export the state of a deployment to JSON format |
import |
import deployments into the state file from JSON format |
Assumptions in these Blog Posts
The commands described in these blog posts have only been tested from
a NixOS machine. There may be problems when, for example, running from
a MacOSX machine. Feedback suggests you may need to be careful about how
the system
argument gets passed to NixPkgs. Describing how to get other
operating systems working with NixOps is outside the scope of these
blog posts.
The deployments described in these blog posts target
VirtualBox. This is for simplicity in
being able to run the deployment on the machine being tested from without
needing a third party cloud service. VirtualBox will require having
some specialised VirtualBox and other virtualisation kernel modules installed.
These blog posts assume you have a working VirtualBox environment already. If
you are using NixOS, virtualbox support can be enabled by specifying
virtualisation.virtualbox.host.enable = true;
in your NixOs configuration
then rebuilding and rebooting your machine.