* Setting up Podman and Portainer ** Set up Podman :factcheck: #+begin_src nix :noweb yes :tangle ~/Development/falcon_nest/virtualization/podman-utils.nix { pkgs, lib, config, ... }: { <> } #+end_src NixOS seems to jive better with Podman, probably due to its ability to run without a daemon, which usually needs root access. I'll be looking to run several containers the NixOS way. What does that mean? Well, no yaml, just Nix. Nix to declare the containers and Nix to declare the systemd scripts that help launch and scrap containers. However, there are some configuration options that we need to set for the VM that will host them. First, I'll enable podman and specify it as the backend...: #+begin_src nix virtualisation.podman.enable = true; virtualisation.oci-containers.backend = "podman"; #+end_src ...and enable some sane defaults, like ensuring compatibility with Docker (so 3rd-party images play nice) and enabling automatic pruning of old images: #+begin_src nix virtualisation.podman.autoPrune.enable = true; virtualisation.podman.dockerCompat = true; #+end_src Then I'll set up DNS for all Podman networks: #+begin_src nix networking.firewall.interfaces = let matchAll = if !config.networking.nftables.enable then "podman+" else "podman*"; in { "${matchAll}".allowedUDPPorts = [ 53 ]; }; #+end_src Here's the completed code: #+begin_src nix :noweb-ref podman-utils virtualisation = { oci-containers.backend = "podman"; podman = { enable = true; autoPrune = { enable = true; flags = [ "--all" "--filter 'until=24h'" "--force" ]; }; dockerCompat = true; }; }; networking.firewall.interfaces = let matchAll = if !config.networking.nftables.enable then "podman+" else "podman*"; in { "${matchAll}".allowedUDPPorts = [ 53 ]; }; # If you want to trigger this service manually, run sudo systemctl restart update-containers.service. systemd.services.update-containers = { serviceConfig = { Type = "oneshot"; ExecStart = lib.getExe (pkgs.writeShellScriptBin "update-containers" '' images=$(${pkgs.podman}/bin/podman ps -a --format="{{.Image}}" | sort -u) for image in $images; do ${pkgs.podman}/bin/podman pull "$image" done ''); }; }; #+end_src ** Set up Portainer #+begin_src nix :noweb yes :tangle ~/Development/falcon_nest/virtualization/portainer.nix { pkgs, lib, config, ... }: { <> } #+end_src This virtual machine will feature a single Portainer instance to act as "mission control" for my Debian-based fileserver and a Raspberry Pi running some public-facing utilities that run a variety of Docker containers. I want to be careful to run this instance properly and securely. I'll need to create a user for the Portainer container. #+begin_src nix :noweb-ref portainer-config users.users.portainer = { isSystemUser = true; group = "podman"; home = "/var/lib/portainer"; createHome = true; }; #+end_src Now for the actual container configuration, and this is courtesy of a great tool called [[https://github.com/aksiksi/compose2nix][compose2nix]]. compose2nix takes your standard Docker Compose YAML file and generates a configuration for the container. #+begin_src nix :noweb-ref portainer-config virtualisation.oci-containers.containers."portainer" = { podman.user = "portainer"; image = "portainer/portainer-ce:lts"; volumes = [ "/var/run/docker.sock:/var/run/docker.sock:rw" "portainer_portainer_data:/data:rw" ]; ports = [ "8000:8000/tcp" "9443:9443/tcp" "9000:9000/tcp" ]; log-driver = "journald"; extraOptions = [ "--network-alias=portainer" "--network=portainer_default" ]; }; #+end_src compose2nix also takes care of setting up the systemd service that's responsible for restarting the container when necessary, as well as setting up the volumes and networks the container will use. #+begin_src nix :noweb-ref portainer-config # Networks systemd.services."podman-network-portainer_default" = { path = [ pkgs.podman ]; serviceConfig = { Type = "oneshot"; RemainAfterExit = true; ExecStop = "podman network rm -f portainer_default"; }; script = '' podman network inspect portainer_default || podman network create portainer_default ''; partOf = [ "podman-compose-portainer-root.target" ]; wantedBy = [ "podman-compose-portainer-root.target" ]; }; # Volumes systemd.services."podman-volume-portainer_portainer_data" = { path = [ pkgs.podman ]; serviceConfig = { Type = "oneshot"; RemainAfterExit = true; }; script = '' podman volume inspect portainer_portainer_data || podman volume create portainer_portainer_data ''; partOf = [ "podman-compose-portainer-root.target" ]; wantedBy = [ "podman-compose-portainer-root.target" ]; }; # Root service # When started, this will automatically create all resources and start # the containers. When stopped, this will teardown all resources. systemd.targets."podman-compose-portainer-root" = { unitConfig = { Description = "Root target generated by compose2nix."; }; wantedBy = [ "multi-user.target" ]; }; #+end_src All that's left is to import these into this VM's configuration: #+begin_src nix imports = [ ../virtualization/podman-utils.nix ../virtualization/portainer.nix ]; #+end_src