1{ config, lib, pkgs, ... }:
2
3with lib;
4
5let
6 cfg = config.services.dockerRegistry;
7
8 blobCache = if cfg.enableRedisCache
9 then "redis"
10 else "inmemory";
11
12 registryConfig = {
13 version = "0.1";
14 log.fields.service = "registry";
15 storage = {
16 cache.blobdescriptor = blobCache;
17 delete.enabled = cfg.enableDelete;
18 } // (optionalAttrs (cfg.storagePath != null) { filesystem.rootdirectory = cfg.storagePath; });
19 http = {
20 addr = "${cfg.listenAddress}:${builtins.toString cfg.port}";
21 headers.X-Content-Type-Options = ["nosniff"];
22 };
23 health.storagedriver = {
24 enabled = true;
25 interval = "10s";
26 threshold = 3;
27 };
28 };
29
30 registryConfig.redis = mkIf cfg.enableRedisCache {
31 addr = "${cfg.redisUrl}";
32 password = "${cfg.redisPassword}";
33 db = 0;
34 dialtimeout = "10ms";
35 readtimeout = "10ms";
36 writetimeout = "10ms";
37 pool = {
38 maxidle = 16;
39 maxactive = 64;
40 idletimeout = "300s";
41 };
42 };
43
44 configFile = pkgs.writeText "docker-registry-config.yml" (builtins.toJSON (recursiveUpdate registryConfig cfg.extraConfig));
45
46in {
47 options.services.dockerRegistry = {
48 enable = mkEnableOption (lib.mdDoc "Docker Registry");
49
50 package = mkOption {
51 type = types.package;
52 description = mdDoc "Which Docker registry package to use.";
53 default = pkgs.docker-distribution;
54 defaultText = literalExpression "pkgs.docker-distribution";
55 example = literalExpression "pkgs.gitlab-container-registry";
56 };
57
58 listenAddress = mkOption {
59 description = lib.mdDoc "Docker registry host or ip to bind to.";
60 default = "127.0.0.1";
61 type = types.str;
62 };
63
64 port = mkOption {
65 description = lib.mdDoc "Docker registry port to bind to.";
66 default = 5000;
67 type = types.port;
68 };
69
70 storagePath = mkOption {
71 type = types.nullOr types.path;
72 default = "/var/lib/docker-registry";
73 description = lib.mdDoc ''
74 Docker registry storage path for the filesystem storage backend. Set to
75 null to configure another backend via extraConfig.
76 '';
77 };
78
79 enableDelete = mkOption {
80 type = types.bool;
81 default = false;
82 description = lib.mdDoc "Enable delete for manifests and blobs.";
83 };
84
85 enableRedisCache = mkEnableOption (lib.mdDoc "redis as blob cache");
86
87 redisUrl = mkOption {
88 type = types.str;
89 default = "localhost:6379";
90 description = lib.mdDoc "Set redis host and port.";
91 };
92
93 redisPassword = mkOption {
94 type = types.str;
95 default = "";
96 description = lib.mdDoc "Set redis password.";
97 };
98
99 extraConfig = mkOption {
100 description = lib.mdDoc ''
101 Docker extra registry configuration via environment variables.
102 '';
103 default = {};
104 type = types.attrs;
105 };
106
107 enableGarbageCollect = mkEnableOption (lib.mdDoc "garbage collect");
108
109 garbageCollectDates = mkOption {
110 default = "daily";
111 type = types.str;
112 description = lib.mdDoc ''
113 Specification (in the format described by
114 {manpage}`systemd.time(7)`) of the time at
115 which the garbage collect will occur.
116 '';
117 };
118 };
119
120 config = mkIf cfg.enable {
121 systemd.services.docker-registry = {
122 description = "Docker Container Registry";
123 wantedBy = [ "multi-user.target" ];
124 after = [ "network.target" ];
125 script = ''
126 ${cfg.package}/bin/registry serve ${configFile}
127 '';
128
129 serviceConfig = {
130 User = "docker-registry";
131 WorkingDirectory = cfg.storagePath;
132 AmbientCapabilities = mkIf (cfg.port < 1024) "cap_net_bind_service";
133 };
134 };
135
136 systemd.services.docker-registry-garbage-collect = {
137 description = "Run Garbage Collection for docker registry";
138
139 restartIfChanged = false;
140 unitConfig.X-StopOnRemoval = false;
141
142 serviceConfig.Type = "oneshot";
143
144 script = ''
145 ${cfg.package}/bin/registry garbage-collect ${configFile}
146 /run/current-system/systemd/bin/systemctl restart docker-registry.service
147 '';
148
149 startAt = optional cfg.enableGarbageCollect cfg.garbageCollectDates;
150 };
151
152 users.users.docker-registry =
153 (optionalAttrs (cfg.storagePath != null) {
154 createHome = true;
155 home = cfg.storagePath;
156 }) // {
157 group = "docker-registry";
158 isSystemUser = true;
159 };
160 users.groups.docker-registry = {};
161 };
162}