Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

quadlet-nix

Manages Podman containers, networks, pods, etc. on NixOS via Quadlet.

Features

  • Supports Podman containers, networks, pods, volumes, etc.
  • Supports declarative update and deletion of networks.
  • Supports rootful and rootless (via Home Manager) resources behind the same interface.
  • Supports Podman auto-update.
  • Supports cross-referencing between resources in Nix language.
  • Full quadlet options support, typed and properly escaped.
  • Reliability through effective testing.
  • Simplicity.
  • Whatever offered by Nix or Quadlet.

Motivation

This project was started in Aug 2023, as a result of the author's frustration on some relatively simple container management needs, where then available technologies are either overly restrictive, or overly complex that requires non-trivial but pointless investment ad-hoc domain knowledge.

quadlet-nix is designed to be a simple tool that just works. Quadlet options are directly mapped into Nix, allowing users to effectively manage their Podman resources in the Nix language, without having to acquire domain knowledge in yet another tool. Prior knowledge and documentation of Podman continue to apply.

Comparison

Below are comparisons with several alternatives for declaratively managing Podman containers on NixOS, effective as of May 2025.

NixOS virtualisation.oci-containers
  • 👍 Part of NixOS, no additional dependencies.
  • 👍 Rootless container support without additional dependencies.
  • 👍 Supports Docker.
  • 😐 Compatible with podman auto-update (requires external setup).
  • 👎 Limited options.
  • 👎 Lack of support for networks, pods, etc.
arion
  • 👍 Supports Docker.
  • 😐 More indirection and moving parts.
  • 👎 Limited options.
  • 👎 Incompatible with podman auto-update.
Vanilla Podman Quadlet
  • 👍 Even less indirection.
  • 😐 Compatible with podman auto-update (requires external setup).
  • 😐 Requires more work to set up.
  • 👎 Not integrated with rest of Nix configuration.
Home Manager services.podman
  • 👍 Part of Home Manager, no additional dependencies if you are already using it.
  • 👎 Lack of rootful container support.
compose2nix
  • 👍 Supports Docker.
  • 😐 Compatible with podman auto-update (requires external setup).
  • 😐 More indirection and moving parts.
  • 👎 Less maintainable Nix files due to generated boilerplate.
  • 👎 Manual regeneration is required.
  • 👎 Lack of rootless container support.
  • 👎 Limited options.
  • 👎 Fragmented configuration with source of truth being outside of Nix.

How

See seiarotg.github.io/quadlet-nix for all options.

Recipes

Rootful containers

flake.nix

{
    inputs = {
        nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
        quadlet-nix.url = "github:SEIAROTg/quadlet-nix";
    };
    outputs = { nixpkgs, quadlet-nix, ... }@attrs: {
        nixosConfigurations.machine = nixpkgs.lib.nixosSystem {
            system = "x86_64-linux";
            modules = [
                ./configuration.nix
                quadlet-nix.nixosModules.quadlet
            ];
        };
    };
}

configuration.nix

{ config, ... }: {
    # ...
    virtualisation.quadlet = let
        inherit (config.virtualisation.quadlet) networks pods;
    in {
        containers = {
            nginx.containerConfig.image = "docker.io/library/nginx:latest";
            nginx.containerConfig.networks = [ "podman" networks.internal.ref ];
            nginx.containerConfig.pod = pods.foo.ref;
            nginx.serviceConfig.TimeoutStartSec = "60";
        };
        networks = {
            internal.networkConfig.subnets = [ "10.0.123.1/24" ];
        };
        pods = {
            foo = { };
        };
    };
}
Rootless containers (via Home Manager)

flake.nix

{
    inputs = {
        nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
        home-manager.url = "github:nix-community/home-manager";
        home-manager.inputs.nixpkgs.follows = "nixpkgs";
        quadlet-nix.url = "github:SEIAROTg/quadlet-nix";
    };
    outputs = { nixpkgs, quadlet-nix, home-manager, ... }@attrs: {
        nixosConfigurations.machine = nixpkgs.lib.nixosSystem {
            system = "x86_64-linux";
            modules = [
                ./configuration.nix
                home-manager.nixosModules.home-manager
                # to enable podman & podman systemd generator
                quadlet-nix.nixosModules.quadlet
            ];
        };
    };
}

configuration.nix

{
    # ...
    users.users.alice = {
        # ...
        # required for auto start before user login
        linger = true;
        # required for rootless container with multiple users
        autoSubUidGidRange = true;
    };
    home-manager.users.alice = { pkgs, config, ... }: {
        # ...
        imports = [ inputs.quadlet-nix.homeManagerModules.quadlet ];
        # This is crucial to ensure the systemd services are (re)started on config change
        systemd.user.startServices = "sd-switch";
        virtualisation.quadlet.containers = {
            echo-server = {
                autoStart = true;
                serviceConfig = {
                    RestartSec = "10";
                    Restart = "always";
                };
                containerConfig = {
                    image = "docker.io/mendhak/http-https-echo:31";
                    publishPorts = [ "127.0.0.1:8080:8080" ];
                    userns = "keep-id";
                };
            };
        };
    };
}
Install raw Quadlet files

If you wish to write raw Quadlet files instead of using the Nix options, you may do so with rawConfig. Using this will cause all other options (except autoStart) to be ignored though.

{ config, ... }: {
    # ...
    virtualisation.quadlet = let
        inherit (config.virtualisation.quadlet) networks pods;
    in {
        containers = {
            nginx.rawConfig = ''
                [Container]
                Image=docker.io/library/nginx:latest
                Network=podman
                Network=${networks.internal.ref}
                Pod=${pods.foo.ref}
                [Service]
                TimeoutStartSec=60
            '';
        };
        networks = {
            internal.networkConfig.subnets = [ "10.0.123.1/24" ];
        };
        pods = {
            foo = { };
        };
    };
}
Work with pkgs.dockerTools

Podman natively supports multiple transport, including docker-archive that can be used with pkgs.dockerTools.

{ pkgs, ... }: let
    image = pkgs.dockerTools.buildImage {
        # ...
    };
in {
    virtualisation.quadlet.containers = {
        foo.containerConfig.image = "docker-archive:${image}";
    };
}

See: https://docs.podman.io/en/v5.5.0/markdown/podman-run.1.html#image

Debug & log access

quadlet-nix tries to put containers into full management under systemd. This means once a container crashes, it will be fully deleted and debugging mechanisms like podman ps -a or podman logs will not work.

However, status and logs are still accessible through systemd, namely, systemctl status <service name> and journalctl -u <service name>, where <service name> is container name, <network name>-network, <pod name>-pod, or similar. These names are the names as appeared in virtualisation.quadlet.containers.<container name>, rather than podman container name, in case it's different.

The option I need is not available

Check if that option is supported by Podman Quadlet here: https://docs.podman.io/en/latest/markdown/podman-systemd.unit.5.html.

If it exists, please create an issue or send a PR to add.

Otherwise, please use PodmanArgs and GlobalArgs to insert additional command line arguments as quadlet-nix does not intend to support options beyond what Quadlet offers.