Deploy NixOS to AWS using Determinate
This guide shows you how to run NixOS systems on Amazon Web Services (AWS) using Determinate Systems’ provide Amazon Machine Images (AMIs).
This guide involves publishing NixOS configurations to FlakeHub Cache, which requires you to sign up for a paid plan for FlakeHub.
NixOS AMIs
Determinate offers custom AMIs for NixOS on both AMD64 Linux (x86_64-linux
) and ARM64 Linux (aarch64-linux
).
You can see the code for these AMIs in the DeterminateSystems/nixos-amis repository on GitHub.
On both systems, the AMIs have these tools installed:
-
Determinate Nix, which includes Determinate Nixd, which enables you to log in to FlakeHub from AWS using only this command:
Log in to FlakeHub from AWS using Determinate Nixddeterminate-nixd login aws
Authentication requires no static keys because Determinate uses AWS’s Security Token Service (STS) to authenticate dynamically. For more on STS, see our dedicated guide.
-
fh, the CLI for FlakeHub. fh provides functionality like the
fh apply nixos
command, which enables you to apply NixOS configurations pushed to FlakeHub Cache without needing to even evaluate the configuration’s Nix expression (using a feature called resolved store paths). Here’s an example command:Apply a NixOS configuration from a resolved store pathsudo fh apply nixos "my-org/my-flake/*#nixosConfigurations.my-nixos-configuration-output"
Having Determinate Nix and fh on the AMI enables you to apply a NixOS configuration in just two commands. Here’s an example:
determinate-nixd login aws
sudo fh apply nixos "my-org/my-flake/*#nixosConfigurations.my-nixos-configuration-output"
In order to use fh apply nixos
in this way, however, you need to publish your NixOS configuration to FlakeHub.
Publish NixOS configurations to FlakeHub
In order to use the fh apply nixos
command, you’ll need to publish a flake to FlakeHub that has a NixOS configuration output.
Here’s a brief example:
{
inputs.nixpkgs.url = "https://flakehub.com/f/NixOS/nixpkgs/0"; # stable Nixpkgs
outputs =
{ self, ... }@inputs:
{
nixosConfigurations.my-nixos-system = inputs.nixpkgs.lib.nixosSystem {
# system configuration
};
};
}
You can publish that flake to FlakeHub on GitHub using the flakehub-push Action.
Here’s an example configuration:
name: Publish every Git push to main to FlakeHub
on:
push:
branches:
- main
jobs:
flakehub-publish:
runs-on: ubuntu-latest
permissions:
id-token: write
contents: read
steps:
- uses: actions/checkout@v5
- uses: DeterminateSystems/determinate-nix-action@v3
- uses: DeterminateSystems/flakehub-push@main
with:
rolling: true
visibility: private # can also be public
include-output-paths: true
The permissions
block is necessary and you must set include-output-paths: true
to take advantage of resolved store paths.
As an alternative to writing your own YAML, you can use our publishing wizard to generate your flakehub-push configuration.
In addition to publishing your flake to FlakeHub, you’ll need to push your NixOS configuration to FlakeHub Cache.
Push your NixOS configuration to FlakeHub Cache
In addition to publishing your NixOS configuration to FlakeHub, you’ll need to push the configuration’s closure to FlakeHub Cache.
When you use the flakehub-cache-action
, as in the example below, the closure is automatically pushed to the cache when you build the NixOS system’s toplevel.
Here’s an example for the flake above:
name: Push NixOS system to FlakeHub Cache
on:
push:
branches:
- main
jobs:
flakehub-publish:
runs-on: ubuntu-latest
permissions:
id-token: write
contents: read
steps:
- uses: actions/checkout@v5
- uses: DeterminateSystems/determinate-nix-action@v3
- uses: DeterminateSystems/flakehub-cache-action@v3
- name: Build NixOS toplevel
run: nix build .#nixosConfigurations.my-nixos-system.config.system.build.toplevel
An alternative to explicitly building/caching your NixOS closures, you can use Determinate CI, which automatically builds/caches all of your flake’s derivation outputs.
Once you’ve pushed your NixOS closure to the cache, you can set up deployment.
Run a deploy script
There’s a wide variety of ways to deploy AMIs to AWS, and we won’t cover that here, although we have a demo repository that shows you how to do it with Terraform. Generally speaking, you’ll need to add a deploy script as user data, like this one from above:
determinate-nixd login aws
sudo fh apply nixos "my-org/my-flake/0.1#nixosConfigurations.my-nixos-configuration-output"
This script, for example, would deploy EtherCalc, as in the demo:
determinate-nixd login aws
sudo fh apply nixos "DeterminateSystems/demo/0.1#nixosConfigurations.ethercalc-demo"
If you’re a Terraform user, you could set things up along these lines:
# Declare the flake reference for your NixOS configuration
locals {
flake_reference = "my-org/my-flake/0.1#nixosConfigurations.my-nixos-configuration-output"
}
# Select our NixOS AMI
data "aws_ami" "determinate_nixos_ami" {
most_recent = true
owners = ["535002876703"]
filter {
name = "name"
values = ["determinate/nixos/epoch-1/24.05.*"]
}
filter {
name = "architecture"
values = ["x86_64"] # aarch64-linux also available
}
}
# Declare an AWS instance using the AMI
resource "aws_instance" "demo" {
ami = data.aws_ami.determinate_nixos_ami.id
user_data = <<-USERDATA
#!/bin/sh
determinate-nixd login aws
fh apply nixos "${local.flake_reference}"
USERDATA
# other options
}
Once you’ve deployed your AMI, you can now trigger updates by (a) publishing a new flake release and pushing the new closure to FlakeHub Cache and then (b) re-running the user data script. Updating the system should be quite speedy, as the closure is cached and Determinate Nix doesn’t even need to calculate the closure’s store path, as FlakeHub has already done that. For automating re-running that script, we recommend using AWS Systems Manager.