f51d27acb2
The bundled tool is sort of convenient, I first tried it when debugging an issue (that doesn't affect this module) and it seems a bit nicer. Change-Id: Ief948f8e46e51f05b04fe7628ab9298284f780e9 Reviewed-on: https://cl.tvl.fyi/c/depot/+/12444 Reviewed-by: sterni <sternenseemann@systemli.org> Tested-by: BuildkiteCI Autosubmit: sterni <sternenseemann@systemli.org>
126 lines
3.7 KiB
Nix
126 lines
3.7 KiB
Nix
# Companion module to minecraft-fabric.nix which automatically and regularly
|
|
# creates backups of all minecraft servers' worlds to a shared borg(1)
|
|
# repository.
|
|
#
|
|
# SPDX-License-Identifier: MIT
|
|
# SPDX-FileCopyrightText: 2023 sterni <sternenseemann@systemli.org>
|
|
{ pkgs, depot, config, lib, ... }:
|
|
|
|
let
|
|
inherit (depot.nix) getBins;
|
|
|
|
bins = getBins pkgs.borgbackup [ "borg" ]
|
|
// getBins pkgs.mcrcon [ "mcrcon" ]
|
|
// getBins pkgs.systemd [ "systemd-creds" ];
|
|
|
|
unvaried = ls: builtins.all (l: l == builtins.head ls) ls;
|
|
|
|
cfg = config.services.backup-minecraft-fabric-servers;
|
|
|
|
instances = lib.filterAttrs (_: i: i.enable) config.services.minecraft-fabric-server;
|
|
users = lib.mapAttrsToList (_: i: i.user) instances;
|
|
groups = lib.mapAttrsToList (_: i: i.group) instances;
|
|
|
|
mkBackupScript = instanceName: instanceCfg:
|
|
let
|
|
archivePrefix = "minecraft-fabric-${instanceName}-world-${builtins.baseNameOf instanceCfg.world}-";
|
|
in
|
|
|
|
pkgs.writeShellScript "backup-minecraft-fabric-${instanceName}" ''
|
|
export MCRCON_HOST="localhost"
|
|
export MCRCON_PORT="${toString instanceCfg.serverProperties."rcon.port"}"
|
|
# Unfortunately, mcrcon can't read the password from a file
|
|
export MCRCON_PASS="$(${bins.systemd-creds} cat "${instanceName}-rcon-password")"
|
|
|
|
${bins.mcrcon} save-all
|
|
unset MCRCON_PASS
|
|
|
|
# Give the server plenty of time to save
|
|
sleep 60
|
|
|
|
${bins.borg} ${lib.escapeShellArgs [
|
|
"create"
|
|
"--verbose" "--filter" "AMEU" "--list"
|
|
"--stats" "--show-rc"
|
|
"--compression" "zlib"
|
|
"${cfg.repository}::${archivePrefix}{now}"
|
|
instanceCfg.world
|
|
]}
|
|
|
|
${bins.borg} ${lib.escapeShellArgs [
|
|
"prune"
|
|
"--list"
|
|
"--show-rc"
|
|
"--glob-archives" "${archivePrefix}*"
|
|
"--keep-hourly" "168"
|
|
"--keep-daily" "31"
|
|
"--keep-monthly" "6"
|
|
"--keep-yearly" "2"
|
|
cfg.repository
|
|
]}
|
|
|
|
${bins.borg} compact ${lib.escapeShellArg cfg.repository}
|
|
'';
|
|
in
|
|
|
|
{
|
|
imports = [
|
|
./minecraft-fabric.nix
|
|
];
|
|
|
|
options = {
|
|
services.backup-minecraft-fabric-servers = {
|
|
enable = lib.mkEnableOption "backups of all Minecraft fabric servers";
|
|
|
|
repository = lib.mkOption {
|
|
type = lib.types.path;
|
|
description = "Path to the borg(1) repository to use for all backups.";
|
|
default = "/var/lib/backup/minecraft-fabric";
|
|
};
|
|
};
|
|
};
|
|
|
|
config = lib.mkIf (cfg.enable && builtins.length (builtins.attrNames instances) > 0) {
|
|
assertions = [
|
|
{
|
|
assertion = unvaried users && unvaried groups;
|
|
message = "all instances under services.minecraft-fabric-server must use the same user and group";
|
|
}
|
|
];
|
|
|
|
environment.systemPackages = [
|
|
pkgs.borgbackup
|
|
];
|
|
|
|
systemd = {
|
|
services.backup-minecraft-fabric-servers = {
|
|
description = "Backup world of all fabric based Minecraft servers";
|
|
wantedBy = [ ];
|
|
after = builtins.map
|
|
(name: "minecraft-fabric-${name}.service")
|
|
(builtins.attrNames instances);
|
|
|
|
script = lib.concatStrings (lib.mapAttrsToList mkBackupScript instances);
|
|
|
|
serviceConfig = {
|
|
Type = "oneshot";
|
|
User = builtins.head users;
|
|
Group = builtins.head groups;
|
|
LoadCredential = lib.mapAttrsToList
|
|
(instanceName: instanceCfg: "${instanceName}-rcon-password:${instanceCfg.rconPasswordFile}")
|
|
instances;
|
|
};
|
|
};
|
|
|
|
timers.backup-minecraft-fabric-servers = {
|
|
description = "Regularly backup Minecraft fabric servers";
|
|
wantedBy = [ "timers.target" ];
|
|
timerConfig = {
|
|
OnCalendar = "*-*-* 00/3:00:00";
|
|
Persistent = true;
|
|
RandomizedDelaySec = "1h";
|
|
};
|
|
};
|
|
};
|
|
};
|
|
}
|