1 { config, lib, pkgs, ... }:
3 cfg = config.services.minecraft-server;
5 # We don't allow eula=false anyways
6 eulaFile = builtins.toFile "eula.txt" ''
7 # eula.txt managed by NixOS Configuration
11 whitelistFile = pkgs.writeText "whitelist.json"
13 (lib.mapAttrsToList (n: v: { name = n; uuid = v; }) cfg.whitelist));
15 cfgToString = v: if builtins.isBool v then lib.boolToString v else toString v;
17 serverPropertiesFile = pkgs.writeText "server.properties" (''
18 # server.properties managed by NixOS configuration
19 '' + lib.concatStringsSep "\n" (lib.mapAttrsToList
20 (n: v: "${n}=${cfgToString v}") cfg.serverProperties));
22 stopScript = pkgs.writeShellScript "minecraft-server-stop" ''
23 echo stop > ${config.systemd.sockets.minecraft-server.socketConfig.ListenFIFO}
25 # Wait for the PID of the minecraft server to disappear before
26 # returning, so systemd doesn't attempt to SIGKILL it.
27 while kill -0 "$1" 2> /dev/null; do
32 # To be able to open the firewall, we need to read out port values in the
33 # server properties, but fall back to the defaults when those don't exist.
34 # These defaults are from https://minecraft.gamepedia.com/Server.properties#Java_Edition_3
35 defaultServerPort = 25565;
37 serverPort = cfg.serverProperties.server-port or defaultServerPort;
39 rconPort = if cfg.serverProperties.enable-rcon or false
40 then cfg.serverProperties."rcon.port" or 25575
43 queryPort = if cfg.serverProperties.enable-query or false
44 then cfg.serverProperties."query.port" or 25565
49 services.minecraft-server = {
51 enable = lib.mkOption {
52 type = lib.types.bool;
55 If enabled, start a Minecraft Server. The server
56 data will be loaded from and saved to
57 {option}`services.minecraft-server.dataDir`.
61 declarative = lib.mkOption {
62 type = lib.types.bool;
65 Whether to use a declarative Minecraft server configuration.
66 Only if set to `true`, the options
67 {option}`services.minecraft-server.whitelist` and
68 {option}`services.minecraft-server.serverProperties` will be
74 type = lib.types.bool;
79 Mojangs EULA](https://account.mojang.com/documents/minecraft_eula). This option must be set to
80 `true` to run Minecraft server.
84 dataDir = lib.mkOption {
85 type = lib.types.path;
86 default = "/var/lib/minecraft";
88 Directory to store Minecraft database and other state/data files.
92 openFirewall = lib.mkOption {
93 type = lib.types.bool;
96 Whether to open ports in the firewall for the server.
100 whitelist = lib.mkOption {
102 minecraftUUID = lib.types.strMatching
103 "[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}" // {
104 description = "Minecraft UUID";
106 in lib.types.attrsOf minecraftUUID;
109 Whitelisted players, only has an effect when
110 {option}`services.minecraft-server.declarative` is
111 `true` and the whitelist is enabled
112 via {option}`services.minecraft-server.serverProperties` by
113 setting `white-list` to `true`.
114 This is a mapping from Minecraft usernames to UUIDs.
115 You can use <https://mcuuid.net/> to get a
116 Minecraft UUID for a username.
118 example = lib.literalExpression ''
120 username1 = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx";
121 username2 = "yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy";
126 serverProperties = lib.mkOption {
127 type = with lib.types; attrsOf (oneOf [ bool int str ]);
129 example = lib.literalExpression ''
135 motd = "NixOS Minecraft server!";
138 "rcon.password" = "hunter2";
142 Minecraft server properties for the server.properties file. Only has
143 an effect when {option}`services.minecraft-server.declarative`
144 is set to `true`. See
145 <https://minecraft.gamepedia.com/Server.properties#Java_Edition_3>
146 for documentation on these values.
150 package = lib.mkPackageOption pkgs "minecraft-server" {
151 example = "minecraft-server_1_12_2";
154 jvmOpts = lib.mkOption {
155 type = lib.types.separatedString " ";
156 default = "-Xmx2048M -Xms2048M";
157 # Example options from https://minecraft.gamepedia.com/Tutorials/Server_startup_script
158 example = "-Xms4092M -Xmx4092M -XX:+UseG1GC -XX:+CMSIncrementalPacing "
159 + "-XX:+CMSClassUnloadingEnabled -XX:ParallelGCThreads=2 "
160 + "-XX:MinHeapFreeRatio=5 -XX:MaxHeapFreeRatio=10";
161 description = "JVM options for the Minecraft server.";
166 config = lib.mkIf cfg.enable {
168 users.users.minecraft = {
169 description = "Minecraft server service user";
175 users.groups.minecraft = {};
177 systemd.sockets.minecraft-server = {
178 bindsTo = [ "minecraft-server.service" ];
180 ListenFIFO = "/run/minecraft-server.stdin";
182 SocketUser = "minecraft";
183 SocketGroup = "minecraft";
189 systemd.services.minecraft-server = {
190 description = "Minecraft Server Service";
191 wantedBy = [ "multi-user.target" ];
192 requires = [ "minecraft-server.socket" ];
193 after = [ "network.target" "minecraft-server.socket" ];
196 ExecStart = "${cfg.package}/bin/minecraft-server ${cfg.jvmOpts}";
197 ExecStop = "${stopScript} $MAINPID";
200 WorkingDirectory = cfg.dataDir;
202 StandardInput = "socket";
203 StandardOutput = "journal";
204 StandardError = "journal";
207 CapabilityBoundingSet = [ "" ];
208 DeviceAllow = [ "" ];
209 LockPersonality = true;
210 PrivateDevices = true;
214 ProtectControlGroups = true;
216 ProtectHostname = true;
217 ProtectKernelLogs = true;
218 ProtectKernelModules = true;
219 ProtectKernelTunables = true;
220 ProtectProc = "invisible";
221 RestrictAddressFamilies = [ "AF_INET" "AF_INET6" ];
222 RestrictNamespaces = true;
223 RestrictRealtime = true;
224 RestrictSUIDSGID = true;
225 SystemCallArchitectures = "native";
230 ln -sf ${eulaFile} eula.txt
231 '' + (if cfg.declarative then ''
233 if [ -e .declarative ]; then
235 # Was declarative before, no need to back up anything
236 ln -sf ${whitelistFile} whitelist.json
237 cp -f ${serverPropertiesFile} server.properties
241 # Declarative for the first time, backup stateful files
242 ln -sb --suffix=.stateful ${whitelistFile} whitelist.json
243 cp -b --suffix=.stateful ${serverPropertiesFile} server.properties
245 # server.properties must have write permissions, because every time
246 # the server starts it first parses the file and then regenerates it..
247 chmod +w server.properties
248 echo "Autogenerated file that signifies that this server configuration is managed declaratively by NixOS" \
253 if [ -e .declarative ]; then
259 networking.firewall = lib.mkIf cfg.openFirewall (if cfg.declarative then {
260 allowedUDPPorts = [ serverPort ];
261 allowedTCPPorts = [ serverPort ]
262 ++ lib.optional (queryPort != null) queryPort
263 ++ lib.optional (rconPort != null) rconPort;
265 allowedUDPPorts = [ defaultServerPort ];
266 allowedTCPPorts = [ defaultServerPort ];
270 { assertion = cfg.eula;
271 message = "You must agree to Mojangs EULA to run minecraft-server."
272 + " Read https://account.mojang.com/documents/minecraft_eula and"
273 + " set `services.minecraft-server.eula` to `true` if you agree.";