1 { config, lib, options, pkgs, ... }:
6 cfg = config.services.etcd;
7 opt = options.services.etcd;
11 options.services.etcd = {
13 description = lib.mdDoc "Whether to enable etcd.";
19 description = lib.mdDoc "Etcd unique node name.";
20 default = config.networking.hostName;
21 defaultText = literalExpression "config.networking.hostName";
25 advertiseClientUrls = mkOption {
26 description = lib.mdDoc "Etcd list of this member's client URLs to advertise to the rest of the cluster.";
27 default = cfg.listenClientUrls;
28 defaultText = literalExpression "config.${opt.listenClientUrls}";
29 type = types.listOf types.str;
32 listenClientUrls = mkOption {
33 description = lib.mdDoc "Etcd list of URLs to listen on for client traffic.";
34 default = ["http://127.0.0.1:2379"];
35 type = types.listOf types.str;
38 listenPeerUrls = mkOption {
39 description = lib.mdDoc "Etcd list of URLs to listen on for peer traffic.";
40 default = ["http://127.0.0.1:2380"];
41 type = types.listOf types.str;
44 initialAdvertisePeerUrls = mkOption {
45 description = lib.mdDoc "Etcd list of this member's peer URLs to advertise to rest of the cluster.";
46 default = cfg.listenPeerUrls;
47 defaultText = literalExpression "config.${opt.listenPeerUrls}";
48 type = types.listOf types.str;
51 initialCluster = mkOption {
52 description = lib.mdDoc "Etcd initial cluster configuration for bootstrapping.";
53 default = ["${cfg.name}=http://127.0.0.1:2380"];
54 defaultText = literalExpression ''["''${config.${opt.name}}=http://127.0.0.1:2380"]'';
55 type = types.listOf types.str;
58 initialClusterState = mkOption {
59 description = lib.mdDoc "Etcd initial cluster configuration for bootstrapping.";
61 type = types.enum ["new" "existing"];
64 initialClusterToken = mkOption {
65 description = lib.mdDoc "Etcd initial cluster token for etcd cluster during bootstrap.";
66 default = "etcd-cluster";
70 discovery = mkOption {
71 description = lib.mdDoc "Etcd discovery url";
76 clientCertAuth = mkOption {
77 description = lib.mdDoc "Whether to use certs for client authentication";
82 trustedCaFile = mkOption {
83 description = lib.mdDoc "Certificate authority file to use for clients";
85 type = types.nullOr types.path;
89 description = lib.mdDoc "Cert file to use for clients";
91 type = types.nullOr types.path;
95 description = lib.mdDoc "Key file to use for clients";
97 type = types.nullOr types.path;
100 peerCertFile = mkOption {
101 description = lib.mdDoc "Cert file to use for peer to peer communication";
102 default = cfg.certFile;
103 defaultText = literalExpression "config.${opt.certFile}";
104 type = types.nullOr types.path;
107 peerKeyFile = mkOption {
108 description = lib.mdDoc "Key file to use for peer to peer communication";
109 default = cfg.keyFile;
110 defaultText = literalExpression "config.${opt.keyFile}";
111 type = types.nullOr types.path;
114 peerTrustedCaFile = mkOption {
115 description = lib.mdDoc "Certificate authority file to use for peer to peer communication";
116 default = cfg.trustedCaFile;
117 defaultText = literalExpression "config.${opt.trustedCaFile}";
118 type = types.nullOr types.path;
121 peerClientCertAuth = mkOption {
122 description = lib.mdDoc "Whether to check all incoming peer requests from the cluster for valid client certificates signed by the supplied CA";
127 extraConf = mkOption {
128 description = lib.mdDoc ''
129 Etcd extra configuration. See
130 <https://github.com/coreos/etcd/blob/master/Documentation/op-guide/configuration.md#configuration-flags>
132 type = types.attrsOf types.str;
134 example = literalExpression ''
137 "NAME" = "default-name";
138 "MAX_RESULT_BUFFER" = "1024";
139 "MAX_CLUSTER_SIZE" = "9";
140 "MAX_RETRY_ATTEMPTS" = "3";
147 default = "/var/lib/etcd";
148 description = lib.mdDoc "Etcd data directory.";
152 config = mkIf cfg.enable {
153 systemd.tmpfiles.rules = [
154 "d '${cfg.dataDir}' 0700 etcd - - -"
157 systemd.services.etcd = {
158 description = "etcd key-value store";
159 wantedBy = [ "multi-user.target" ];
160 after = [ "network.target" ];
162 environment = (filterAttrs (n: v: v != null) {
163 ETCD_NAME = cfg.name;
164 ETCD_DISCOVERY = cfg.discovery;
165 ETCD_DATA_DIR = cfg.dataDir;
166 ETCD_ADVERTISE_CLIENT_URLS = concatStringsSep "," cfg.advertiseClientUrls;
167 ETCD_LISTEN_CLIENT_URLS = concatStringsSep "," cfg.listenClientUrls;
168 ETCD_LISTEN_PEER_URLS = concatStringsSep "," cfg.listenPeerUrls;
169 ETCD_INITIAL_ADVERTISE_PEER_URLS = concatStringsSep "," cfg.initialAdvertisePeerUrls;
170 ETCD_PEER_TRUSTED_CA_FILE = cfg.peerTrustedCaFile;
171 ETCD_PEER_CERT_FILE = cfg.peerCertFile;
172 ETCD_PEER_KEY_FILE = cfg.peerKeyFile;
173 ETCD_CLIENT_CERT_AUTH = toString cfg.peerClientCertAuth;
174 ETCD_TRUSTED_CA_FILE = cfg.trustedCaFile;
175 ETCD_CERT_FILE = cfg.certFile;
176 ETCD_KEY_FILE = cfg.keyFile;
177 }) // (optionalAttrs (cfg.discovery == ""){
178 ETCD_INITIAL_CLUSTER = concatStringsSep "," cfg.initialCluster;
179 ETCD_INITIAL_CLUSTER_STATE = cfg.initialClusterState;
180 ETCD_INITIAL_CLUSTER_TOKEN = cfg.initialClusterToken;
181 }) // (mapAttrs' (n: v: nameValuePair "ETCD_${n}" v) cfg.extraConf);
184 Documentation = "https://github.com/coreos/etcd";
189 ExecStart = "${pkgs.etcd}/bin/etcd";
195 environment.systemPackages = [ pkgs.etcd ];
200 description = "Etcd daemon user";
203 users.groups.etcd = {};