1# Systemd services for docker.
2
3{ config, lib, pkgs, ... }:
4
5with lib;
6
7let
8
9 cfg = config.virtualisation.docker;
10 proxy_env = config.networking.proxy.envVars;
11 settingsFormat = pkgs.formats.json {};
12 daemonSettingsFile = settingsFormat.generate "daemon.json" cfg.daemon.settings;
13in
14
15{
16 ###### interface
17
18 options.virtualisation.docker = {
19 enable =
20 mkOption {
21 type = types.bool;
22 default = false;
23 description = ''
24 This option enables docker, a daemon that manages
25 linux containers. Users in the "docker" group can interact with
26 the daemon (e.g. to start or stop containers) using the
27 {command}`docker` command line tool.
28 '';
29 };
30
31 listenOptions =
32 mkOption {
33 type = types.listOf types.str;
34 default = ["/run/docker.sock"];
35 description = ''
36 A list of unix and tcp docker should listen to. The format follows
37 ListenStream as described in systemd.socket(5).
38 '';
39 };
40
41 enableOnBoot =
42 mkOption {
43 type = types.bool;
44 default = true;
45 description = ''
46 When enabled dockerd is started on boot. This is required for
47 containers which are created with the
48 `--restart=always` flag to work. If this option is
49 disabled, docker might be started on demand by socket activation.
50 '';
51 };
52
53 daemon.settings =
54 mkOption {
55 type = settingsFormat.type;
56 default = { };
57 example = {
58 ipv6 = true;
59 "fixed-cidr-v6" = "fd00::/80";
60 };
61 description = ''
62 Configuration for docker daemon. The attributes are serialized to JSON used as daemon.conf.
63 See https://docs.docker.com/engine/reference/commandline/dockerd/#daemon-configuration-file
64 '';
65 };
66
67 enableNvidia =
68 mkOption {
69 type = types.bool;
70 default = false;
71 description = ''
72 **Deprecated**, please use virtualisation.containers.cdi.dynamic.nvidia.enable instead.
73
74 Enable nvidia-docker wrapper, supporting NVIDIA GPUs inside docker containers.
75 '';
76 };
77
78 liveRestore =
79 mkOption {
80 type = types.bool;
81 default = true;
82 description = ''
83 Allow dockerd to be restarted without affecting running container.
84 This option is incompatible with docker swarm.
85 '';
86 };
87
88 storageDriver =
89 mkOption {
90 type = types.nullOr (types.enum ["aufs" "btrfs" "devicemapper" "overlay" "overlay2" "zfs"]);
91 default = null;
92 description = ''
93 This option determines which Docker
94 [storage driver](https://docs.docker.com/storage/storagedriver/select-storage-driver/)
95 to use.
96 By default it lets docker automatically choose the preferred storage
97 driver.
98 However, it is recommended to specify a storage driver explicitly, as
99 docker's default varies over versions.
100
101 ::: {.warning}
102 Changing the storage driver will cause any existing containers
103 and images to become inaccessible.
104 :::
105 '';
106 };
107
108 logDriver =
109 mkOption {
110 type = types.enum ["none" "json-file" "syslog" "journald" "gelf" "fluentd" "awslogs" "splunk" "etwlogs" "gcplogs" "local"];
111 default = "journald";
112 description = ''
113 This option determines which Docker log driver to use.
114 '';
115 };
116
117 extraOptions =
118 mkOption {
119 type = types.separatedString " ";
120 default = "";
121 description = ''
122 The extra command-line options to pass to
123 {command}`docker` daemon.
124 '';
125 };
126
127 autoPrune = {
128 enable = mkOption {
129 type = types.bool;
130 default = false;
131 description = ''
132 Whether to periodically prune Docker resources. If enabled, a
133 systemd timer will run `docker system prune -f`
134 as specified by the `dates` option.
135 '';
136 };
137
138 flags = mkOption {
139 type = types.listOf types.str;
140 default = [];
141 example = [ "--all" ];
142 description = ''
143 Any additional flags passed to {command}`docker system prune`.
144 '';
145 };
146
147 dates = mkOption {
148 default = "weekly";
149 type = types.str;
150 description = ''
151 Specification (in the format described by
152 {manpage}`systemd.time(7)`) of the time at
153 which the prune will occur.
154 '';
155 };
156 };
157
158 package = mkPackageOption pkgs "docker" { };
159
160 extraPackages = mkOption {
161 type = types.listOf types.package;
162 default = [ ];
163 example = literalExpression "with pkgs; [ criu ]";
164 description = ''
165 Extra packages to add to PATH for the docker daemon process.
166 '';
167 };
168 };
169
170 ###### implementation
171
172 config = mkIf cfg.enable (mkMerge [{
173 boot.kernelModules = [ "bridge" "veth" "br_netfilter" "xt_nat" ];
174 boot.kernel.sysctl = {
175 "net.ipv4.conf.all.forwarding" = mkOverride 98 true;
176 "net.ipv4.conf.default.forwarding" = mkOverride 98 true;
177 };
178 environment.systemPackages = [ cfg.package ]
179 ++ optional cfg.enableNvidia pkgs.nvidia-docker;
180 users.groups.docker.gid = config.ids.gids.docker;
181 systemd.packages = [ cfg.package ];
182
183 # Docker 25.0.0 supports CDI by default
184 # (https://docs.docker.com/engine/release-notes/25.0/#new). Encourage
185 # moving to CDI as opposed to having deprecated runtime
186 # wrappers.
187 warnings = lib.optionals (cfg.enableNvidia && (lib.strings.versionAtLeast cfg.package.version "25")) [
188 ''
189 You have set virtualisation.docker.enableNvidia. This option is deprecated, please set virtualisation.containers.cdi.dynamic.nvidia.enable instead.
190 ''
191 ];
192
193 systemd.services.docker = {
194 wantedBy = optional cfg.enableOnBoot "multi-user.target";
195 after = [ "network.target" "docker.socket" ];
196 requires = [ "docker.socket" ];
197 environment = proxy_env;
198 serviceConfig = {
199 Type = "notify";
200 ExecStart = [
201 ""
202 ''
203 ${cfg.package}/bin/dockerd \
204 --config-file=${daemonSettingsFile} \
205 ${cfg.extraOptions}
206 ''];
207 ExecReload=[
208 ""
209 "${pkgs.procps}/bin/kill -s HUP $MAINPID"
210 ];
211 };
212
213 path = [ pkgs.kmod ] ++ optional (cfg.storageDriver == "zfs") pkgs.zfs
214 ++ optional cfg.enableNvidia pkgs.nvidia-docker
215 ++ cfg.extraPackages;
216 };
217
218 systemd.sockets.docker = {
219 description = "Docker Socket for the API";
220 wantedBy = [ "sockets.target" ];
221 socketConfig = {
222 ListenStream = cfg.listenOptions;
223 SocketMode = "0660";
224 SocketUser = "root";
225 SocketGroup = "docker";
226 };
227 };
228
229 systemd.services.docker-prune = {
230 description = "Prune docker resources";
231
232 restartIfChanged = false;
233 unitConfig.X-StopOnRemoval = false;
234
235 serviceConfig.Type = "oneshot";
236
237 script = ''
238 ${cfg.package}/bin/docker system prune -f ${toString cfg.autoPrune.flags}
239 '';
240
241 startAt = optional cfg.autoPrune.enable cfg.autoPrune.dates;
242 after = [ "docker.service" ];
243 requires = [ "docker.service" ];
244 };
245
246 assertions = [
247 { assertion = cfg.enableNvidia && pkgs.stdenv.isx86_64 -> config.hardware.opengl.driSupport32Bit or false;
248 message = "Option enableNvidia on x86_64 requires 32bit support libraries";
249 }];
250
251 virtualisation.docker.daemon.settings = {
252 group = "docker";
253 hosts = [ "fd://" ];
254 log-driver = mkDefault cfg.logDriver;
255 storage-driver = mkIf (cfg.storageDriver != null) (mkDefault cfg.storageDriver);
256 live-restore = mkDefault cfg.liveRestore;
257 runtimes = mkIf cfg.enableNvidia {
258 nvidia = {
259 path = "${pkgs.nvidia-docker}/bin/nvidia-container-runtime";
260 };
261 };
262 };
263 }
264 ]);
265
266 imports = [
267 (mkRemovedOptionModule ["virtualisation" "docker" "socketActivation"] "This option was removed and socket activation is now always active")
268 ];
269
270}