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
163 ###### implementation
164
165 config = mkIf cfg.enable (mkMerge [{
166 boot.kernelModules = [ "bridge" "veth" "br_netfilter" "xt_nat" ];
167 boot.kernel.sysctl = {
168 "net.ipv4.conf.all.forwarding" = mkOverride 98 true;
169 "net.ipv4.conf.default.forwarding" = mkOverride 98 true;
170 };
171 environment.systemPackages = [ cfg.package ]
172 ++ optional cfg.enableNvidia pkgs.nvidia-docker;
173 users.groups.docker.gid = config.ids.gids.docker;
174 systemd.packages = [ cfg.package ];
175
176 systemd.services.docker = {
177 wantedBy = optional cfg.enableOnBoot "multi-user.target";
178 after = [ "network.target" "docker.socket" ];
179 requires = [ "docker.socket" ];
180 environment = proxy_env;
181 serviceConfig = {
182 Type = "notify";
183 ExecStart = [
184 ""
185 ''
186 ${cfg.package}/bin/dockerd \
187 --config-file=${daemonSettingsFile} \
188 ${cfg.extraOptions}
189 ''];
190 ExecReload=[
191 ""
192 "${pkgs.procps}/bin/kill -s HUP $MAINPID"
193 ];
194 };
195
196 path = [ pkgs.kmod ] ++ optional (cfg.storageDriver == "zfs") pkgs.zfs
197 ++ optional cfg.enableNvidia pkgs.nvidia-docker;
198 };
199
200 systemd.sockets.docker = {
201 description = "Docker Socket for the API";
202 wantedBy = [ "sockets.target" ];
203 socketConfig = {
204 ListenStream = cfg.listenOptions;
205 SocketMode = "0660";
206 SocketUser = "root";
207 SocketGroup = "docker";
208 };
209 };
210
211 systemd.services.docker-prune = {
212 description = "Prune docker resources";
213
214 restartIfChanged = false;
215 unitConfig.X-StopOnRemoval = false;
216
217 serviceConfig.Type = "oneshot";
218
219 script = ''
220 ${cfg.package}/bin/docker system prune -f ${toString cfg.autoPrune.flags}
221 '';
222
223 startAt = optional cfg.autoPrune.enable cfg.autoPrune.dates;
224 after = [ "docker.service" ];
225 requires = [ "docker.service" ];
226 };
227
228 assertions = [
229 { assertion = cfg.enableNvidia -> config.hardware.opengl.driSupport32Bit or false;
230 message = "Option enableNvidia requires 32bit support libraries";
231 }];
232
233 virtualisation.docker.daemon.settings = {
234 group = "docker";
235 hosts = [ "fd://" ];
236 log-driver = mkDefault cfg.logDriver;
237 storage-driver = mkIf (cfg.storageDriver != null) (mkDefault cfg.storageDriver);
238 live-restore = mkDefault cfg.liveRestore;
239 runtimes = mkIf cfg.enableNvidia {
240 nvidia = {
241 path = "${pkgs.nvidia-docker}/bin/nvidia-container-runtime";
242 };
243 };
244 };
245 }
246 ]);
247
248 imports = [
249 (mkRemovedOptionModule ["virtualisation" "docker" "socketActivation"] "This option was removed and socket activation is now always active")
250 ];
251
252}