While my instinct is to use Phoenix for everything, I had to admit it’s overkill for a personal website. I’m not here to flex complex backend integrations or live features. I’ve heard good things about Hugo, so maybe it’s time to try it. I will describe how Nix and deploy-rs allowed me to get framed out and deployed in one short and painless sitting!

A quick google search yielded a serviceable flake for a Hugo dev environment. So I just ran nix develop, hugo new site and then had a Claude agent throw in some placeholder content. 5 minutes later I had something serving on localhost.

It’s placeholder slop but I decided to complete the loop and ship it. The nice thing is this flake also exports the built site as a package. Excellent! I verified that nix build succeeds.

Then I popped into my deployment repo that has the configuration for my NixOS VPS. The top-level flake gets a new input attribute that pulls in the Hugo repo from local storage: inputs.paradigmatic.url = "path:/path/to/paradigmatic.systems";. Then my nginx config gets a new entry:

            virtualHosts."paradigmatic.systems" = {
              enableACME = true;
              forceSSL = true;
              root = "${paradigmatic.packages.${system}.website}";
            };

Just like that, I’ve got a new reverse proxy serving this static site. Since I already have deploy-rs set up, a simple deploy command kicks of the Nix magic which does the build and sends up the new system. Deployment failed the first time because the Acme certificates hadn’t gone through. But thanks to magic rollbacks, the system was uninterrupted.

At time of writing my NixOS flake has 4 different projects coexisting. The flake basically does the job of pulling in different repos that each contain their own packaging instructions. For a more complex (Phoenix) application there is also a module definition that lets me enable and configure the service. So in addition to the input and nginx config we’d have something like:

services.phoenix-app = {
  enable = true;
  port = 4002;
  host = "phoenix-app.com";
  secretKeyBase = "";
};

I have some lingering curiosity that I’d like to state and follow up on.

When I run nix flake update it pulls all the input repos into the nix store and updates the lock file. Are those store paths immune from garbage collection? What if I was in some silly situation where the nix store has the actual valid copy of what I’m deploying, but the source is corrupted?