1{ config, lib, pkgs, ... }:
2
3with lib;
4
5let
6 cfg = config.services.elasticsearch;
7
8 es6 = builtins.compareVersions cfg.package.version "6" >= 0;
9
10 esConfig = ''
11 network.host: ${cfg.listenAddress}
12 cluster.name: ${cfg.cluster_name}
13
14 http.port: ${toString cfg.port}
15 transport.tcp.port: ${toString cfg.tcp_port}
16
17 ${cfg.extraConf}
18 '';
19
20 configDir = cfg.dataDir + "/config";
21
22 elasticsearchYml = pkgs.writeTextFile {
23 name = "elasticsearch.yml";
24 text = esConfig;
25 };
26
27 loggingConfigFilename = "log4j2.properties";
28 loggingConfigFile = pkgs.writeTextFile {
29 name = loggingConfigFilename;
30 text = cfg.logging;
31 };
32
33 esPlugins = pkgs.buildEnv {
34 name = "elasticsearch-plugins";
35 paths = cfg.plugins;
36 postBuild = "${pkgs.coreutils}/bin/mkdir -p $out/plugins";
37 };
38
39in {
40
41 ###### interface
42
43 options.services.elasticsearch = {
44 enable = mkOption {
45 description = "Whether to enable elasticsearch.";
46 default = false;
47 type = types.bool;
48 };
49
50 package = mkOption {
51 description = "Elasticsearch package to use.";
52 default = pkgs.elasticsearch;
53 defaultText = "pkgs.elasticsearch";
54 type = types.package;
55 };
56
57 listenAddress = mkOption {
58 description = "Elasticsearch listen address.";
59 default = "127.0.0.1";
60 type = types.str;
61 };
62
63 port = mkOption {
64 description = "Elasticsearch port to listen for HTTP traffic.";
65 default = 9200;
66 type = types.int;
67 };
68
69 tcp_port = mkOption {
70 description = "Elasticsearch port for the node to node communication.";
71 default = 9300;
72 type = types.int;
73 };
74
75 cluster_name = mkOption {
76 description = "Elasticsearch name that identifies your cluster for auto-discovery.";
77 default = "elasticsearch";
78 type = types.str;
79 };
80
81 extraConf = mkOption {
82 description = "Extra configuration for elasticsearch.";
83 default = "";
84 type = types.str;
85 example = ''
86 node.name: "elasticsearch"
87 node.master: true
88 node.data: false
89 '';
90 };
91
92 logging = mkOption {
93 description = "Elasticsearch logging configuration.";
94 default = ''
95 logger.action.name = org.elasticsearch.action
96 logger.action.level = info
97
98 appender.console.type = Console
99 appender.console.name = console
100 appender.console.layout.type = PatternLayout
101 appender.console.layout.pattern = [%d{ISO8601}][%-5p][%-25c{1.}] %marker%m%n
102
103 rootLogger.level = info
104 rootLogger.appenderRef.console.ref = console
105 '';
106 type = types.str;
107 };
108
109 dataDir = mkOption {
110 type = types.path;
111 default = "/var/lib/elasticsearch";
112 description = ''
113 Data directory for elasticsearch.
114 '';
115 };
116
117 extraCmdLineOptions = mkOption {
118 description = "Extra command line options for the elasticsearch launcher.";
119 default = [];
120 type = types.listOf types.str;
121 };
122
123 extraJavaOptions = mkOption {
124 description = "Extra command line options for Java.";
125 default = [];
126 type = types.listOf types.str;
127 example = [ "-Djava.net.preferIPv4Stack=true" ];
128 };
129
130 plugins = mkOption {
131 description = "Extra elasticsearch plugins";
132 default = [];
133 type = types.listOf types.package;
134 };
135
136 };
137
138 ###### implementation
139
140 config = mkIf cfg.enable {
141 systemd.services.elasticsearch = {
142 description = "Elasticsearch Daemon";
143 wantedBy = [ "multi-user.target" ];
144 after = [ "network.target" ];
145 path = [ pkgs.inetutils ];
146 environment = {
147 ES_HOME = cfg.dataDir;
148 ES_JAVA_OPTS = toString ( optional (!es6) [ "-Des.path.conf=${configDir}" ]
149 ++ cfg.extraJavaOptions);
150 } // optionalAttrs es6 {
151 ES_PATH_CONF = configDir;
152 };
153 serviceConfig = {
154 ExecStart = "${cfg.package}/bin/elasticsearch ${toString cfg.extraCmdLineOptions}";
155 User = "elasticsearch";
156 PermissionsStartOnly = true;
157 LimitNOFILE = "1024000";
158 };
159 preStart = ''
160 ${optionalString (!config.boot.isContainer) ''
161 # Only set vm.max_map_count if lower than ES required minimum
162 # This avoids conflict if configured via boot.kernel.sysctl
163 if [ `${pkgs.procps}/bin/sysctl -n vm.max_map_count` -lt 262144 ]; then
164 ${pkgs.procps}/bin/sysctl -w vm.max_map_count=262144
165 fi
166 ''}
167
168 mkdir -m 0700 -p ${cfg.dataDir}
169
170 # Install plugins
171 ln -sfT ${esPlugins}/plugins ${cfg.dataDir}/plugins
172 ln -sfT ${cfg.package}/lib ${cfg.dataDir}/lib
173 ln -sfT ${cfg.package}/modules ${cfg.dataDir}/modules
174
175 # elasticsearch needs to create the elasticsearch.keystore in the config directory
176 # so this directory needs to be writable.
177 mkdir -m 0700 -p ${configDir}
178
179 # Note that we copy config files from the nix store instead of symbolically linking them
180 # because otherwise X-Pack Security will raise the following exception:
181 # java.security.AccessControlException:
182 # access denied ("java.io.FilePermission" "/var/lib/elasticsearch/config/elasticsearch.yml" "read")
183
184 cp ${elasticsearchYml} ${configDir}/elasticsearch.yml
185 # Make sure the logging configuration for old elasticsearch versions is removed:
186 rm -f "${configDir}/logging.yml"
187 cp ${loggingConfigFile} ${configDir}/${loggingConfigFilename}
188 mkdir -p ${configDir}/scripts
189 ${optionalString es6 "cp ${cfg.package}/config/jvm.options ${configDir}/jvm.options"}
190
191 if [ "$(id -u)" = 0 ]; then chown -R elasticsearch:elasticsearch ${cfg.dataDir}; fi
192 '';
193 };
194
195 environment.systemPackages = [ cfg.package ];
196
197 users = {
198 groups.elasticsearch.gid = config.ids.gids.elasticsearch;
199 users.elasticsearch = {
200 uid = config.ids.uids.elasticsearch;
201 description = "Elasticsearch daemon user";
202 home = cfg.dataDir;
203 group = "elasticsearch";
204 };
205 };
206 };
207}