feat(nixos): run Valheim in a guest #37
116
configs/nixos/guests/valheim.nix
Normal file
116
configs/nixos/guests/valheim.nix
Normal file
@@ -0,0 +1,116 @@
|
|||||||
|
{
|
||||||
|
config,
|
||||||
|
lib,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
let
|
||||||
|
cfg = config.guests.valheim;
|
||||||
|
steamDir = "/data/steamcmd";
|
||||||
|
installDir = "/data/server";
|
||||||
|
stateDir = "/data/state";
|
||||||
|
in
|
||||||
|
{
|
||||||
|
options.guests.valheim = {
|
||||||
|
steamPath = lib.mkOption {
|
||||||
|
type = lib.types.nullOr lib.types.str;
|
||||||
|
default = null;
|
||||||
|
example = "/srv/steamcmd";
|
||||||
|
description = "Host path bind-mounted as SteamCMD's metadata directory.";
|
||||||
|
};
|
||||||
|
|
||||||
|
installPath = lib.mkOption {
|
||||||
|
type = lib.types.nullOr lib.types.str;
|
||||||
|
default = null;
|
||||||
|
example = "/srv/valheim/server";
|
||||||
|
description = "Host path bind-mounted as Valheim's server install directory.";
|
||||||
|
};
|
||||||
|
|
||||||
|
statePath = lib.mkOption {
|
||||||
|
type = lib.types.nullOr lib.types.str;
|
||||||
|
default = null;
|
||||||
|
example = "/srv/valheim/state";
|
||||||
|
description = "Host path bind-mounted as Valheim's world state directory.";
|
||||||
|
};
|
||||||
|
|
||||||
|
serverName = lib.mkOption {
|
||||||
|
type = lib.types.str;
|
||||||
|
default = "valheim";
|
||||||
|
description = "Name shown for the Valheim server.";
|
||||||
|
};
|
||||||
|
|
||||||
|
port = lib.mkOption {
|
||||||
|
type = lib.types.port;
|
||||||
|
default = 2456;
|
||||||
|
description = "Base UDP port for the Valheim server.";
|
||||||
|
};
|
||||||
|
|
||||||
|
passwordSecret = lib.mkOption {
|
||||||
|
type = lib.types.str;
|
||||||
|
default = "services/game-servers/password";
|
||||||
|
description = "SOPS secret name containing the Valheim server password.";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = lib.mkIf cfg.enable {
|
||||||
|
assertions = [
|
||||||
|
{
|
||||||
|
assertion = cfg.steamPath != null;
|
||||||
|
message = "guests.valheim.steamPath must point at the host directory that stores SteamCMD metadata.";
|
||||||
|
}
|
||||||
|
{
|
||||||
|
assertion = cfg.installPath != null;
|
||||||
|
message = "guests.valheim.installPath must point at the host directory that stores Valheim server files.";
|
||||||
|
}
|
||||||
|
{
|
||||||
|
assertion = cfg.statePath != null;
|
||||||
|
message = "guests.valheim.statePath must point at the host directory that stores Valheim world state.";
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
guests.valheim = {
|
||||||
|
interior.modules.services.games.valheim = {
|
||||||
|
enable = true;
|
||||||
|
inherit (cfg) serverName port;
|
||||||
|
inherit steamDir installDir stateDir;
|
||||||
|
passwordFile = "/run/secrets/${cfg.passwordSecret}";
|
||||||
|
};
|
||||||
|
|
||||||
|
mounts = {
|
||||||
|
${steamDir} = {
|
||||||
|
directory = cfg.steamPath;
|
||||||
|
owner = config.registry."stable-uid".${config.networking.hostName}.steamcmd;
|
||||||
|
group = "storage";
|
||||||
|
mode = "0770";
|
||||||
|
};
|
||||||
|
${installDir} = {
|
||||||
|
directory = cfg.installPath;
|
||||||
|
owner = config.registry."stable-uid".${config.networking.hostName}.steamcmd;
|
||||||
|
group = "storage";
|
||||||
|
mode = "0770";
|
||||||
|
};
|
||||||
|
${stateDir} = {
|
||||||
|
directory = cfg.statePath;
|
||||||
|
owner = config.registry."stable-uid".${config.networking.hostName}.steamcmd;
|
||||||
|
group = "storage";
|
||||||
|
mode = "0770";
|
||||||
|
};
|
||||||
|
${"/run/secrets/${cfg.passwordSecret}"} = {
|
||||||
|
file = config.sops.secrets.${cfg.passwordSecret}.path;
|
||||||
|
readOnly = true;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
limits = {
|
||||||
|
memory = lib.mkDefault "4G";
|
||||||
|
cpu = lib.mkDefault "400%";
|
||||||
|
tasksMax = lib.mkDefault 1024;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
sops.secrets.${cfg.passwordSecret} = {
|
||||||
|
owner = "root";
|
||||||
|
group = "storage";
|
||||||
|
mode = "0440";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -65,21 +65,25 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
registry.address.pikachu.self = "10.23.10.102";
|
registry.address.pikachu.self = "10.23.10.102";
|
||||||
registry.route.pikachu.self = {
|
|
||||||
domain = "valheim.alexion.dev";
|
|
||||||
udp.ports = [
|
|
||||||
config.modules.services.games.valheim.port
|
|
||||||
(config.modules.services.games.valheim.port + 1)
|
|
||||||
];
|
|
||||||
};
|
|
||||||
|
|
||||||
modules.services.games.valheim = {
|
guests.valheim = {
|
||||||
enable = true;
|
enable = true;
|
||||||
steamDir = "/pikachu/data/steamcmd";
|
vlan = 20;
|
||||||
installDir = "/pikachu/data/servers/valheim/main";
|
address = "10.23.20.197";
|
||||||
stateDir = "/pikachu/media/games/servers/valheim/main";
|
steamPath = "/pikachu/data/steamcmd";
|
||||||
|
installPath = "/pikachu/data/servers/valheim/main";
|
||||||
|
statePath = "/pikachu/media/games/servers/valheim/main";
|
||||||
serverName = "main";
|
serverName = "main";
|
||||||
password = "foobarbaz";
|
reverseProxy = {
|
||||||
|
enable = true;
|
||||||
|
route = {
|
||||||
|
domain = "valheim.alexion.dev";
|
||||||
|
udp.ports = [
|
||||||
|
config.guests.valheim.port
|
||||||
|
(config.guests.valheim.port + 1)
|
||||||
|
];
|
||||||
|
};
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
guests.actualbudget = {
|
guests.actualbudget = {
|
||||||
|
|||||||
@@ -42,6 +42,10 @@ in
|
|||||||
|
|
||||||
users.groups.storage.gid = 10000;
|
users.groups.storage.gid = 10000;
|
||||||
|
|
||||||
|
security.sudo.extraConfig = ''
|
||||||
|
Defaults env_keep += "EDITOR VISUAL"
|
||||||
|
'';
|
||||||
|
|
||||||
time.timeZone = "America/New_York";
|
time.timeZone = "America/New_York";
|
||||||
i18n.defaultLocale = "en_GB.UTF-8";
|
i18n.defaultLocale = "en_GB.UTF-8";
|
||||||
|
|
||||||
@@ -87,6 +91,10 @@ in
|
|||||||
|
|
||||||
update = "sudo nixos-rebuild switch --flake .#${config.networking.hostName}";
|
update = "sudo nixos-rebuild switch --flake .#${config.networking.hostName}";
|
||||||
cleanup = "sudo nix-collect-garbage -d";
|
cleanup = "sudo nix-collect-garbage -d";
|
||||||
|
sops-host = {
|
||||||
|
command = "sudo --preserve-env=EDITOR,VISUAL env SOPS_AGE_KEY_FILE=/var/lib/sops-nix/key.txt nix shell nixpkgs#sops nixpkgs#neovim -c sops";
|
||||||
|
abbreviation = false;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
modules.shell.fish.enable = lib.mkDefault true;
|
modules.shell.fish.enable = lib.mkDefault true;
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
{
|
{
|
||||||
config,
|
config,
|
||||||
lib,
|
lib,
|
||||||
|
pkgs,
|
||||||
...
|
...
|
||||||
}:
|
}:
|
||||||
{
|
{
|
||||||
@@ -37,9 +38,9 @@
|
|||||||
description = "Base UDP port for the Valheim server.";
|
description = "Base UDP port for the Valheim server.";
|
||||||
};
|
};
|
||||||
|
|
||||||
password = lib.mkOption {
|
passwordFile = lib.mkOption {
|
||||||
type = lib.types.str;
|
type = lib.types.path;
|
||||||
description = "Valheim server password.";
|
description = "Runtime file containing the Valheim server password.";
|
||||||
};
|
};
|
||||||
|
|
||||||
};
|
};
|
||||||
@@ -47,6 +48,12 @@
|
|||||||
config = lib.mkIf config.modules.services.games.valheim.enable (
|
config = lib.mkIf config.modules.services.games.valheim.enable (
|
||||||
let
|
let
|
||||||
service = config.modules.services.games.valheim;
|
service = config.modules.services.games.valheim;
|
||||||
|
startScript = pkgs.writeShellScript "valheim-start" ''
|
||||||
|
set -eu
|
||||||
|
|
||||||
|
password="$(${pkgs.coreutils}/bin/tr -d '\n' < ${lib.escapeShellArg service.passwordFile})"
|
||||||
|
exec ./valheim_server.x86_64 "$@" -password "$password"
|
||||||
|
'';
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
modules.services.steamcmd = {
|
modules.services.steamcmd = {
|
||||||
@@ -54,7 +61,7 @@
|
|||||||
inherit (service) steamDir installDir stateDir;
|
inherit (service) steamDir installDir stateDir;
|
||||||
serviceName = "valheim";
|
serviceName = "valheim";
|
||||||
appId = 896660;
|
appId = 896660;
|
||||||
executable = "./valheim_server.x86_64";
|
executable = "${startScript}";
|
||||||
arguments = [
|
arguments = [
|
||||||
"-nographics"
|
"-nographics"
|
||||||
"-batchmode"
|
"-batchmode"
|
||||||
@@ -66,8 +73,6 @@
|
|||||||
service.serverName
|
service.serverName
|
||||||
"-savedir"
|
"-savedir"
|
||||||
service.stateDir
|
service.stateDir
|
||||||
"-password"
|
|
||||||
service.password
|
|
||||||
"-public"
|
"-public"
|
||||||
"0"
|
"0"
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -113,11 +113,11 @@
|
|||||||
|
|
||||||
systemd.tmpfiles.rules = [
|
systemd.tmpfiles.rules = [
|
||||||
"d ${service.steamDir} 0770 ${user} ${group} - -"
|
"d ${service.steamDir} 0770 ${user} ${group} - -"
|
||||||
"z ${service.steamDir} 0770 ${user} ${group} - -"
|
"Z ${service.steamDir} 0770 ${user} ${group} - -"
|
||||||
"d ${service.installDir} 0770 ${user} ${group} - -"
|
"d ${service.installDir} 0770 ${user} ${group} - -"
|
||||||
"z ${service.installDir} 0770 ${user} ${group} - -"
|
"Z ${service.installDir} 0770 ${user} ${group} - -"
|
||||||
"d ${service.stateDir} 0770 ${user} ${group} - -"
|
"d ${service.stateDir} 0770 ${user} ${group} - -"
|
||||||
"z ${service.stateDir} 0770 ${user} ${group} - -"
|
"Z ${service.stateDir} 0770 ${user} ${group} - -"
|
||||||
];
|
];
|
||||||
|
|
||||||
systemd.services = {
|
systemd.services = {
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ services:
|
|||||||
users:
|
users:
|
||||||
alexion:
|
alexion:
|
||||||
password: ENC[AES256_GCM,data:SSyo2l6tCjgjxIuCEREIxU+inw2WHtJJR0/IEKUqefFfuAdA5Nf+lH/jfPw=,iv:9225w1Yur3KL3dBr1ct1aPvDdVJ94O8q9qAUiRrj8Nc=,tag:qZgx4XieVq4BcxkTZc0KGQ==,type:str]
|
password: ENC[AES256_GCM,data:SSyo2l6tCjgjxIuCEREIxU+inw2WHtJJR0/IEKUqefFfuAdA5Nf+lH/jfPw=,iv:9225w1Yur3KL3dBr1ct1aPvDdVJ94O8q9qAUiRrj8Nc=,tag:qZgx4XieVq4BcxkTZc0KGQ==,type:str]
|
||||||
|
game-servers:
|
||||||
|
password: ENC[AES256_GCM,data:Wtq03MLRnc6/,iv:HhbiP8ZcltVGbCNDdP/gH5mCZXhVCTFv1DGzfrrgQG8=,tag:srsqcBQSD/HU8zFsuFimJQ==,type:str]
|
||||||
sops:
|
sops:
|
||||||
age:
|
age:
|
||||||
- enc: |
|
- enc: |
|
||||||
@@ -42,7 +44,7 @@ sops:
|
|||||||
28uWUpKzzgErq1aEbKNgh4cU5TH2RlAHjtzRC3ARUu9hlW6lnB4pSw==
|
28uWUpKzzgErq1aEbKNgh4cU5TH2RlAHjtzRC3ARUu9hlW6lnB4pSw==
|
||||||
-----END AGE ENCRYPTED FILE-----
|
-----END AGE ENCRYPTED FILE-----
|
||||||
recipient: age1wf5s0n0tgt6ld2ysgu9dc67mj8ylwecgl4utzg7hqwy3kut9zyms7aglmh
|
recipient: age1wf5s0n0tgt6ld2ysgu9dc67mj8ylwecgl4utzg7hqwy3kut9zyms7aglmh
|
||||||
lastmodified: "2026-08-17T00:30:50Z"
|
lastmodified: "2026-09-10T13:40:57Z"
|
||||||
mac: ENC[AES256_GCM,data:bNOsakxhrlO1quwLGA8inruHZ6tFpUo6cKMlV10jkJ75GOFzl5jzflGIuPfyncJCtIaZRBA2tS17TpGEEJozmbB2xQkeu67NmzOpEdoJzZWs/bY0FslbpGf8aGAd15BjOYNyMMNFavt0anRgEnsYXTDCflDCHLg1wvAvwtgRGYs=,iv:kfpzzWn3BHkSTQ8qpcD9lYn4wnZ/3nj/41SR25vUahE=,tag:8D1LSEBJIQcwYK9ETomwIw==,type:str]
|
mac: ENC[AES256_GCM,data:3nu+ho3TNDnGE55LIaHqJxlMs1hWVK2wwiJiBIDH0jop9Wo/AgaUXzwXY1MwMXJHhGrkcjhwWWJdEgxX88NNFTc59b4kLtRqE3bfNXoQ3y3giyDMFyVE9YCNd4jyl+pLhpVSZA/+TKEIBpdZRwaXHrYSaLtysg/OdzXing0l3SM=,iv:C/vZ6X1HikuBn+pWTlPIfCfg5mtW2i4P6XLwN1VwF6k=,tag:UQl10fueTwCtXuirP5+C/g==,type:str]
|
||||||
unencrypted_suffix: _unencrypted
|
unencrypted_suffix: _unencrypted
|
||||||
version: 3.13.3
|
version: 3.13.3
|
||||||
|
|||||||
Reference in New Issue
Block a user