nixos/services/selkies.nix

86 lines
3.4 KiB
Nix
Raw Normal View History

# services/selkies.nix — Guild Wars in a browser via Selkies
#
# Replaces the Neko attempt (services/neko.nix, now unimported): Neko's
# absolute-pointer input model can't handle Guild Wars' exclusive mouse grab.
# Selkies captures the mouse client-side with the browser Pointer Lock API and
# sends *relative* movement, so the grab is a non-issue — and it uses the GPU
# (NVENC + EGL) instead of software rendering.
#
# Reach it at selkies.nordhammer.it (Authelia-gated). The Wine prefix with
# Guild Wars already installed is reused from the old Neko home, seeded into
# /var/lib/selkies/home/.wine (see the deploy note in the repo).
{ config, pkgs, lib, ... }:
let
# Selkies' NVIDIA EGL desktop (Ubuntu 24.04) plus Wine for the 32-bit GW
# client. Built from stdin (no build context); see neko.nix for the why.
dockerfile = pkgs.writeText "selkies-gw.Dockerfile" ''
FROM ghcr.io/selkies-project/nvidia-egl-desktop:24.04
USER root
RUN add-apt-repository -y multiverse \
&& dpkg --add-architecture i386 \
&& apt-get update \
&& DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
wine wine32 wine64 winbind cabextract ca-certificates wget \
&& rm -rf /var/lib/apt/lists/*
USER 1000
'';
in
{
config = lib.mkIf (config.networking.hostName == "FredOS-Mediaserver") {
virtualisation.docker.enable = true;
# GPU into the container (CDI: nvidia.com/gpu=all) + 32-bit host GL libs so
# the toolkit can expose them to the 32-bit Wine/GW OpenGL stack.
hardware.nvidia-container-toolkit.enable = true;
hardware.graphics.enable32Bit = true;
systemd.tmpfiles.rules = [
"d /var/lib/selkies 0755 root root -"
# Container user is uid/gid 1000 and must own its home.
"d /var/lib/selkies/home 0755 1000 1000 -"
];
systemd.services.selkies = {
description = "Selkies Guild Wars in a browser (EGL desktop + Wine + NVENC)";
after = [ "docker.service" "network-online.target" ];
requires = [ "docker.service" ];
wants = [ "network-online.target" ];
wantedBy = [ "multi-user.target" ];
startLimitIntervalSec = 600;
startLimitBurst = 3;
serviceConfig = {
Restart = "on-failure";
RestartSec = "30s";
TimeoutStartSec = "3600";
ExecStartPre = [
"-${pkgs.docker}/bin/docker rm -f selkies"
"${pkgs.writeShellScript "selkies-build" ''
exec ${pkgs.docker}/bin/docker build -t selkies-gw:local - < ${dockerfile}
''}"
];
ExecStart = pkgs.writeShellScript "selkies-run" ''
exec ${pkgs.docker}/bin/docker run --rm --name selkies \
--device=nvidia.com/gpu=all \
-e NVIDIA_VISIBLE_DEVICES=all \
-e NVIDIA_DRIVER_CAPABILITIES=all \
--shm-size=2g \
-p 127.0.0.1:8093:8080 \
-e TZ=Europe/Stockholm \
-e DISPLAY_SIZEW=1280 -e DISPLAY_SIZEH=720 \
-e DISPLAY_REFRESH=30 -e DISPLAY_DPI=96 -e DISPLAY_CDEPTH=24 \
-e PASSWD=selkies \
-e SELKIES_ENCODER=nvh264enc \
-e SELKIES_VIDEO_BITRATE=8000 \
-e SELKIES_FRAMERATE=30 \
-e SELKIES_ENABLE_BASIC_AUTH=false \
-v /var/lib/selkies/home:/home/ubuntu \
selkies-gw:local
'';
ExecStop = "${pkgs.docker}/bin/docker stop selkies";
};
};
};
}