Glenn McDonald

Software Engineer | Operant | Toronto, Ontario


Deploying your Blog With Nix Flakes (Part 1)

Published June 17, 2024

Nix Flakes and NixOS, make the process of deploying your blog simple, and reproducible.

In this post, we’ll explore how to use Nix Flakes to deploy your blog.

What are Nix Flakes?

A lot of the material on the web explaining Nix Flakes are a bit confusing at best.

I like to think of them as a recipe - they contain a description of everything you need to run a piece of software. They are somewhat similar to Dockerfiles but with a few important differences:

  • Reproducibility: Nix Flakes are reproducible. They’re guaranteed to be built the exact same way every time.
  • Development Environment: They can be your development environment. Everyone can use the flake and be guaranteed to be using the same compiler versions, dependencies, etc.
  • Built-in Build Functionality: They sprinkle in some Makefile-like functionality into the recipe. You can run commands like nix build or nix run.

The Hugo Blog

Now that we understand what a Nix Flake is, let’s look at a practical example: deploying my blog.

I want my blog powered by Hugo, so let’s get that setup first.

{
  description = "Glenn McDonald's Blog";

  # Inputs section specifying dependencies for the flake, aka imports
  inputs = {
    nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
    flake-utils.url = "github:numtide/flake-utils";
  };

  # Outputs defines what this flake provides
  outputs = { nixpkgs, flake-utils, ... }:
    # Helper functions for compatibility across different system architectures
    flake-utils.lib.eachDefaultSystem (system:
      # Import the nixpkgs collection for the current system
      let pkgs = import nixpkgs { inherit system; };
      in {
        # Define the development shell environment
        devShell = pkgs.mkShell {
          # Specify what packages we want available in our development shell
          # This could be things like linters, cli tooling, compilers etc.
          buildInputs = [ pkgs.hugo ];
        };
      });
}

Now we can run nix develop and you’ll be dropped into a shell with Hugo available!

hugo new site flake-blog

Now that we have our Hugo scaffolding setup, let’s get it building using Nix Flakes.

{
  description = "Glenn McDonald's Blog";

  inputs = {
    nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
    flake-utils.url = "github:numtide/flake-utils";
    # Inputs don't have to be other flakes, it can be anything! 
    # We'll use this to fetch the Hugo theme we want
    devise = {
      url = "github:austingebauer/devise";
      flake = false;
    };
  };

  outputs = { self, nixpkgs, flake-utils, ... }@inputs:
    flake-utils.lib.eachDefaultSystem (system:
      let pkgs = import nixpkgs { inherit system; };
      in {
        # Define the packages that this flake provides, in this case our blog
        packages.website = pkgs.stdenv.mkDerivation {
          name = "blog";
          # The directory of the package, self is the directory of the Flake
          src = self;

          # Build inputs required for building our package, this could be things like 
          # openssl, gcc, etc
          buildInputs = [ pkgs.git ];

          # Our build step instructions
          # We want to add a custom theme, so we'll make that directory and move the input contents in there
          # Finally we'll call hugo to generate the static files
          buildPhase = ''
            mkdir -p themes
            ln -s ${inputs.devise} themes/devise
            ${pkgs.hugo}/bin/hugo
          '';

          # What to do once the package is built
          installPhase = "cp -r public $out";
        };

        # Define the default package to be built, a flake could have multiple packages
        defaultPackage = self.packages.${system}.website;

        devShell =
          pkgs.mkShell { buildInputs = [ pkgs.hugo ]; };
      });
}

Now we’ve defined our Hugo package we can run nix build which will run the steps we’ve defined.

If you run ls in our blog directory now we should should see a result symlink which points to our built Hugo blog!

In my follow up post I’ll explain how to use this package in a NixOS configuration, which will serve it with Nginx.