Packaging a dynamic website

I’m pretty new to Nix, I have been using NixOS for a year and I have written a few Python derivations and I think I understand more or less the basic concepts.

I recently started using Nixops and liked it a lot, and I thought I’d migrate one of my Debian servers to NixOS. So I’m trying to package a website that is hosted on this server.

The website is made with Python/Django, and uses webpack to build the frontend assets. I managed to generate derivations for the Python part using mach-nix, and for the frontend part using node2nix. I’m using these derivations in a third derivation which contains both the Python code and the compiled JS/CSS files like so:

with import <nixpkgs> { };

let
  src = builtins.fetchGit {
    url = ./.;
    rev = "7bda671b2f52e48507497059a88b6a6f776e76bc";
  };

  mach-nix = import (builtins.fetchGit {
    url = "https://github.com/DavHau/mach-nix/";
    ref = "refs/tags/3.1.1";
  }) { };

  pyenv = mach-nix.mkPython {
    requirements = builtins.readFile ./requirements/base.txt;
  };

  nodeEnv = callPackage ./node-env.nix {
    nodejs = pkgs.nodejs-12_x;
    libtool = if pkgs.stdenv.isDarwin then pkgs.darwin.cctools else null;
  };

  nodePackages = callPackage ./node-packages.nix { inherit nodeEnv; };

  nodeArgs = nodePackages.args // {
    production = false;
    src = builtins.filterSource (path: type:
      /. + path == ./package.json || /. + path == ./package-lock.json) ./.;
    dontNpmInstall = true;
  };

  nodeShell = nodeEnv.buildNodeShell nodeArgs;

  nodeDependencies = stdenv.mkDerivation {
    name = "mywebsite-front";
    src = src;
    installPhase = ''
      ln -s ${nodeShell.nodeDependencies}/lib/node_modules ./node_modules
      export PATH="${nodeShell.nodeDependencies}/bin:$PATH"
      NODE_ENV=production webpack
      mkdir $out
      cp -r dist/* $out/
    '';
  };

in python3Packages.buildPythonPackage {
  propagatedBuildInputs = [ pyenv ];
  name = "mywebsite";
  src = src;
  doCheck = false;
  configurePhase = ''
    ln -s ${nodeDependencies} ./blog/staticfiles
  '';
}

It works but:

  • I need to update src every time I make a new commit. Ideally I’d like to always build the main branch. I tried using ref = refs/heads/main but it seems that without specifying a checksum, Nix doesn’t recreate the derivation. The reason I’m using fetchgit here is that I want to package a clean version of the project, without the risk of including any non-committed change. Maybe there’s another way to do this?
  • I would like to be able to run commands on the website (using Django’s entrypoint ./manage.py command). I guess I can add the entrypoint in my setup.py file, but the thing is that I would like to be able to deploy multiple “instances” of the website on the same server (eg. staging, production, etc). So instead of having a mywebsite command, I guess I should have something like mywebsite-staging and mywebsite-prod commands (which could, or could not, point to the same version of the package). Is it something that should be done outside of the package, eg. with Nixops?
  • What if I want to use this Nix package on a non-NixOS server? How can I make the application server point to the correct directory in /nix/store? Or maybe I should add the application server as an input for the derivation, but then how do I write a systemd unit to use it?

I’m surprised the only examples I could find on the internet were people packaging static websites, but very few packaging something more complex. I plan on sharing my experience, with the hope that it will be of use to some people.

Thanks for your help!

That should be possible to handle with the nix option tarball-ttl. Something like nixos-rebuild -I /your/system/configuration.nix --tarball-ttl 0.

I can imagine that you override each of your package instances with a patch that renames the entry point accordingly. Such as:

stage: python3Packages.buildPythonPackage {
# ...
patchPhase = ''
  substituteInPlace $src/setup.cfg \
    "manage = manage:main" "manage-${stage} = manage:main"
'';
}

For the other question I can’t help, but I remember reading about people trying to run NixOS systemd services on Ubuntu. The gut feel I was left with was, it’s nightmarishly tedious.

I guess it was this Unix & Linux Stackexchange thread:
How does Nix manage SystemD modules on a non-NixOS?

Just linking it here because I’m about to descend into this hellscape…:slight_smile: