feat(nixos): deploy RomM guest

This commit is contained in:
2026-10-19 20:27:46 -04:00
parent f160fd15a2
commit db75cb2d61
3 changed files with 249 additions and 1 deletions

View File

@@ -0,0 +1,228 @@
{
config,
lib,
...
}:
let
cfg = config.guests.romm;
environmentFile = "/run/secrets/romm.env";
rommUid = config.registry."stable-uid".${config.networking.hostName}.romm;
redisUid = config.registry."stable-uid".${config.networking.hostName}."redis-romm";
in
{
options.guests.romm = {
dataPath = lib.mkOption {
type = lib.types.nullOr lib.types.str;
default = null;
example = "/srv/romm";
description = "Host path mounted as RomM application data.";
};
postgresqlPath = lib.mkOption {
type = lib.types.nullOr lib.types.str;
default = null;
example = "/srv/romm/postgresql";
description = "Host path mounted as RomM's PostgreSQL data.";
};
redisPath = lib.mkOption {
type = lib.types.nullOr lib.types.str;
default = null;
example = "/srv/romm/redis";
description = "Host path mounted as RomM's Redis data.";
};
romsPath = lib.mkOption {
type = lib.types.nullOr lib.types.str;
default = null;
example = "/srv/roms";
description = "Host path mounted as RomM's ROM library.";
};
biosPath = lib.mkOption {
type = lib.types.nullOr lib.types.str;
default = null;
example = "/srv/bios";
description = "Host path mounted as RomM's BIOS library.";
};
usersPath = lib.mkOption {
type = lib.types.nullOr lib.types.str;
default = null;
example = "/srv/romm/users";
description = "Host path mounted as RomM's user assets.";
};
igdbClientIdSecret = lib.mkOption {
type = lib.types.str;
default = "services/romm/igdb/client-id";
description = "SOPS secret name containing RomM's IGDB client ID.";
};
igdbClientSecretSecret = lib.mkOption {
type = lib.types.str;
default = "services/romm/igdb/client-secret";
description = "SOPS secret name containing RomM's IGDB client secret.";
};
screenScraperUserSecret = lib.mkOption {
type = lib.types.str;
default = "services/romm/screenscraper/user";
description = "SOPS secret name containing RomM's ScreenScraper user name.";
};
screenScraperPasswordSecret = lib.mkOption {
type = lib.types.str;
default = "services/romm/screenscraper/password";
description = "SOPS secret name containing RomM's ScreenScraper password.";
};
steamGridDbApiKeySecret = lib.mkOption {
type = lib.types.str;
default = "services/romm/steamgriddb/api-key";
description = "SOPS secret name containing RomM's SteamGridDB API key.";
};
};
config = lib.mkIf cfg.enable {
assertions =
map
(path: {
assertion = path.value != null;
message = "guests.romm.${path.name} must point at the host directory that stores ${path.description}.";
})
[
{
name = "dataPath";
value = cfg.dataPath;
description = "RomM application data";
}
{
name = "postgresqlPath";
value = cfg.postgresqlPath;
description = "RomM PostgreSQL data";
}
{
name = "redisPath";
value = cfg.redisPath;
description = "RomM Redis data";
}
{
name = "romsPath";
value = cfg.romsPath;
description = "RomM's ROM library";
}
{
name = "biosPath";
value = cfg.biosPath;
description = "RomM's BIOS library";
}
{
name = "usersPath";
value = cfg.usersPath;
description = "RomM's user assets";
}
];
registry."stable-uid".${config.networking.hostName} = {
romm = 20005;
"redis-romm" = 20006;
};
guests.romm = {
interior = {
networking.firewall.allowedTCPPorts = [ 80 ];
services.romm = {
enable = true;
group = "storage";
database.createLocally = true;
redis.createLocally = true;
watcher.enable = true;
nginx.virtualHost = cfg.reverseProxy.route.domain;
inherit environmentFile;
};
users.users = {
romm.uid = rommUid;
redis-romm.uid = redisUid;
};
};
mounts = {
"/var/lib/romm" = {
directory = cfg.dataPath;
owner = rommUid;
group = "storage";
mode = "2770";
};
"/var/lib/postgresql" = {
directory = cfg.postgresqlPath;
owner = config.ids.uids.postgres;
group = "storage";
mode = "0700";
};
"/var/lib/redis-romm" = {
directory = cfg.redisPath;
owner = redisUid;
group = "storage";
mode = "0700";
};
"/var/lib/romm/library/roms" = {
directory = cfg.romsPath;
owner = rommUid;
group = "storage";
mode = "2770";
};
"/var/lib/romm/library/bios" = {
directory = cfg.biosPath;
owner = rommUid;
group = "storage";
mode = "2770";
};
"/var/lib/romm/assets/users" = {
directory = cfg.usersPath;
owner = rommUid;
group = "storage";
mode = "2770";
};
${environmentFile} = {
file = config.sops.templates."romm.env".path;
readOnly = true;
};
};
limits = {
memory = lib.mkDefault "4G";
cpu = lib.mkDefault "200%";
tasksMax = lib.mkDefault 512;
};
};
sops.secrets =
lib.genAttrs
[
cfg.igdbClientIdSecret
cfg.igdbClientSecretSecret
cfg.screenScraperUserSecret
cfg.screenScraperPasswordSecret
cfg.steamGridDbApiKeySecret
]
(_: {
sopsFile = ../secrets + "/${config.networking.hostName}.yaml";
});
sops.templates."romm.env" = {
owner = "root";
group = "storage";
mode = "0440";
content = ''
IGDB_CLIENT_ID=${config.sops.placeholder.${cfg.igdbClientIdSecret}}
IGDB_CLIENT_SECRET=${config.sops.placeholder.${cfg.igdbClientSecretSecret}}
SCREENSCRAPER_USER=${config.sops.placeholder.${cfg.screenScraperUserSecret}}
SCREENSCRAPER_PASSWORD=${config.sops.placeholder.${cfg.screenScraperPasswordSecret}}
STEAMGRIDDB_API_KEY=${config.sops.placeholder.${cfg.steamGridDbApiKeySecret}}
'';
};
};
}

View File

@@ -88,6 +88,26 @@
};
};
guests.romm = {
enable = true;
vlan = 20;
address = "10.23.20.44";
dataPath = "/pikachu/data/romm";
postgresqlPath = "/pikachu/data/romm/postgresql";
redisPath = "/pikachu/data/romm/redis";
romsPath = "/pikachu/media/games/emulation/roms";
biosPath = "/pikachu/media/games/emulation/bios";
usersPath = "/pikachu/media/games/emulation/users";
reverseProxy = {
enable = true;
route = {
domain = "romm.alexion.dev";
protocol = "https";
port = 80;
};
};
};
guests.reverse-proxy = {
enable = true;
vlan = 20;

View File

@@ -12,7 +12,7 @@ let
in
{
# Next stable UID: 20005
# Next stable UID: 20007
type = lib.types.ints.positive;