grafana-alloy: don't build the frontend twice
[NixPkgs.git] / nixos / modules / services / search / elasticsearch.nix
blobdb19e47493a6a1e960dab70e3f4345ecfaeb0266
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 = "Whether to enable elasticsearch.";
49       default = false;
50       type = types.bool;
51     };
53     package = mkPackageOption pkgs "elasticsearch" { };
55     listenAddress = mkOption {
56       description = "Elasticsearch listen address.";
57       default = "127.0.0.1";
58       type = types.str;
59     };
61     port = mkOption {
62       description = "Elasticsearch port to listen for HTTP traffic.";
63       default = 9200;
64       type = types.port;
65     };
67     tcp_port = mkOption {
68       description = "Elasticsearch port for the node to node communication.";
69       default = 9300;
70       type = types.int;
71     };
73     cluster_name = mkOption {
74       description = "Elasticsearch name that identifies your cluster for auto-discovery.";
75       default = "elasticsearch";
76       type = types.str;
77     };
79     single_node = mkOption {
80       description = "Start a single-node cluster";
81       default = true;
82       type = types.bool;
83     };
85     extraConf = mkOption {
86       description = "Extra configuration for elasticsearch.";
87       default = "";
88       type = types.str;
89       example = ''
90         node.name: "elasticsearch"
91         node.master: true
92         node.data: false
93       '';
94     };
96     logging = mkOption {
97       description = "Elasticsearch logging configuration.";
98       default = ''
99         logger.action.name = org.elasticsearch.action
100         logger.action.level = info
102         appender.console.type = Console
103         appender.console.name = console
104         appender.console.layout.type = PatternLayout
105         appender.console.layout.pattern = [%d{ISO8601}][%-5p][%-25c{1.}] %marker%m%n
107         rootLogger.level = info
108         rootLogger.appenderRef.console.ref = console
109       '';
110       type = types.str;
111     };
113     dataDir = mkOption {
114       type = types.path;
115       default = "/var/lib/elasticsearch";
116       description = ''
117         Data directory for elasticsearch.
118       '';
119     };
121     extraCmdLineOptions = mkOption {
122       description = "Extra command line options for the elasticsearch launcher.";
123       default = [ ];
124       type = types.listOf types.str;
125     };
127     extraJavaOptions = mkOption {
128       description = "Extra command line options for Java.";
129       default = [ ];
130       type = types.listOf types.str;
131       example = [ "-Djava.net.preferIPv4Stack=true" ];
132     };
134     plugins = mkOption {
135       description = "Extra elasticsearch plugins";
136       default = [ ];
137       type = types.listOf types.package;
138       example = lib.literalExpression "[ pkgs.elasticsearchPlugins.discovery-ec2 ]";
139     };
141     restartIfChanged  = mkOption {
142       type = types.bool;
143       description = ''
144         Automatically restart the service on config change.
145         This can be set to false to defer restarts on a server or cluster.
146         Please consider the security implications of inadvertently running an older version,
147         and the possibility of unexpected behavior caused by inconsistent versions across a cluster when disabling this option.
148       '';
149       default = true;
150     };
152   };
154   ###### implementation
156   config = mkIf cfg.enable {
157     systemd.services.elasticsearch = {
158       description = "Elasticsearch Daemon";
159       wantedBy = [ "multi-user.target" ];
160       after = [ "network.target" ];
161       path = [ pkgs.inetutils ];
162       inherit (cfg) restartIfChanged;
163       environment = {
164         ES_HOME = cfg.dataDir;
165         ES_JAVA_OPTS = toString cfg.extraJavaOptions;
166         ES_PATH_CONF = configDir;
167       };
168       serviceConfig = {
169         ExecStart = "${cfg.package}/bin/elasticsearch ${toString cfg.extraCmdLineOptions}";
170         User = "elasticsearch";
171         PermissionsStartOnly = true;
172         LimitNOFILE = "1024000";
173         Restart = "always";
174         TimeoutStartSec = "infinity";
175       };
176       preStart = ''
177         ${optionalString (!config.boot.isContainer) ''
178           # Only set vm.max_map_count if lower than ES required minimum
179           # This avoids conflict if configured via boot.kernel.sysctl
180           if [ `${pkgs.procps}/bin/sysctl -n vm.max_map_count` -lt 262144 ]; then
181             ${pkgs.procps}/bin/sysctl -w vm.max_map_count=262144
182           fi
183         ''}
185         mkdir -m 0700 -p ${cfg.dataDir}
187         # Install plugins
188         ln -sfT ${esPlugins}/plugins ${cfg.dataDir}/plugins
189         ln -sfT ${cfg.package}/lib ${cfg.dataDir}/lib
190         ln -sfT ${cfg.package}/modules ${cfg.dataDir}/modules
192         # elasticsearch needs to create the elasticsearch.keystore in the config directory
193         # so this directory needs to be writable.
194         mkdir -m 0700 -p ${configDir}
196         # Note that we copy config files from the nix store instead of symbolically linking them
197         # because otherwise X-Pack Security will raise the following exception:
198         # java.security.AccessControlException:
199         # access denied ("java.io.FilePermission" "/var/lib/elasticsearch/config/elasticsearch.yml" "read")
201         cp ${elasticsearchYml} ${configDir}/elasticsearch.yml
202         # Make sure the logging configuration for old elasticsearch versions is removed:
203         rm -f "${configDir}/logging.yml"
204         cp ${loggingConfigFile} ${configDir}/${loggingConfigFilename}
205         mkdir -p ${configDir}/scripts
206         cp ${cfg.package}/config/jvm.options ${configDir}/jvm.options
207         # redirect jvm logs to the data directory
208         mkdir -m 0700 -p ${cfg.dataDir}/logs
209         ${pkgs.sd}/bin/sd 'logs/gc.log' '${cfg.dataDir}/logs/gc.log' ${configDir}/jvm.options \
211         if [ "$(id -u)" = 0 ]; then chown -R elasticsearch:elasticsearch ${cfg.dataDir}; fi
212       '';
213       postStart = ''
214         # Make sure elasticsearch is up and running before dependents
215         # are started
216         while ! ${pkgs.curl}/bin/curl -sS -f http://${cfg.listenAddress}:${toString cfg.port} 2>/dev/null; do
217           sleep 1
218         done
219       '';
220     };
222     environment.systemPackages = [ cfg.package ];
224     users = {
225       groups.elasticsearch.gid = config.ids.gids.elasticsearch;
226       users.elasticsearch = {
227         uid = config.ids.uids.elasticsearch;
228         description = "Elasticsearch daemon user";
229         home = cfg.dataDir;
230         group = "elasticsearch";
231       };
232     };
233   };