Nix-Vibe public snapshot (squashed history)
Current state of main at 0240060 feat(hp-laptop): install TeleportFling from its flake. History intentionally collapsed to a single commit; this repo mirrors only the latest state.
This commit is contained in:
@@ -0,0 +1,268 @@
|
||||
# NixOS configuration for homeserver-1
|
||||
# Headless server environment (Command Line Interface only)
|
||||
|
||||
{ config
|
||||
, pkgs
|
||||
, lib
|
||||
, inputs
|
||||
, ...
|
||||
}:
|
||||
|
||||
{
|
||||
imports = [
|
||||
../../modules/core/common.nix
|
||||
(import ../../modules/storage/disko.nix {
|
||||
inherit inputs lib config;
|
||||
diskoConfigPath = ./disko-config.nix;
|
||||
})
|
||||
./hardware-configuration.nix
|
||||
../../modules/core/management.nix
|
||||
../../modules/core/podman.nix
|
||||
../../modules/hardware/nvidia.nix
|
||||
../../modules/core/known-hosts.nix
|
||||
../../modules/services/immich.nix
|
||||
../../modules/services/jellyfin.nix
|
||||
../../modules/services/backrest.nix
|
||||
../../modules/services/homepage.nix
|
||||
./homepage.nix
|
||||
];
|
||||
|
||||
services.backrest = {
|
||||
enable = true;
|
||||
host = "0.0.0.0";
|
||||
port = 9898;
|
||||
dataDir = "/data/backrest";
|
||||
};
|
||||
|
||||
systemd.services.backrest = {
|
||||
after = [ "restic-ssh-key-format.service" ];
|
||||
wants = [ "restic-ssh-key-format.service" ];
|
||||
};
|
||||
|
||||
# Allow backrest to read backup source directories recursively.
|
||||
# Uses ACLs with a default mask so newly created files also inherit access.
|
||||
# Runs after immich and the postgresql backup so the dirs/files exist.
|
||||
systemd.services.backrest-permissions = {
|
||||
description = "Grant backrest read access to backup source dirs";
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
after = [
|
||||
"immich-server.service"
|
||||
"postgresqlBackup-immich.service"
|
||||
];
|
||||
serviceConfig = {
|
||||
Type = "oneshot";
|
||||
RemainAfterExit = true;
|
||||
};
|
||||
script = ''
|
||||
# Recursive read+execute ACL for backrest on immich media
|
||||
${pkgs.acl}/bin/setfacl -R -m u:backrest:rx -m m::r-x /data/immich
|
||||
# Default ACL so future immich files are readable by backrest
|
||||
${pkgs.acl}/bin/setfacl -R -m d:u:backrest:rx -m d:m::r-x /data/immich
|
||||
# Recursive read for backrest on postgresql backups
|
||||
${pkgs.acl}/bin/setfacl -R -m u:backrest:rx -m m::r-x /data/backup/postgresql
|
||||
${pkgs.acl}/bin/setfacl -R -m d:u:backrest:rx -m d:m::r-x /data/backup/postgresql
|
||||
'';
|
||||
};
|
||||
|
||||
services.immich-server = {
|
||||
enable = true;
|
||||
port = 2283;
|
||||
mediaLocation = "/data/immich";
|
||||
};
|
||||
|
||||
services.jellyfin-server = {
|
||||
enable = true;
|
||||
port = 8096;
|
||||
mediaLocation = "/data/jellyfin";
|
||||
};
|
||||
|
||||
services.postgresqlBackup = {
|
||||
enable = true;
|
||||
databases = [ "immich" ];
|
||||
location = "/data/backup/postgresql";
|
||||
compression = "zstd";
|
||||
startAt = "weekly";
|
||||
};
|
||||
|
||||
# The services.postgresqlBackup module creates a tmpfiles `d` rule that sets
|
||||
# the backup directory to 0700 on every rebuild/switch. On a directory with
|
||||
# POSIX ACLs, `chmod 0700` resets the ACL mask to `---`, which nullifies the
|
||||
# `user:backrest` read ACL that backrest uses to back up these dumps. Override
|
||||
# the mode so rebuilds keep the directory group-traversable (mask stays rx).
|
||||
# Note: systemd-tmpfiles dedupes conflicting rules and keeps the FIRST one for
|
||||
# a path, so our override must appear before the module's rule.
|
||||
systemd.tmpfiles.rules = lib.mkBefore [
|
||||
"d /data/backup/postgresql 0750 postgres - - -"
|
||||
];
|
||||
|
||||
sops.secrets = {
|
||||
"users/petere-password" = {
|
||||
neededForUsers = true;
|
||||
};
|
||||
"pocket-id-env" = {
|
||||
neededForUsers = false;
|
||||
};
|
||||
"homeserver-1/restic-passphrase" = { };
|
||||
"homeserver-1/restic-ssh-key" = { };
|
||||
"homeserver-1/homepage-env" = { };
|
||||
"homeserver-1/samba-petere-password" = { };
|
||||
};
|
||||
|
||||
# Set petere's Samba password from SOPS at boot. Samba keeps its own password
|
||||
# database (smbpasswd/tdbsam), separate from Linux login, so this must run
|
||||
# smbpasswd. Idempotent: re-applied on every boot from the secret.
|
||||
systemd.services.samba-set-petere-password = {
|
||||
description = "Set petere's Samba password from SOPS";
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
after = [ "sops-nix.service" ];
|
||||
before = [ "samba-smbd.service" ];
|
||||
serviceConfig.Type = "oneshot";
|
||||
script = ''
|
||||
PASS="$(cat ${config.sops.secrets."homeserver-1/samba-petere-password".path})"
|
||||
${pkgs.samba}/bin/smbpasswd -s -a petere <<EOF
|
||||
$PASS
|
||||
$PASS
|
||||
EOF
|
||||
'';
|
||||
};
|
||||
|
||||
# Install the restic SSH key (stored multi-line in sops) and set up SSH access
|
||||
# for the backrest user so restic can reach mcf-server.
|
||||
# Writes to a persistent location (NOT /run) because sops-nix clears /run/secrets.
|
||||
systemd.services.restic-ssh-key-format = {
|
||||
description = "Install restic SSH key and configure SSH for backrest";
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
after = [ "sops-nix.service" ];
|
||||
serviceConfig = {
|
||||
Type = "oneshot";
|
||||
RemainAfterExit = true;
|
||||
};
|
||||
script = ''
|
||||
KEY_FILE="${config.sops.secrets."homeserver-1/restic-ssh-key".path}"
|
||||
FORMATTED="/data/backrest/restic-ssh-key"
|
||||
${pkgs.coreutils}/bin/install -m 640 -o root -g backrest "$KEY_FILE" "$FORMATTED"
|
||||
|
||||
# SSH config for backrest so restic (via Backrest) uses the correct key
|
||||
mkdir -p /data/backrest/.ssh
|
||||
cat > /data/backrest/.ssh/config <<EOF
|
||||
Host mcf-server
|
||||
HostName mcf-server
|
||||
User restic-homeserver1
|
||||
IdentityFile /data/backrest/restic-ssh-key
|
||||
IdentitiesOnly yes
|
||||
Host richmond-server
|
||||
HostName richmond-server
|
||||
User restic-homeserver1
|
||||
IdentityFile /data/backrest/restic-ssh-key
|
||||
IdentitiesOnly yes
|
||||
EOF
|
||||
chown -R backrest:backrest /data/backrest/.ssh
|
||||
chmod 700 /data/backrest/.ssh
|
||||
chmod 600 /data/backrest/.ssh/config
|
||||
'';
|
||||
};
|
||||
|
||||
networking.hostName = "homeserver-1";
|
||||
|
||||
# SSH configuration
|
||||
services.openssh.enable = true;
|
||||
|
||||
# Trusted host keys for restic backup targets (via Backrest).
|
||||
my.knownHosts = {
|
||||
mcfServer = true;
|
||||
richmondServer = true;
|
||||
};
|
||||
|
||||
# Glances system monitor - exposed to the tailnet so the Homepage
|
||||
# dashboard can display real-time stats for this machine (localhost).
|
||||
services.glances = {
|
||||
enable = true;
|
||||
port = 61208;
|
||||
extraArgs = [ "--webserver" ];
|
||||
};
|
||||
|
||||
# Expose services on Tailscale only (not the LAN).
|
||||
# Immich (2283): accessed via nginx proxy on another machine over Tailscale.
|
||||
# Backrest (9898), Pocket ID (8443), Homepage (8082), Glances (61208): admin services.
|
||||
networking.firewall.interfaces.tailscale.allowedTCPPorts = lib.mkAfter [
|
||||
2283 # Immich
|
||||
8443 # Pocket ID
|
||||
9898 # Backrest
|
||||
8082 # Homepage
|
||||
61208 # Glances
|
||||
];
|
||||
|
||||
# Add Pocket ID package for tooling
|
||||
environment.systemPackages = with pkgs; [ pocket-id ];
|
||||
|
||||
# Pocket ID service configuration
|
||||
services.pocket-id = {
|
||||
enable = true;
|
||||
environmentFile = config.sops.secrets."pocket-id-env".path;
|
||||
settings = {
|
||||
APP_URL = "https://homeserver-1.gerbil-opah.ts.net:8443";
|
||||
PORT = 8443;
|
||||
TRUST_PROXY = true;
|
||||
TLS_CERT_FILE = "/etc/ssl/certs/pocket-id.crt";
|
||||
TLS_KEY_FILE = "/etc/ssl/private/pocket-id.key";
|
||||
};
|
||||
};
|
||||
|
||||
systemd.services.pocket-id = {
|
||||
wants = [ "pocket-id-tailscale-cert.service" ];
|
||||
after = [ "pocket-id-tailscale-cert.service" ];
|
||||
};
|
||||
|
||||
# Systemd service to obtain TLS cert via Tailscale
|
||||
systemd.services.pocket-id-tailscale-cert = {
|
||||
description = "Obtain TLS cert for Pocket-ID via Tailscale";
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
wants = [
|
||||
"network-online.target"
|
||||
"tailscaled.service"
|
||||
];
|
||||
after = [
|
||||
"network-online.target"
|
||||
"tailscaled.service"
|
||||
];
|
||||
serviceConfig = {
|
||||
Type = "oneshot";
|
||||
ExecStart = pkgs.writeShellScript "get-tailscale-cert" ''
|
||||
set -eu
|
||||
mkdir -p /etc/ssl/certs /etc/ssl/private
|
||||
if [ ! -f /etc/ssl/certs/pocket-id.crt ] || [ ! -f /etc/ssl/private/pocket-id.key ]; then
|
||||
${pkgs.tailscale}/bin/tailscale cert --cert-file /etc/ssl/certs/pocket-id.crt --key-file /etc/ssl/private/pocket-id.key homeserver-1.gerbil-opah.ts.net
|
||||
fi
|
||||
chown root:pocket-id /etc/ssl/private/pocket-id.key
|
||||
chmod 640 /etc/ssl/private/pocket-id.key
|
||||
chmod 644 /etc/ssl/certs/pocket-id.crt
|
||||
'';
|
||||
User = "root";
|
||||
Group = "root";
|
||||
};
|
||||
};
|
||||
|
||||
# NVIDIA GPU Configuration for GeForce GTX 960 (Maxwell GM206)
|
||||
my.hardware.nvidia = {
|
||||
enable = true;
|
||||
# GTX 960 (Maxwell) needs the 580.xx legacy driver branch; the default
|
||||
# driver no longer supports it (NVRM: No NVIDIA GPU found).
|
||||
package = config.boot.kernelPackages.nvidiaPackages.legacy_580;
|
||||
};
|
||||
|
||||
boot.kernelModules = [ "sg" ];
|
||||
|
||||
# Root account is locked (no password login); access is via SSH key + sudo.
|
||||
users.users.root.hashedPassword = "!";
|
||||
|
||||
# Standard user account (shared definition in modules/core/users.nix)
|
||||
my.users.petere = {
|
||||
hashedPasswordFile = config.sops.secrets."users/petere-password".path;
|
||||
};
|
||||
|
||||
# Trusted users for Nix operations
|
||||
nix.settings.trusted-users = [
|
||||
"root"
|
||||
"petere"
|
||||
];
|
||||
}
|
||||
Reference in New Issue
Block a user