This repository contains a presentation and examples for building Docker images with Nix, presented at Nix Bern on June 26, 2025.
- Nix package manager with flakes enabled
- Docker (for running the built images)
-
Enter the development shell:
nix develop
-
Start the container talk:
presenterm -X -x presentation.md
-
Enter the same development shell:
nix develop
-
Launch the VM-focused talk:
presenterm -X -x presentation-vm.md
Both decks share the tooling in the dev shell (now including nixos-rebuild and qemu) so you can swap between them without leaving the environment.
Build a minimal container that runs the hello program:
# Build the image
nix-build hello.nix
# Load into Docker
docker load < result
# Run it
docker run hello-nix:latestThe spa/ directory contains a React SPA example that demonstrates:
- Building a Node.js application with Nix
- Creating a layered Docker image
- Serving static files with Caddy
- Alternative
container.nixfile to build a container to serve images using nginx
cd spa
# Build the Docker image
nix build .#docker
# Load into Docker
docker load < result
# Run the container
docker run -p 8080:80 spa-nix:latestYou can then visit http://localhost:8080 to see the running application.
Alternatively you can build the nginx based container image:
cd spa
# Build the Docker image
nix build -f container.nix
# Load into Docker
docker load < result
# Run the container
docker run -p 8080:80 spa-nix:latestYou can compare this image with the container image you can build using Dockerfile.nginx:
cd spa
docker build -t spa-docker -f Dockerfile.nginx .Use dive to explore the layer structure:
# Examine the layers
dive hello-nix:latest
dive spa-nix:latestThe same SPA can be turned into a NixOS VM image with first-class tooling:
# Build a local QCOW2 image and inspect its size
nixos-rebuild build-image --flake .#spa-vm --image-variant qcow2
du -h result/*
# Produce an AWS-compatible tarball (uses the built-in amazon image module)
nixos-rebuild build-image --flake .#spa-vm --image-variant amazon
du -h result/*
# Or build the AMI artifact directly via the flake package
nix build .#spa-ami
ls -lh result
# Spin up the VM locally for a quick demo (uses qemu under the hood)
nix run .#run-vmNote: older
nixos-generatorscommands are no longer required—the functionality now lives insidenixos-rebuild build-image. You can still mention the project for historical context, but the new workflow keeps everything within the flake.
Once the VM is running you can visit http://localhost:8080 to reach the forwarded port and validate the SPA.
- TLS certificates cannot ship inside immutable images—inject them via cloud-init snippets, an ACME client on first boot, or a secret manager (e.g., AWS SSM).
- Include other environment-specific data (logging endpoints, monitoring agents, SSH config) as separate Nix modules so the base image stays generic.
- The flake exports reusable modules under
.#nixosModulesso you can layer the SPA service into other systems or build cloud images. - The local qemu profile (
.#spa-vm) sets a demo root password (demo1234) for walkthroughs—rotate or disable it in any shared environment.
The same flake exports a Hetzner-friendly profile that keeps SSH access available during installs:
# Create a new Hetzner Cloud server tied to your SSH key
hcloud server create \
--type cx23 \
--image debian-12 \
--ssh-key my-key \
--name spa-anywhere
# Push the SPA configuration directly onto the node
nixos-anywhere \
--flake .#spa-hetzner \
root@<server-ip>The profile currently ships a demo SSH key (zimbatm@p1) in users.users.root.openssh.authorizedKeys; replace it with your own (or overlay one in) before running so the assertion passes with the credentials you control. Swap the target hostname/IP to point at any machine you can reach over SSH (bare metal, other clouds, lab hardware) and nixos-anywhere will provision it with the same system derivation.
The profile also ships a disko definition that partitions /dev/sda with a GRUB BIOS boot sector, EFI system partition, and ext4 root, so the remote installer can lay down disks deterministically. Adjust the layout if your target hardware exposes different devices (e.g., NVMe) before running the command.
- Reproducible: Same inputs always produce the same outputs
- Minimal: Only include what your application actually needs
- Cacheable: Efficient layer sharing across different images
- Transparent: Complete dependency tracking and SBOM generation
presentation.md- The main presentation slidespresentation-vm.md- Companion deck covering the VM workflowhello.nix- Simple hello world container examplespa/- React SPA example with Dockerfile comparisonnixos/services/- Reusable NixOS modules (e.g.,spa.nixwires the SPA into nginx)nixos/profiles/- Base machine profile (spa-machine.nix) consumed by every imageconfigurations/- Target-specific system configs layered on top of the profile (qemu demo, AMI, Hetzner/NixOS-anywhere)tests/- NixOS VM tests to ensure the image serves the SPAflake.nix- Development environment setuplinks.md- Useful resources and links
The flake provides all necessary tools for the presentation:
presenterm- For presenting the slidesnixos-rebuild/qemu- For building and running VM imagescrane- For examining container manifestsjq- For JSON processingqrencode- For generating QR codesdive- For exploring container layerssbomnix- For generating Software Bill of Materialshcloud- For managing Hetzner Cloud servers from the CLInixos-anywhere- For pushing the configuration onto existing hosts
Run the automated checks before sharing updates:
nix flake check