nixos/preload: init
[NixPkgs.git] / nixos / modules / services / search / elasticsearch.nix
blobfa1627566ebedb9d57ca870595c90143e531a357
1 { config, lib, pkgs, ... }:
3 with lib;
5 let
6   cfg = config.services.elasticsearch;
8   es7 = builtins.compareVersions cfg.package.version "7" >= 0;
10   esConfig = ''
11     network.host: ${cfg.listenAddress}
12     cluster.name: ${cfg.cluster_name}
13     ${lib.optionalString cfg.single_node "discovery.type: single-node"}
14     ${lib.optionalString (cfg.single_node && es7) "gateway.auto_import_dangling_indices: true"}
16     http.port: ${toString cfg.port}
17     transport.port: ${toString cfg.tcp_port}
19     ${cfg.extraConf}
20   '';
22   configDir = cfg.dataDir + "/config";
24   elasticsearchYml = pkgs.writeTextFile {
25     name = "elasticsearch.yml";
26     text = esConfig;
27   };
29   loggingConfigFilename = "log4j2.properties";
30   loggingConfigFile = pkgs.writeTextFile {
31     name = loggingConfigFilename;
32     text = cfg.logging;
33   };
35   esPlugins = pkgs.buildEnv {
36     name = "elasticsearch-plugins";
37     paths = cfg.plugins;
38     postBuild = "${pkgs.coreutils}/bin/mkdir -p $out/plugins";
39   };
44   ###### interface
46   options.services.elasticsearch = {
47     enable = mkOption {
48       description = lib.mdDoc "Whether to enable elasticsearch.";
49       default = false;
50       type = types.bool;
51     };
53     package = mkOption {
54       description = lib.mdDoc "Elasticsearch package to use.";
55       default = pkgs.elasticsearch;
56       defaultText = literalExpression "pkgs.elasticsearch";
57       type = types.package;
58     };
60     listenAddress = mkOption {
61       description = lib.mdDoc "Elasticsearch listen address.";
62       default = "127.0.0.1";
63       type = types.str;
64     };
66     port = mkOption {
67       description = lib.mdDoc "Elasticsearch port to listen for HTTP traffic.";
68       default = 9200;
69       type = types.port;
70     };
72     tcp_port = mkOption {
73       description = lib.mdDoc "Elasticsearch port for the node to node communication.";
74       default = 9300;
75       type = types.int;
76     };
78     cluster_name = mkOption {
79       description = lib.mdDoc "Elasticsearch name that identifies your cluster for auto-discovery.";
80       default = "elasticsearch";
81       type = types.str;
82     };
84     single_node = mkOption {
85       description = lib.mdDoc "Start a single-node cluster";
86       default = true;
87       type = types.bool;
88     };
90     extraConf = mkOption {
91       description = lib.mdDoc "Extra configuration for elasticsearch.";
92       default = "";
93       type = types.str;
94       example = ''
95         node.name: "elasticsearch"
96         node.master: true
97         node.data: false
98       '';
99     };
101     logging = mkOption {
102       description = lib.mdDoc "Elasticsearch logging configuration.";
103       default = ''
104         logger.action.name = org.elasticsearch.action
105         logger.action.level = info
107         appender.console.type = Console
108         appender.console.name = console
109         appender.console.layout.type = PatternLayout
110         appender.console.layout.pattern = [%d{ISO8601}][%-5p][%-25c{1.}] %marker%m%n
112         rootLogger.level = info
113         rootLogger.appenderRef.console.ref = console
114       '';
115       type = types.str;
116     };
118     dataDir = mkOption {
119       type = types.path;
120       default = "/var/lib/elasticsearch";
121       description = lib.mdDoc ''
122         Data directory for elasticsearch.
123       '';
124     };
126     extraCmdLineOptions = mkOption {
127       description = lib.mdDoc "Extra command line options for the elasticsearch launcher.";
128       default = [ ];
129       type = types.listOf types.str;
130     };
132     extraJavaOptions = mkOption {
133       description = lib.mdDoc "Extra command line options for Java.";
134       default = [ ];
135       type = types.listOf types.str;
136       example = [ "-Djava.net.preferIPv4Stack=true" ];
137     };
139     plugins = mkOption {
140       description = lib.mdDoc "Extra elasticsearch plugins";
141       default = [ ];
142       type = types.listOf types.package;
143       example = lib.literalExpression "[ pkgs.elasticsearchPlugins.discovery-ec2 ]";
144     };
146     restartIfChanged  = mkOption {
147       type = types.bool;
148       description = lib.mdDoc ''
149         Automatically restart the service on config change.
150         This can be set to false to defer restarts on a server or cluster.
151         Please consider the security implications of inadvertently running an older version,
152         and the possibility of unexpected behavior caused by inconsistent versions across a cluster when disabling this option.
153       '';
154       default = true;
155     };
157   };
159   ###### implementation
161   config = mkIf cfg.enable {
162     systemd.services.elasticsearch = {
163       description = "Elasticsearch Daemon";
164       wantedBy = [ "multi-user.target" ];
165       after = [ "network.target" ];
166       path = [ pkgs.inetutils ];
167       inherit (cfg) restartIfChanged;
168       environment = {
169         ES_HOME = cfg.dataDir;
170         ES_JAVA_OPTS = toString cfg.extraJavaOptions;
171         ES_PATH_CONF = configDir;
172       };
173       serviceConfig = {
174         ExecStart = "${cfg.package}/bin/elasticsearch ${toString cfg.extraCmdLineOptions}";
175         User = "elasticsearch";
176         PermissionsStartOnly = true;
177         LimitNOFILE = "1024000";
178         Restart = "always";
179         TimeoutStartSec = "infinity";
180       };
181       preStart = ''
182         ${optionalString (!config.boot.isContainer) ''
183           # Only set vm.max_map_count if lower than ES required minimum
184           # This avoids conflict if configured via boot.kernel.sysctl
185           if [ `${pkgs.procps}/bin/sysctl -n vm.max_map_count` -lt 262144 ]; then
186             ${pkgs.procps}/bin/sysctl -w vm.max_map_count=262144
187           fi
188         ''}
190         mkdir -m 0700 -p ${cfg.dataDir}
192         # Install plugins
193         ln -sfT ${esPlugins}/plugins ${cfg.dataDir}/plugins
194         ln -sfT ${cfg.package}/lib ${cfg.dataDir}/lib
195         ln -sfT ${cfg.package}/modules ${cfg.dataDir}/modules
197         # elasticsearch needs to create the elasticsearch.keystore in the config directory
198         # so this directory needs to be writable.
199         mkdir -m 0700 -p ${configDir}
201         # Note that we copy config files from the nix store instead of symbolically linking them
202         # because otherwise X-Pack Security will raise the following exception:
203         # java.security.AccessControlException:
204         # access denied ("java.io.FilePermission" "/var/lib/elasticsearch/config/elasticsearch.yml" "read")
206         cp ${elasticsearchYml} ${configDir}/elasticsearch.yml
207         # Make sure the logging configuration for old elasticsearch versions is removed:
208         rm -f "${configDir}/logging.yml"
209         cp ${loggingConfigFile} ${configDir}/${loggingConfigFilename}
210         mkdir -p ${configDir}/scripts
211         cp ${cfg.package}/config/jvm.options ${configDir}/jvm.options
212         # redirect jvm logs to the data directory
213         mkdir -m 0700 -p ${cfg.dataDir}/logs
214         ${pkgs.sd}/bin/sd 'logs/gc.log' '${cfg.dataDir}/logs/gc.log' ${configDir}/jvm.options \
216         if [ "$(id -u)" = 0 ]; then chown -R elasticsearch:elasticsearch ${cfg.dataDir}; fi
217       '';
218       postStart = ''
219         # Make sure elasticsearch is up and running before dependents
220         # are started
221         while ! ${pkgs.curl}/bin/curl -sS -f http://${cfg.listenAddress}:${toString cfg.port} 2>/dev/null; do
222           sleep 1
223         done
224       '';
225     };
227     environment.systemPackages = [ cfg.package ];
229     users = {
230       groups.elasticsearch.gid = config.ids.gids.elasticsearch;
231       users.elasticsearch = {
232         uid = config.ids.uids.elasticsearch;
233         description = "Elasticsearch daemon user";
234         home = cfg.dataDir;
235         group = "elasticsearch";
236       };
237     };
238   };