# 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 \ -p 3478:3478 -p 3478:3478/udp \ -p 65532-65535:65532-65535/udp \ -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 \ -e SELKIES_TURN_HOST=10.0.0.1 \ -e SELKIES_TURN_PROTOCOL=udp \ -e SELKIES_TURN_PORT=3478 \ -e TURN_MIN_PORT=65532 -e TURN_MAX_PORT=65535 \ -v /var/lib/selkies/home:/home/ubuntu \ selkies-gw:local ''; ExecStop = "${pkgs.docker}/bin/docker stop selkies"; }; }; }; }