iac/nixos/hosts/asxpio-services/containers.nix
Sergei Poljanski 71047d179d
Some checks failed
nixos / asxpio-services (push) Failing after 54s
ci: run Renovate from asxpi/renovate
Renovate now runs from its own repository for every repo, with the
shared rules in its preset. Extend that preset here and keep only what
is specific to this repo (the Ansible var manager, the Paper JRE, the
runner and nix notes); drop the workflow that ran it for iac alone.

Assisted-by: Claude:opus-5-5
2026-11-13 00:00:06 +04:00

83 lines
3.1 KiB
Nix

# Minecraft and SillyTavern. Their data stays in /opt and is not managed
# here.
{ config, ... }:
let
# Paper needs Java 21; the tag is a floating JRE line
javaImage = "docker.io/library/eclipse-temurin:21-jre";
paperJar = "paper-1.21.11-54.jar";
# Time for the server to save the world before it is killed
stopTimeout = 120;
# renovate: datasource=docker depName=ghcr.io/sillytavern/sillytavern
sillytavernVersion = "1.19.0";
in
{
# Paper server; the jar and the world live in /opt/minecraft. Not an
# oci-containers unit: the console needs a TTY and an open stdin, and
# `docker run -it` refuses to start without a terminal. The container
# is created first and then attached to, output only.
#
# docker attach minecraft console; leave with Ctrl-p Ctrl-q
systemd.services.docker-minecraft = {
description = "Minecraft (Paper)";
wantedBy = [ "multi-user.target" ];
wants = [ "network-online.target" ];
# requires: Docker restarting stops the server first, with time to save
requires = [ "docker.service" ];
after = [
"docker.service"
"network-online.target"
];
unitConfig.ConditionPathExists = "/opt/.restored";
path = [ config.virtualisation.docker.package ];
preStart = ''
docker rm -f minecraft || true
docker create --name minecraft --tty --interactive \
--stop-timeout ${toString stopTimeout} \
--workdir /data --volume /opt/minecraft:/data \
--publish 25565:25565 --publish 19132:19132/udp \
${javaImage} java -Xmx4G -Xms2G -jar ${paperJar} nogui
'';
script = "exec docker start --attach minecraft";
preStop = "docker stop minecraft || true";
postStop = "docker rm -f minecraft || true";
serviceConfig = {
TimeoutStartSec = 0; # the first start pulls the image
TimeoutStopSec = stopTimeout + 30;
# A JVM stopped by SIGTERM exits 143 after saving; that is a clean stop
SuccessExitStatus = [ 143 ];
Restart = "on-failure";
};
};
# Reachable on the WireGuard address only
virtualisation.oci-containers.containers.sillytavern = {
image = "ghcr.io/sillytavern/sillytavern:${sillytavernVersion}";
ports = [ "10.20.0.20:8000:8000" ];
volumes = [
"/opt/sillytavern/config:/home/node/app/config"
"/opt/sillytavern/data:/home/node/app/data"
"/opt/sillytavern/plugins:/home/node/app/plugins"
];
};
# The WireGuard address must exist before a port can be bound to it
systemd.services.docker-sillytavern = {
unitConfig.ConditionPathExists = "/opt/.restored";
after = [ "wireguard-asxpio-services.service" ];
requires = [ "wireguard-asxpio-services.service" ];
};
# Published ports reach the containers without passing these rules over
# IPv4. Over IPv6 Docker's proxy listens on the host, so they are needed.
networking.firewall.allowedTCPPorts = [ 25565 ];
networking.firewall.allowedUDPPorts = [ 19132 ];
systemd.tmpfiles.rules = [
"d /opt 0755 root root -"
"d /opt/minecraft 0755 root root -"
"d /opt/sillytavern/config 0755 root root -"
"d /opt/sillytavern/data 0755 root root -"
"d /opt/sillytavern/plugins 0755 root root -"
];
}