Replacing Docker workflow for service ops with Nix

For our web service(s), we currently build a Docker image (via Nix) and on the servers, start these images via docker run .... We build one image per branch (in the CI pipeline), tagging it with the branch, and we have a self-hosted Docker repo to push to.

I wanted to check if just using Nix would be a viable alternative to that, as we build the Docker images using Nix, anyways, so we might save a little time. But I’m not sure how to do that. Does anyone have experience with it?

Here’s what I was thinking about: Assuming we have one machine and thus one Nix store.

  • We build the service using Nix.
  • We save the out-link to /etc/nix-services/mywebservice/branchname, so our regular garbage collection doesn’t just pull the rag.
  • We then create a systemd service for the thing we just built, referencing /etc/nix-services/mywebservice/branchname/bin/server --port 1337 as the executable to start.
  • We start the systemd service.

A few problems remain partly unsolved here:

  • We’d love to have auto-updates when the symlink changes. Not sure if systemd can do that on its own? Otherwise, we’d need a service that checks for changes and restarts.
  • How do we delete symlinks that are not used anymore? This could also be a script, of course, so it’s doable.
  • How would the workflow look like if we had more than one machine?
2 Likes

It sounds like you might be looking for virtualisation.oci-containers.containers.

I think that doesn’t solve my issues. I want to prevent building containers in the first place, and also I’m not on NixOS (which I omitted in my original post - sorry for that).

1 Like

Personally, I would just write a home-manager service module, which starts a user daemon.

This replaces docker with systemd unit files, but if all you care about is a service being open at a particular port, it should be fine. And home-manager + services should be supported on any linux distro using systemd.

We’d love to have auto-updates when the symlink changes. Not sure if systemd can do that on its own? Otherwise, we’d need a service that checks for changes and restarts.

With home-manager, you can have a systemd timer (which can also be reflected in a module) which runs cd <home-manager dir> && nix flake lock --update-input nixpkgs && home-manager rebuild --flake .#<user>. That should rebuild everything, create the new unit files, and trigger some logic for systemd to reload (or whatever configuration you wrote for the service).

How do we delete symlinks that are not used anymore? This could also be a script, of course, so it’s doable.

Home-manager does this through it’s nix profiles.

How would the workflow look like if we had more than one machine?

That would be rough, and I don’t know. You could just re-use the same home-manger + module across any number of systems.

Thanks for the extensive answer!

If I remember correctly, these user daemons only start once you actually log-in, right? I played around with those (without home-manager though) once and was a bit confused. That would defeat the purpose of running this on a server somewhat.

Also, to be completely honest, the solution doesn’t sound too appealing if there’s no good way to distribute that across machines in the end. I guess using NixOS and NixOps would be a smoother experience?

1 Like

User daemons can be started without the user ever logging in, this feature can be enabled by running

loginctl enable-linger [USER]

And what it does under the hood is simply creating an empty file, thus can be done declaratively.

If what you have in mind is some sort of dynamic orchestration system like k8s, I would recommend using disnix or nomad-driver-nix (disclaimer: I do not have any personal experience with them).

2 Likes