feat(nixos): add pikachu homepage guest

This commit is contained in:
2026-09-15 12:20:55 -04:00
parent fca37666d3
commit 1caf5aa3e0
9 changed files with 243 additions and 2 deletions

View File

@@ -55,5 +55,7 @@
- On Pikachu, `nix copy --to ssh://pikachu` accepted a small `nix store add-file` probe,
but rejected locally built system paths that lacked trusted signatures.
Use a sudo `nix-store --import` path for unsigned deploy closures.
- Public DNS entries are not managed through the NixOS configuration.
Do not describe NixOS route choices as public-DNS management.
- Do not hand-author `hardware-configuration.nix` for a host that has not been generated on that machine.
Generate it on the target system or live ISO, then stage it before evaluating the flake.

View File

@@ -25,6 +25,14 @@ in
}
];
registry.homepage.${config.networking.hostName}.actualbudget = {
tab = "Apps";
section = "Other";
name = "Actual Budget";
description = "Finance";
icon = "actual-budget.png";
};
guests.actualbudget = {
interior = {
modules.services.actualbudget = {

View File

@@ -90,6 +90,15 @@ in
}
];
registry.homepage.${config.networking.hostName}.copyparty = {
tab = "Apps";
section = "Files";
name = "Copyparty";
description = "Files";
icon = "copyparty.svg";
href = lib.mkIf cfg.reverseProxy.enable "https://${cfg.reverseProxy.route.domain}/?h";
};
guests.copyparty = {
interior.modules.services.copyparty = {
enable = true;

View File

@@ -0,0 +1,32 @@
{
config,
lib,
...
}:
let
cfg = config.guests.homepage;
in
{
options.guests.homepage = {
allowedHosts = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [ "home" ];
description = "Host headers accepted by Homepage.";
};
};
config = lib.mkIf cfg.enable {
guests.homepage = {
interior.modules.services.homepage = {
enable = true;
inherit (cfg) allowedHosts;
};
limits = {
memory = lib.mkDefault "1G";
cpu = lib.mkDefault "100%";
tasksMax = lib.mkDefault 256;
};
};
};
}

View File

@@ -48,6 +48,14 @@ in
}
];
registry.homepage.${config.networking.hostName}.navidrome = {
tab = "Apps";
section = "Media";
name = "Navidrome";
description = "Music";
icon = "navidrome.png";
};
guests.navidrome = {
interior.modules.services.navidrome.enable = true;

View File

@@ -144,6 +144,12 @@
address = "10.23.20.109";
};
guests.homepage = {
enable = true;
vlan = 20;
address = "10.23.20.46";
};
guests.copyparty = {
enable = true;
vlan = 20;

View File

@@ -0,0 +1,132 @@
{
config,
lib,
registry,
...
}:
let
cfg = config.modules.services.homepage;
mkRegistryPath = registryName: host: entry: "registry.${registryName}.${host}.${entry}";
homepageEntries = lib.concatLists (
lib.mapAttrsToList (
host: entries:
lib.mapAttrsToList (entry: homepage: {
inherit host entry homepage;
composite = registry.join host entry;
}) entries
) registry.homepage
);
mkHref = item:
if item.homepage.href != null then
item.homepage.href
else if (item.composite.route or null) != null && item.composite.route.port != null then
"https://${item.composite.route.domain}"
else
throw "${mkRegistryPath "homepage" item.host item.entry} needs href or a matching TCP route entry";
mkServiceAttrs = item:
lib.filterAttrs (_: value: value != null) {
href = mkHref item;
inherit (item.homepage) description icon;
};
sortedEntries = lib.sort (left: right: left.homepage.name < right.homepage.name) homepageEntries;
insertEntry = acc: item:
acc
// {
${item.homepage.tab} = (acc.${item.homepage.tab} or { })
// {
${item.homepage.section} = (acc.${item.homepage.tab}.${item.homepage.section} or [ ]) ++ [
{ ${item.homepage.name} = mkServiceAttrs item; }
];
};
};
entriesByTab = lib.foldl' insertEntry { } sortedEntries;
tabOrder = [
"Apps"
"Download"
"System"
];
orderedTabs = tabOrder ++ lib.filter (tab: !lib.elem tab tabOrder) (lib.attrNames entriesByTab);
mkTab = tab:
let
sections = entriesByTab.${tab};
in
{
${tab} = map (section: { ${section} = sections.${section}; }) (lib.attrNames sections);
};
homepageServices = map mkTab (lib.filter (tab: lib.hasAttr tab entriesByTab) orderedTabs);
in
{
options.modules.services.homepage = {
enable = lib.mkEnableOption "Homepage Dashboard";
listenPort = lib.mkOption {
type = lib.types.port;
default = 80;
description = "Port for Homepage to bind to.";
};
allowedHosts = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [ "home" ];
description = "Host headers accepted by Homepage.";
};
};
config = lib.mkIf cfg.enable {
services.homepage-dashboard = {
enable = true;
openFirewall = true;
inherit (cfg) listenPort;
allowedHosts = lib.concatStringsSep "," cfg.allowedHosts;
services = homepageServices;
settings = {
title = "Tracion";
theme = "dark";
color = "slate";
showStats = false;
hideVersion = true;
bookmarksStyle = "icons";
useEqualHeights = true;
layout = {
Apps = {
style = "row";
columns = 1;
tab = "Apps";
Media = {
style = "row";
columns = 2;
};
Files = {
style = "row";
columns = 2;
};
Other = {
style = "row";
columns = 3;
iconsOnly = true;
};
};
Download = {
style = "row";
columns = 1;
tab = "Download";
};
System = {
tab = "System";
};
};
};
};
};
}

View File

@@ -0,0 +1,45 @@
{ lib, ... }:
{
type = lib.types.submodule {
options = {
tab = lib.mkOption {
type = lib.types.str;
example = "Apps";
description = "Homepage tab that contains this entry.";
};
section = lib.mkOption {
type = lib.types.str;
example = "Media";
description = "Homepage section that contains this entry.";
};
name = lib.mkOption {
type = lib.types.str;
example = "Navidrome";
description = "Homepage display name for this entry.";
};
description = lib.mkOption {
type = lib.types.nullOr lib.types.str;
default = null;
example = "Music";
description = "Homepage description for this entry.";
};
icon = lib.mkOption {
type = lib.types.nullOr lib.types.str;
default = null;
example = "navidrome.png";
description = "Homepage icon for this entry.";
};
href = lib.mkOption {
type = lib.types.nullOr lib.types.str;
default = null;
example = "https://music.alexion.dev";
description = "Homepage link override for this entry.";
};
};
};
}

View File

@@ -50,10 +50,9 @@
- immich
- romm
- gitea
- vikunja (?)
- nginxproxymanager (gradual migration to nginx guest)
- adguard
- homepage
- manage DNS rewrites for local service names such as `home`
## raichu proxmox -> nixos
- dwn torrent (vpn)