nixosgitpodmandocker

When I was setting up my first NixOS server, I started to think about how I could run my own software on there in a way where the CI would update the running container on rebuild.
I thought about it quite a lot, in the beginning I tried experimenting with setting up binaries in the system that the CI could call, to restart the containers, but I just really didn’t like how the runners had to basically execute commands as root on my machine.

The solution

Then I somehow discovered the podman-auto-update systemd service - one can simply schedule it with a timer:

systemd.timers.podman-auto-update = {
	wantedBy = [ "timers.target" ];
	timerConfig = {
		OnCalendar = "*:0/5";
		Persistent = true;
		RandomizedDelaySec = 30;
	};
};

Then, it will run based on that timer, and every time it runs, it will check with the container registry, what the sha256 hash of the :latest image is - and if it differs from the image that we are currently running, then it will pull it, and restart the container onto the new image.

systemd[1]: Starting Podman auto-update service...
podman[208463]: 2026-10-25 02:10:19.3016461 +0200 CEST m=+0.064116497 system auto-update
podman[208463]: Trying to pull registry.example.com/me/website:latest...
podman[208463]: Getting image source signatures
podman[208463]: Copying blob sha256:c1b0e353cc0666adebe967c5878a17d3a7b1c30975c5acb4b1f024fe144a35eb
podman[208463]: Copying blob sha256:d2df7799ba997a8ef5b3ed498afe91cacd6278973525af8a0930f2d640e8d217
podman[208463]: Copying blob sha256:d3dff0a445968356ffd650303f7d606949d4b86f225595a390d77fd908c2dac3
podman[208463]: Copying blob sha256:8e0d279432fe66269d7058ce05aed40c296d74cfee5d99edde2eadf54f0d1ae6
podman[208463]: Copying blob sha256:88c9d57524607bf57fb13cc8a9c7699c43ea6674c32b47a851d948c805cae929
podman[208463]: Copying blob sha256:17c612bd9a02baa77fe6d6f1e779ea81b612829cd322048033f190f5e5dad550
podman[208463]: Copying blob sha256:46f950f969a40d1b072568c1befd16d2e63ce1c22c56a32a66283a4a19171a5a
podman[208463]: Copying blob sha256:447dc0b8d144e17b21226e1ae65833e7b007c93967e3266ecf8d51db4c3547f4
podman[208463]: Copying blob sha256:da7cec26d3bb1d3b1debdec90ef2ea4bc13ea9c5c9c98ef53435f271abde8aa0
podman[208463]: Copying blob sha256:17cc8fe4cfa19b46fb861d5d07f54ba6d8835e08f3bb7aa18b1e88daeda091c1
podman[208463]: Copying config sha256:c60e71ff1a7da6b354ad8b58d7fb372a792cd62fdbeb70d0f76dbc8dabfe3dde
podman[208463]: Writing manifest to image destination
podman[208463]: 2026-10-25 02:10:21.732805493 +0200 CEST m=+2.495275872 image pull c60e71ff1a7da6b354ad8b58d7fb372a792cd62fdbeb70d0f76dbc8dabfe3dde registry.example.com/me/website:latest
podman[208463]:             UNIT                    CONTAINER               IMAGE                                   POLICY      UPDATED
podman[208463]:             podman-website.service  1fdcedf4fa8a (website)  registry.example.com/me/website:latest  registry    true
podman[208773]: db3668776eb964e6ec13c3a3b1fc4d9689cfa49aa45484a4b33b1b39ebe065aa
podman[208773]: 2026-10-25 02:10:22.31409499 +0200 CEST m=+0.014237497 image remove db3668776eb964e6ec13c3a3b1fc4d9689cfa49aa45484a4b33b1b39ebe065aa 
systemd[1]: podman-auto-update.service: Deactivated successfully.
systemd[1]: Finished Podman auto-update service.

Very cool!
There’s one important configuration step we must not skip however. The job only restarts the containers that have the label io.containers.autoupdate=registry, so set them up like this:

virtualisation.oci-containers.containers.website = {
	image = "registry.${domain}/me/website:latest";
	login = {
		registry = "registry.${domain}";
		username = "deploy-user";
		passwordFile = config.sops.secrets.registry-deploy-token.path;
	};
	pull = "always";
	ports = [ "127.0.0.1:8081:8080" ];
	extraOptions = [ "--label=io.containers.autoupdate=registry" ];
};

Also useful is the pull = "always" line above: the image is pulled fresh every time the service starts, which makes manual updates easier too.