Overhaul services config on laptop server & fully setup jellyfin + caddy

This commit is contained in:
Tyler Beckman 2025-01-03 21:38:24 -07:00
parent ef017dc3ed
commit 84e2cc3b80
Signed by: Ty
GPG key ID: 2813440C772555A4
9 changed files with 133 additions and 22 deletions

View file

@ -1,7 +1,13 @@
{ ... }: { ... }:
{ {
boot.loader = { boot = {
systemd-boot.enable = true; loader = {
efi.canTouchEfiVariables = true; systemd-boot = {
enable = true;
};
efi.canTouchEfiVariables = true;
};
initrd.systemd.enable = true;
}; };
} }

View file

@ -3,13 +3,16 @@
fileSystems."/mnt/hdd" = { fileSystems."/mnt/hdd" = {
device = "/dev/mapper/hdd"; device = "/dev/mapper/hdd";
fsType = "btrfs"; fsType = "btrfs";
options = [ "compression=zstd:3" "autodefrag" "nofail" ]; options = [ "compress=zstd:3" "autodefrag" "nofail" ];
encrypted = { encrypted = {
enable = true; enable = true;
label = "hdd"; label = "hdd";
blkDev = "/dev/disk/by-uuid/eab5e1d6-6956-46fd-b3ac-5fcf525e1df8"; blkDev = "/dev/disk/by-uuid/eab5e1d6-6956-46fd-b3ac-5fcf525e1df8";
keyFile = "/mnt-root/root/hdd.key"; keyFile = "/sysroot/root/hdd.key";
}; };
}; };
}
# Loading the uas kernel module early is needed to mount the above drive via USB-SATA adapter
boot.initrd.kernelModules = [ "uas" ];
}

View file

@ -0,0 +1,36 @@
# Global options
{
acme_dns porkbun {
api_key {env.PORKBUN_API_KEY}
api_secret_key {env.PORKBUN_API_SECRET_KEY}
}
email ty@myriation.xyz
storage file_system {
root /var/lib/caddy
}
}
# Handle all routes handled by this server
https://*.m.myriation.xyz {
# Enforce always using HTTPS
header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
# Give an HTTP cat on any errors
handle_errors {
header Content-Type text/html
respond "<!DOCTYPE html><html><head><title>Error :(</title></head><body><img src=\"https://http.cat/{http.error.status_code}\" alt=\"cat\"></body></html>" {http.error.status_code}
}
# Configure all service matchers
@jellyfin host jellyfin.m.myriation.xyz
# Handle all services
handle @jellyfin {
reverse_proxy jellyfin.containers:8096
}
# Handle all unmatched requests as a 404
handle {
error "Not Found" 404
}
}

View file

@ -0,0 +1,26 @@
{ pkgs, secrets, ... }:
{
# TODO: Containerize once I setup a proper shared bridge network
services.caddy = {
enable = true;
package = pkgs.caddy.withPlugins {
plugins = [ "github.com/caddy-dns/porkbun@v0.2.1" ];
hash = "sha256-oizWuPXI0M9ngBCt/iEXWt+/33wpKlCs1yBPKnzFhRY=";
};
# Use a custom config because doing Caddyfile in multiline nix strings
# feels messy (and not syntax highlighted)
configFile = ./Caddyfile;
};
# Pass secrets through the systemd service's environment variables
systemd.services.caddy.environment = {
PORKBUN_API_KEY = secrets.programs.caddy.porkbun_api_key;
PORKBUN_API_SECRET_KEY = secrets.programs.caddy.porkbun_secret_key;
};
# Allow caddy through the firewall
networking.firewall = {
allowedTCPPorts = [ 80 443 ]; # HTTP/1-2
allowedUDPPorts = [ 443 ]; # HTTP/3 w/ QUIC
};
}

View file

@ -1,13 +1,9 @@
{ ... }: { ... }:
{ {
imports = [ imports = [
./jellyfin.nix ./networking.nix
# Individual services
./caddy
./jellyfin
]; ];
}
networking = {
nat = {
enable = true;
externalInterface = "wlp2s0";
};
};
}

View file

@ -1,7 +1,7 @@
{ ... }: { ... }:
{ {
containers.jellyfin = { containers.jellyfin = {
config = { pkgs, ...}: { config = { pkgs, ... }: {
system.stateVersion = "25.05"; system.stateVersion = "25.05";
services.jellyfin = { services.jellyfin = {
@ -13,9 +13,13 @@
networking.firewall.enable = false; networking.firewall.enable = false;
}; };
autoStart = true; autoStart = true;
privateNetwork = true;
hostAddress = "172.30.1.2"; # TODO define in config option bindMounts = {
localAddress = "172.30.0.2"; "/var/lib/jellyfin/libraries" = {
hostPath = "/mnt/hdd/jellyfin";
isReadOnly = false;
};
};
}; };
networking = { networking = {
@ -23,4 +27,4 @@
nat.internalInterfaces = [ "ve-jellyfin" ]; nat.internalInterfaces = [ "ve-jellyfin" ];
networkmanager.unmanaged = [ "interface-name:ve-jellyfin" ]; networkmanager.unmanaged = [ "interface-name:ve-jellyfin" ];
}; };
} }

View file

@ -0,0 +1,34 @@
{ lib, ... }:
let
services = {
jellyfin = {
hostByte = 2;
ports = [];
};
};
in {
config = lib.mkMerge ([{
# Config always added
networking = {
nat = {
enable = true;
externalInterface = "wlp2s0";
};
};
}] ++ builtins.map (serviceName: {
# Config added per-service
containers.${serviceName} = {
privateNetwork = true;
# Give it an address of 172.30.0.X on the host-side and 172.30.1.X inside the container
# This appears to be necessary as both having addresses the same seems to cause issues
hostAddress = "172.30.0.${builtins.toString services.${serviceName}.hostByte}";
localAddress = "172.30.1.${builtins.toString services.${serviceName}.hostByte}";
};
networking = {
firewall.trustedInterfaces = [ "ve-${serviceName}" ];
nat.internalInterfaces = [ "ve-${serviceName}" ];
networkmanager.unmanaged = [ "interface-name:ve-${serviceName}" ];
};
}) (builtins.attrNames services));
}

View file

@ -38,4 +38,4 @@
nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux"; nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware; hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware;
} }

View file

@ -19,6 +19,12 @@
token = null; token = null;
}; };
}; };
caddy = {
# The API key and secret key to use for provisioning HTTPS certificates
porkbun_api_key = null;
porkbun_secret_key = null;
};
}; };
passwords = { passwords = {