I started using Nix some time ago when I got this M2 MacBook Air. With nix-darwin
and home-manager
, it’s become an incredibly efficient way to manage dotfiles, programs and even GUI apps.
Nix’s appeal is so great that when you begin using it, you can’t go back to manually entering a series of commands to install and configure your system. You want to use it everywhere.
When I ventured into homelabbing, I chose Proxmox Virtual Environment (PVE) as my hypervisor, which supports both traditional VMs, and more interestingly, Linux containers (LXC). Naturally, I wanted my containers to run NixOS.
While it’s possible to obtain a NixOS container template from Hydra, I wondered: what if you could generate a template that’s pre-configured with your own NixOS setup?
That’s exactly what I achieved using GitHub Actions alongside the nixos-generators
tool.
Solution
Below is my GitHub Actions workflow, which automates the generation of LXC container templates. I’ve included comments for clarity.
name: 'generate-lxc'
# This workflow is triggered on new version tags with a hostname suffix,
# where the hostname corresponds to a nixosConfiguration in flake.nix.
on:
push:
tags:
- 'v*.*.*-*'
# The following permissions are required for softprops/action-gh-release@v1.
permissions:
contents: write
jobs:
generate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
# Extract the hostname from the version tag suffix and save it as an environment variable.
- name: Extract tag suffix
run: echo "TAG_SUFFIX=${GITHUB_REF#refs/tags/*-}" >> $GITHUB_ENV
- name: Install nix
uses: cachix/install-nix-action@v24
with:
github_access_token: ${{ secrets.GITHUB_TOKEN }}
# Use the tag suffix to specify which configuration to build with the generator,
# and then store the build path in an environment variable.
- name: Generate NixOS configuration
run: |
nix run github:nix-community/nixos-generators -- -f proxmox-lxc --flake .#${{ env.TAG_SUFFIX }} | {
read path
echo "BUILD_PATH=$path" >> $GITHUB_ENV
}
# Move the build artifact to a working directory and rename it to include the tag suffix.
- name: Prepend tag suffix to file name
run: |
NEW_FILENAME="${{ env.TAG_SUFFIX }}-$(basename ${{ env.BUILD_PATH }})"
RELEASE_PATH="${{ github.workspace }}/$NEW_FILENAME"
cp "${{ env.BUILD_PATH }}" "$RELEASE_PATH"
echo "RELEASE_PATH=$RELEASE_PATH" >> $GITHUB_ENV
# Create a GitHub release and attach the generated container template.
- name: Release
uses: softprops/action-gh-release@v1
with:
files: ${{ env.RELEASE_PATH }}
This configuration assumes that you’re using Nix flakes and have a properly set up flake.nix
file in your repository.
Here’s a snippet illustrating what your nixosConfigurations
might look like:
{
# ...
outputs = { darwin, home-manager, nixpkgs, ... }: {
darwinConfigurations = {
# macOS config
};
nixosConfigurations = {
"nfs" = nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
modules = [
"${nixpkgs}/nixos/modules/virtualisation/proxmox-lxc.nix"
./nixos
./nixos/nfs.nix
];
};
"dns" = nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
modules = [
"${nixpkgs}/nixos/modules/virtualisation/proxmox-lxc.nix"
./nixos
./nixos/dns.nix
];
};
# ... other configurations
};
};
}
For instance, if I want to build the nfs
system, I commit and push my changes, then tag it as v0.2.2-nfs
. For dns
, I would tag it v0.1.0-dns
. Here’s what the releases page look like:
If your repository is public, you can directly use the Download from URL
feature in the PVE user interface, inputting the tarball URL.
And that’s it! When you create a linux container using this template, you’ll have a NixOS machine ready to go!