diff --git a/nixos/modules/misc/ids.nix b/nixos/modules/misc/ids.nix
index 3ba279f597da..83b47728639c 100644
--- a/nixos/modules/misc/ids.nix
+++ b/nixos/modules/misc/ids.nix
@@ -273,6 +273,7 @@
smokeping = 250;
gocd-agent = 251;
gocd-server = 252;
+ terraria = 253;
# When adding a uid, make sure it doesn't match an existing gid. And don't use uids above 399!
@@ -516,6 +517,7 @@
smokeping = 250;
gocd-agent = 251;
gocd-server = 252;
+ terraria = 253;
# When adding a gid, make sure it doesn't match an existing
# uid. Users and groups with the same name should have equal
diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix
index 6a6730856b15..03a191c72321 100644
--- a/nixos/modules/module-list.nix
+++ b/nixos/modules/module-list.nix
@@ -174,6 +174,7 @@
./services/games/ghost-one.nix
./services/games/minecraft-server.nix
./services/games/minetest-server.nix
+ ./services/games/terraria.nix
./services/hardware/acpid.nix
./services/hardware/actkbd.nix
./services/hardware/amd-hybrid-graphics.nix
diff --git a/nixos/modules/services/games/terraria.nix b/nixos/modules/services/games/terraria.nix
new file mode 100644
index 000000000000..57ac37bb1bd3
--- /dev/null
+++ b/nixos/modules/services/games/terraria.nix
@@ -0,0 +1,139 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+ cfg = config.services.terraria;
+ worldSizeMap = { "small" = 1; "medium" = 2; "large" = 3; };
+ valFlag = name: val: optionalString (val != null) "-${name} \"${escape ["\\" "\""] (toString val)}\"";
+ boolFlag = name: val: optionalString val "-${name}";
+ flags = [
+ (valFlag "port" cfg.port)
+ (valFlag "maxPlayers" cfg.maxPlayers)
+ (valFlag "password" cfg.password)
+ (valFlag "motd" cfg.messageOfTheDay)
+ (valFlag "world" cfg.worldPath)
+ (valFlag "autocreate" (builtins.getAttr cfg.autoCreatedWorldSize worldSizeMap))
+ (valFlag "banlist" cfg.banListPath)
+ (boolFlag "secure" cfg.secure)
+ (boolFlag "noupnp" cfg.noUPnP)
+ ];
+in
+{
+ options = {
+ services.terraria = {
+ enable = mkOption {
+ type = types.bool;
+ default = false;
+ description = ''
+ If enabled, starts a Terraria server. The server can be connected to via tmux -S /var/lib/terraria/terraria.sock attach
+ for administration by users who are a part of the terraria group (use C-b d shortcut to detach again).
+ '';
+ };
+
+ port = mkOption {
+ type = types.int;
+ default = 7777;
+ description = ''
+ Specifies the port to listen on.
+ '';
+ };
+
+ maxPlayers = mkOption {
+ type = types.int;
+ default = 255;
+ description = ''
+ Sets the max number of players (between 1 and 255).
+ '';
+ };
+
+ password = mkOption {
+ type = types.nullOr types.str;
+ default = null;
+ description = ''
+ Sets the server password. Leave null for no password.
+ '';
+ };
+
+ messageOfTheDay = mkOption {
+ type = types.nullOr types.str;
+ default = null;
+ description = ''
+ Set the server message of the day text.
+ '';
+ };
+
+ worldPath = mkOption {
+ type = types.path;
+ default = null;
+ description = ''
+ The path to the world file (.wld) which should be loaded.
+ If no world exists at this path, one will be created with the size
+ specified by autoCreatedWorldSize.
+ '';
+ };
+
+ autoCreatedWorldSize = mkOption {
+ type = types.enum [ "small" "medium" "large" ];
+ default = "medium";
+ description = ''
+ Specifies the size of the auto-created world if worldPath does not
+ point to an existing world.
+ '';
+ };
+
+ banListPath = mkOption {
+ type = types.nullOr types.path;
+ default = null;
+ description = ''
+ The path to the ban list.
+ '';
+ };
+
+ secure = mkOption {
+ type = types.bool;
+ default = false;
+ description = "Adds additional cheat protection to the server.";
+ };
+
+ noUPnP = mkOption {
+ type = types.bool;
+ default = false;
+ description = "Disables automatic Universal Plug and Play.";
+ };
+ };
+ };
+
+ config = mkIf cfg.enable {
+ users.extraUsers.terraria = {
+ description = "Terraria server service user";
+ home = "/var/lib/terraria";
+ createHome = true;
+ uid = config.ids.uids.terraria;
+ };
+
+ users.extraGroups.terraria = {
+ gid = config.ids.gids.terraria;
+ members = [ "terraria" ];
+ };
+
+ systemd.services.terraria = {
+ description = "Terraria Server Service";
+ wantedBy = [ "multi-user.target" ];
+ after = [ "network.target" ];
+
+ serviceConfig = {
+ User = "terraria";
+ Type = "oneshot";
+ RemainAfterExit = true;
+ ExecStart = "${pkgs.tmux.bin}/bin/tmux -S /var/lib/terraria/terraria.sock new -d ${pkgs.terraria-server}/bin/TerrariaServer ${concatStringsSep " " flags}";
+ ExecStop = "${pkgs.tmux.bin}/bin/tmux -S /var/lib/terraria/terraria.sock send-keys Enter \"exit\" Enter";
+ };
+
+ postStart = ''
+ ${pkgs.coreutils}/bin/chmod 660 /var/lib/terraria/terraria.sock
+ ${pkgs.coreutils}/bin/chgrp terraria /var/lib/terraria/terraria.sock
+ '';
+ };
+ };
+}
diff --git a/pkgs/games/terraria-server/default.nix b/pkgs/games/terraria-server/default.nix
index 59dabf2f9cde..5fcb5063bbc8 100644
--- a/pkgs/games/terraria-server/default.nix
+++ b/pkgs/games/terraria-server/default.nix
@@ -16,6 +16,7 @@ stdenv.mkDerivation rec {
installPhase = ''
mkdir -p $out/bin
cp -r Linux $out/
+ chmod +x "$out/Linux/TerrariaServer.bin.x86_64"
ln -s "$out/Linux/TerrariaServer.bin.x86_64" $out/bin/TerrariaServer
# Fix "/lib64/ld-linux-x86-64.so.2" like references in ELF executables.
find "$out" | while read filepath; do