1{ config, lib, pkgs, ... }:
2let
3 cfg = config.virtualisation.podman;
4 toml = pkgs.formats.toml { };
5 json = pkgs.formats.json { };
6
7 inherit (lib) mkOption types;
8
9 podmanPackage = (pkgs.podman.override { inherit (cfg) extraPackages; });
10
11 # Provides a fake "docker" binary mapping to podman
12 dockerCompat = pkgs.runCommand "${podmanPackage.pname}-docker-compat-${podmanPackage.version}" {
13 outputs = [ "out" "man" ];
14 inherit (podmanPackage) meta;
15 } ''
16 mkdir -p $out/bin
17 ln -s ${podmanPackage}/bin/podman $out/bin/docker
18
19 mkdir -p $man/share/man/man1
20 for f in ${podmanPackage.man}/share/man/man1/*; do
21 basename=$(basename $f | sed s/podman/docker/g)
22 ln -s $f $man/share/man/man1/$basename
23 done
24 '';
25
26 net-conflist = pkgs.runCommand "87-podman-bridge.conflist" {
27 nativeBuildInputs = [ pkgs.jq ];
28 extraPlugins = builtins.toJSON cfg.defaultNetwork.extraPlugins;
29 jqScript = ''
30 . + { "plugins": (.plugins + $extraPlugins) }
31 '';
32 } ''
33 jq <${cfg.package}/etc/cni/net.d/87-podman-bridge.conflist \
34 --argjson extraPlugins "$extraPlugins" \
35 "$jqScript" \
36 >$out
37 '';
38
39in
40{
41 imports = [
42 ./podman-dnsname.nix
43 ./podman-network-socket.nix
44 (lib.mkRenamedOptionModule [ "virtualisation" "podman" "libpod" ] [ "virtualisation" "containers" "containersConf" ])
45 ];
46
47 meta = {
48 maintainers = lib.teams.podman.members;
49 };
50
51 options.virtualisation.podman = {
52
53 enable =
54 mkOption {
55 type = types.bool;
56 default = false;
57 description = ''
58 This option enables Podman, a daemonless container engine for
59 developing, managing, and running OCI Containers on your Linux System.
60
61 It is a drop-in replacement for the <command>docker</command> command.
62 '';
63 };
64
65 dockerSocket.enable = mkOption {
66 type = types.bool;
67 default = false;
68 description = ''
69 Make the Podman socket available in place of the Docker socket, so
70 Docker tools can find the Podman socket.
71
72 Podman implements the Docker API.
73
74 Users must be in the <code>podman</code> group in order to connect. As
75 with Docker, members of this group can gain root access.
76 '';
77 };
78
79 dockerCompat = mkOption {
80 type = types.bool;
81 default = false;
82 description = ''
83 Create an alias mapping <command>docker</command> to <command>podman</command>.
84 '';
85 };
86
87 enableNvidia = mkOption {
88 type = types.bool;
89 default = false;
90 description = ''
91 Enable use of NVidia GPUs from within podman containers.
92 '';
93 };
94
95 extraPackages = mkOption {
96 type = with types; listOf package;
97 default = [ ];
98 example = lib.literalExpression ''
99 [
100 pkgs.gvisor
101 ]
102 '';
103 description = ''
104 Extra packages to be installed in the Podman wrapper.
105 '';
106 };
107
108 package = lib.mkOption {
109 type = types.package;
110 default = podmanPackage;
111 internal = true;
112 description = ''
113 The final Podman package (including extra packages).
114 '';
115 };
116
117 defaultNetwork.extraPlugins = lib.mkOption {
118 type = types.listOf json.type;
119 default = [];
120 description = ''
121 Extra CNI plugin configurations to add to podman's default network.
122 '';
123 };
124
125 };
126
127 config = lib.mkIf cfg.enable (lib.mkMerge [
128 {
129 environment.systemPackages = [ cfg.package ]
130 ++ lib.optional cfg.dockerCompat dockerCompat;
131
132 environment.etc."cni/net.d/87-podman-bridge.conflist".source = net-conflist;
133
134 virtualisation.containers = {
135 enable = true; # Enable common /etc/containers configuration
136 containersConf.settings = lib.optionalAttrs cfg.enableNvidia {
137 engine = {
138 conmon_env_vars = [ "PATH=${lib.makeBinPath [ pkgs.nvidia-podman ]}" ];
139 runtimes.nvidia = [ "${pkgs.nvidia-podman}/bin/nvidia-container-runtime" ];
140 };
141 };
142 };
143
144 systemd.packages = [ cfg.package ];
145
146 systemd.services.podman.serviceConfig = {
147 ExecStart = [ "" "${cfg.package}/bin/podman $LOGGING system service" ];
148 };
149
150 systemd.sockets.podman.wantedBy = [ "sockets.target" ];
151 systemd.sockets.podman.socketConfig.SocketGroup = "podman";
152
153 systemd.tmpfiles.packages = [
154 # The /run/podman rule interferes with our podman group, so we remove
155 # it and let the systemd socket logic take care of it.
156 (pkgs.runCommand "podman-tmpfiles-nixos" { package = cfg.package; } ''
157 mkdir -p $out/lib/tmpfiles.d/
158 grep -v 'D! /run/podman 0700 root root' \
159 <$package/lib/tmpfiles.d/podman.conf \
160 >$out/lib/tmpfiles.d/podman.conf
161 '') ];
162
163 systemd.tmpfiles.rules =
164 lib.optionals cfg.dockerSocket.enable [
165 "L! /run/docker.sock - - - - /run/podman/podman.sock"
166 ];
167
168 users.groups.podman = {};
169
170 assertions = [
171 {
172 assertion = cfg.dockerCompat -> !config.virtualisation.docker.enable;
173 message = "Option dockerCompat conflicts with docker";
174 }
175 {
176 assertion = cfg.dockerSocket.enable -> !config.virtualisation.docker.enable;
177 message = ''
178 The options virtualisation.podman.dockerSocket.enable and virtualisation.docker.enable conflict, because only one can serve the socket.
179 '';
180 }
181 ];
182 }
183 ]);
184}