Hi,
I am on NixOS 24.11.
I am trying to piece together the right way to run a shell script with systemd. For context, I am trying to run a Minecraft server using a systemd unit, but I can’t seem to find a clear answer on running user scripts in general.
On my Debian system, I can pretty easily set up a systemd unit that starts the server on startup and closes it on stop, pretty much just like this. Essentially what you do is write a shell script that cd’s into the server directory, and then runs the server .jar file.
#!/bin/bash
cd /some/server/directory
java -jar server.jar
Usually, I throw this script in /usr/local/bin
and pass it to ExecStart
in the systemd unit. I usually would have a second script in /usr/local/bin
that sends a stop signal to the server, and pass that to ExecStop
.
On my NixOS server, I tried to create an equivalent systemd service following this wiki entry (Extend NixOS). Because of this discourse, I thought that I should try just moving the scripts to a directory in my user home, so I decided to move them into the Minecraft server directory that contains the .jar file directly. I then pretty much just copied the unit described here into a dedicated service file that looked something like this:
# server.nix
{config, pkgs, lib, ...}:
let
cfg = config.services.my_minecraft_server;
in
with lib;
{
options = {
services.my_minecraft_server= {
enable = mkOption {
default = false;
type = with types; bool;
};
};
};
config = mkIf cfg.enable {
systemd.services.my_minecraft_server= {
wantedBy = [ "default.target" ];
after = [ "network.target" ];
description = "Start the minecraft server";
serviceConfig = {
Type = "simple";
ExecStart = ''/home/user/minecraft_server/minecraft-server-start'';
ExecStop = ''/home/user/minecraft_server/minecraft-server-stop'';
Restart="always"
};
};
environment.systemPackages = [ mcrcon jdk ];
};
}
I added the service into my configuration.nix
like
imports = [
./server.nix
];
...
services.my_minecraft_server.enable = true;
...
On rebuild, the service gets picked up properly, but it fails and exits. The journal says that systemd isn’t finding /home/user/minecraft_server/minecraft-server-start
. I am not the biggest expert on systemd, or how it runs on NixOS as opposed to another distro, but I guessed that there’s a reason why you would put something in /usr/local/bin
as opposed to anywhere else. I had to create /usr/local/bin
and moved the startup scripts to those location. Here, the service failed due to permission denied, so I gave rwx to all users with chmod 777 minecraft-server-start
. Here, the service failed because it couldnt’ find the java
exectuable, even with both jdk
and jre_minimal
in the environment.systemPackages
list in the unit file. Looking back at this discourse, it seems like the idiomatic thing to do is to write a nix derivation that evaluates? to the shell script, making it globally available on the path. However, I can’t find a straightforward way to do this and install it on my NixOS system. I tried following along with Nix Pill 5-8, but I am confused on how to take the derivation built there and install it on the system. I tried literally just copy pasting the External builder.sh script from this wiki (Shell Scripts), but nix-build
fails and nix build
seems to expect a flake (why these both exist, I am not sure). I was finally able to get a super simple derivation working, like this:
# default.nix
{ pkgs ? import <nixpkgs> {} }:
pkgs.stdenv.mkDerivation {
name = "hello-nix";
src = ./src;
buildPhase = "";
installPhase = ''
mkdir -p $out/bin
cp $src/hello-nix.sh $out/bin
'';
}
# ./src/hello-nix.sh
#!/bin/bash
echo "this is working"
If I nix-build
in this directory, it correctly copies the shell script to result/bin
. However, I can’t get even this to work on my system, either by declaring hello-drv = pkgs.callPackage ./hello-nix/efault.nix { inherit pkgs; }
before my config and then having hello-drv
in my systemPackages list, or by adding ./hello-derivation/default.nix
to my imports, or any combination of the above.
I’m kind of at a loss here. I don’t know what the correct way to do what I’m trying to do is, and various documentation and discourse points me in contradictory directions. I did also take a look at the source for the actual minecraft_server
service in nixpkgs
, but all they do is run pkgs.minecraft_server
on ExecStart
, not call any kind of user script.
Any help here would be appreciated. I don’t know if my approach is wrong, but this seems like it should be a pretty trivial thing to do. Thanks.