Dumont Digital logo

How to generate NixOS LXC templates in GitHub Actions


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.


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.
      - 'v*.*.*-*'

# The following permissions are required for softprops/action-gh-release@v1.
  contents: write

    runs-on: ubuntu-latest
      - 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
          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"

      # Create a GitHub release and attach the generated container template.
      - name: Release
        uses: softprops/action-gh-release@v1
          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 = [

      "dns" = nixpkgs.lib.nixosSystem {
        system = "x86_64-linux";
        modules = [
      # ... 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:

GitHub repository releases page showcasing different NixOS container templates

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!

© 2024 freddydumont