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