How to create docker network in NixOS configuration correctly?

I want to hide containers after VPN like how to blog post describe in NixOS:

https://blog.tommyku.com/blog/hide-docker-containers-behind-a-docker-vpn/

For the containers config, seems like using extraOptions is the right answer:

https://search.nixos.org/options?channel=unstable&show=virtualisation.oci-containers.containers.<name>.extraOptions&from=0&size=50&sort=relevance&type=packages&query=virtualisation.oci-containers

But I am stuck in defining the docker network. I tried to have a script like this to run on generation switch, but it gives me docker command not found

              system.activationScripts.mkVPN = ''
                docker network create vpn --subnet 172.20.0.0/16
              '';

EDIT

I found a solution with alternative approach, but why would activationScripts not work there?

activationScripts don’t have access to a $PATH, as far as they are concerned docker isn’t installed (because it’s somewhere in /nix/store and the system hasn’t been activated yet, i.e. the the store hasn’t been made into a system with a PATH).

You can make the scripts directly use the binaries from derivations though:

system.activationScripts.mkVPN = ''
    ${pkgs.docker}/bin/docker network create vpn --subnet 172.20.0.0/16
'';

After all, this snippet must still function even if you never install docker, at least from the perspective of activationScripts.

This is generally a good pattern in the nix world, since it explicitly states the dependencies of your scripts, so you know why they’re there and they aren’t included if the scripts are never used - for system config scripts like this at least. For real scripts write proper derivations and resolve their dependencies :wink:

As for the approach, I prefer the systemd oneshot, simply because it makes it easier to manage, via proper logging and systemctl to toggle things later on. In practice I think both will function, but managing the docker daemon exclusively declaratively is a challenge…

3 Likes

Thank you for answering! Yes I found out that eventually as well, and to also handle a system that might use docker/podman based on oci-containers:

              system.activationScripts.mkVPN = let
                docker = config.virtualisation.oci-containers.backend;
                dockerBin = "${pkgs.${docker}}/bin/${docker}";
              in ''
                ${dockerBin} network inspect vpn >/dev/null 2>&1 || ${dockerBin} network create vpn --subnet 172.20.0.0/16
              '';
2 Likes