This commit is contained in:
Lilith 2025-12-04 13:28:25 +01:00
parent a18e244ba5
commit 05a9d96d4e
No known key found for this signature in database
GPG key ID: 272C807BD91F8446
26 changed files with 861 additions and 182 deletions

65
server/arr/default.nix Normal file
View file

@ -0,0 +1,65 @@
{ lib, config, ... }:
{
options.server.arr.enable = lib.mkEnableOption "Enable *arr suite";
config = lib.mkIf config.server.arr.enable {
networking.firewall.allowedTCPPorts = [
7878
8191
8686
8989
9696
];
services.radarr = {
user = "jellyfin";
group = "jellyfin";
enable = true;
};
services.sonarr = {
user = "jellyfin";
group = "jellyfin";
enable = true;
};
# services.lidarr = {
# user = "jellyfin";
# group = "jellyfin";
# enable = true;
# };
virtualisation.oci-containers.containers."lidarr" = {
image = "ghcr.io/linuxserver-labs/prarr:lidarr-plugins";
volumes = [
"config:/config"
"/data:/data"
"music:/music"
];
environment = {
"PUID" = "994";
"GUID" = "994";
};
extraOptions = [ "--network=host" ];
};
services.prowlarr = {
enable = true;
};
# services.flaresolverr = {
# enable = true;
# };
services.readarr = {
user = "jellyfin";
group = "jellyfin";
enable = true;
};
virtualisation.oci-containers.containers."flaresolverr" = {
image = "ghcr.io/flaresolverr/flaresolverr:latest";
extraOptions = [ "--network=host" ];
};
};
}

12
server/default.nix Normal file
View file

@ -0,0 +1,12 @@
{ ... }:
{
imports = [
./ollama.nix
./jellyfin
./wireguard.nix
./deluge
./nfs-server.nix
./arr
# ./home-assistant
];
}

154
server/deluge/default.nix Normal file
View file

@ -0,0 +1,154 @@
{
lib,
config,
pkgs,
...
}:
{
options.server.deluge.enable = lib.mkEnableOption "Enable deluge torrent client";
config = lib.mkIf config.server.deluge.enable {
services.deluge = {
enable = true;
web.enable = true;
};
environment.systemPackages = with pkgs; [ libnatpmp ];
networking.firewall.allowedTCPPorts = [ 8112 ];
# creating network namespace
systemd.services."netns@" = {
description = "%I network namespace";
before = [ "network.target" ];
serviceConfig = {
Type = "oneshot";
RemainAfterExit = true;
ExecStart = "${pkgs.iproute2}/bin/ip netns add %I";
ExecStop = "${pkgs.iproute2}/bin/ip netns del %I";
};
};
systemd.services.wg-port-opener = {
description = "port opener for protonvpn";
bindsTo = [ "netns@wg.service" ];
requires = [ "network-online.target" ];
after = [ "netns@wg.service" ];
serviceConfig = {
Type = "exec";
NetworkNamespacePath = [ "/var/run/netns/wg" ];
ExecStart =
with pkgs;
writers.writeBash "port-opening" ''
while true; do
date;
${libnatpmp}/bin/natpmpc -a 1 0 udp 60 -g 10.2.0.1 || { echo -e "ERROR with natpmpc udp command \a" ; break ; };
${libnatpmp}/bin/natpmpc -a 1 0 tcp 60 -g 10.2.0.1 || { echo -e "ERROR with natpmpc tcp command \a" ; break ; };
sleep 45;
done;
'';
};
};
# setting up wireguard interface within network namespace
systemd.services.wg = {
description = "wg network interface";
bindsTo = [ "netns@wg.service" ];
requires = [ "network-online.target" ];
after = [ "netns@wg.service" ];
serviceConfig = {
Type = "oneshot";
RemainAfterExit = true;
ExecStart =
with pkgs;
writers.writeBash "wg-up" ''
${iproute2}/bin/ip link add wg0 type wireguard
${iproute2}/bin/ip link set wg0 netns wg
${iproute2}/bin/ip -n wg address add 10.2.0.2/32 dev wg0
# ${iproute2}/bin/ip -n wg -6 address add fc00:bbbb:bbbb:bb01::a:1674/128 dev wg0
${iproute2}/bin/ip netns exec wg \
${wireguard-tools}/bin/wg setconf wg0 /root/proton.conf
${iproute2}/bin/ip -n wg link set wg0 up
# need to set lo up as network namespace is started with lo down
${iproute2}/bin/ip -n wg link set lo up
${iproute2}/bin/ip -n wg route add default dev wg0
${iproute2}/bin/ip -n wg -6 route add default dev wg0
'';
ExecStop =
with pkgs;
writers.writeBash "wg-down" ''
${iproute2}/bin/ip -n wg route del default dev wg0
${iproute2}/bin/ip -n wg -6 route del default dev wg0
${iproute2}/bin/ip -n wg link del wg0
'';
};
};
# binding deluged to network namespace
systemd.services.deluged.bindsTo = [ "netns@wg.service" ];
systemd.services.deluged.requires = [
"network-online.target"
"wg.service"
];
systemd.services.deluged.serviceConfig.NetworkNamespacePath = [ "/var/run/netns/wg" ];
systemd.services.deluge-port-setter = {
description = "sets deluge ports";
bindsTo = [ "netns@wg.service" ];
requires = [ "network-online.target" ];
after = [ "deluged.service" ];
serviceConfig = {
Type = "oneshot";
User = "deluge";
RemainAfterExit = true;
NetworkNamespacePath = [ "/var/run/netns/wg" ];
ExecStart =
with pkgs;
writers.writeBash "deluge-ports" ''
TCP=($(${libnatpmp}/bin/natpmpc -a 1 0 tcp 60 -g 10.2.0.1 2> /dev/null | grep Mapped))
TCP_PORT=''${TCP[3]}
UDP=($(${libnatpmp}/bin/natpmpc -a 1 0 udp 60 -g 10.2.0.1 2> /dev/null | grep Mapped))
UDP_PORT=''${UDP[3]}
echo "The ports are"
echo "TCP: $TCP_PORT"
echo "UDP: $UDP_PORT"
PORTS="($TCP_PORT, $UDP_PORT)"
${deluge}/bin/deluge-console "config -s random_port false; config -s listen_ports $PORTS"
exit 0
'';
};
};
# allowing delugeweb to access deluged in network namespace, a socket is necesarry
systemd.sockets."proxy-to-deluged" = {
enable = true;
description = "Socket for Proxy to Deluge Daemon";
listenStreams = [ "58846" ];
wantedBy = [ "sockets.target" ];
};
# creating proxy service on socket, which forwards the same port from the root namespace to the isolated namespace
systemd.services."proxy-to-deluged" = {
enable = true;
description = "Proxy to Deluge Daemon in Network Namespace";
requires = [
"deluged.service"
"proxy-to-deluged.socket"
];
after = [
"deluged.service"
"proxy-to-deluged.socket"
];
unitConfig = {
JoinsNamespaceOf = "deluged.service";
};
serviceConfig = {
User = "deluge";
Group = "deluge";
ExecStart = "${pkgs.systemd}/lib/systemd/systemd-socket-proxyd --exit-idle-time=5min 127.0.0.1:58846";
PrivateNetwork = "yes";
};
};
};
}

View file

@ -0,0 +1,30 @@
{ ... }:
{
services.home-assistant.config = {
"automation" = [
{
alias = "Towel Warmer Timer";
trigger = {
type = "turned_on";
device_id = "bf77f1611d1d9959e967b4e35ba5234c";
entity_id = "b674ce8cc70a4d0c1bfba7c6946ab3e4";
domain = "switch";
trigger = "device";
for = {
hours = 0;
minutes = 60;
seconds = 0;
};
};
action = [
{
type = "turn_off";
device_id = "bf77f1611d1d9959e967b4e35ba5234c";
entity_id = "b674ce8cc70a4d0c1bfba7c6946ab3e4";
domain = "switch";
}
];
}
];
};
}

View file

@ -0,0 +1,66 @@
{ lib, config, ... }:
{
options.server.home-assistant.enable = lib.mkEnableOption "Enable home-assistant";
config = lib.mkIf config.server.home-assistant.enable {
networking.firewall.allowedTCPPorts = [
8123
5683
];
imports = [
./zones.nix
./wyoming.nix
./mosquitto.nix
./bathroom.nix
./lights.nix
./heating.nix
];
services.home-assistant = {
enable = true;
extraComponents = [
"pushover"
"isal"
"nina"
"jellyfin"
"deluge"
"conversation"
"ollama"
"anthropic"
"mqtt"
"shelly"
"tasmota"
"wyoming"
"whisper"
"piper"
"open_meteo"
"wake_on_lan"
"bluetooth"
"bthome"
"fritz"
];
config = {
default_config = { };
# anthropic = {
# intents = [
# "HassTurnOn"
# "HassTurnOff"
# "HassGetWeather"
# ];
# };
intent = { };
};
};
};
}

View file

@ -0,0 +1,16 @@
{...}: {
services.home-assistant.config.automation = [
{
description = "Pause Heating when Window opens";
alias = "Heating Pause";
use_blueprint = {
path = "raffy-ops/hvac_pause.yaml";
input = {
climate_device = "climate.shellyblutrv_286847ef7fc0";
doors_windows = "binary_sensor.shelly_blu_door_window_3a5a_window";
action_first = true;
};
};
}
];
}

View file

@ -0,0 +1,55 @@
{...}: {
services.home-assistant.config.automation = [
{
alias = "Wake Up Light";
description = "Turn on the light on alarm ringing";
trigger = {
platform = "time";
at = "sensor.lilith_phone_next_alarm";
};
condition = [];
action = [
{
action = "light.turn_on";
metadata = {};
data = {
transition = 3;
kelvin = 3000;
brightness_pct = 100;
};
target = {
device_id = "7d40ebcac890a2743d7dc2a6dc4ca797";
};
}
];
mode = "single";
}
{
alias = "Automatic Light Off";
description = "Turn off the light after 15 minutes once Liv leaves the house";
trigger = {
platform = "state";
entity_id = "person.liv_benstem";
from = "home";
to = "not_home";
for = {
hours = 0;
minutes = 10;
seconds = 0;
};
};
condition = [];
action = [
{
action = "light.turn_off";
metadata = {};
data = {};
target = {
area_id = "bedroom";
};
}
];
mode = "single";
}
];
}

View file

@ -0,0 +1,20 @@
{ sops, config, ...}: {
sops.secrets."home-assistant/mosquitto/hass" = {};
services.mosquitto = {
enable = true;
listeners = [
{
address = "192.168.178.111";
port = 1883;
users.hass = {
acl = [ "readwrite #" ];
hashedPasswordFile = config.sops.secrets."home-assistant/mosquitto/hass".path;
};
}
];
};
networking.firewall.allowedTCPPorts = [ 1883 ];
}

View file

@ -0,0 +1,19 @@
{...}: {
services.wyoming = {
piper.servers = {
alba = {
enable=true;
uri="tcp://localhost:12001";
voice="en_GB-alba-medium";
};
};
faster-whisper.servers = {
tiny = {
enable=true;
uri="tcp://localhost:12002";
model="tiny-int8";
language="en";
};
};
};
}

View file

@ -0,0 +1,27 @@
{
services.home-assistant.config.homeassistant = {
name = "Home";
latitude = "52.405212";
country = "DE";
longitude = "13.047246";
elevation = "45";
unit_system = "metric";
time_zone = "Europe/Berlin";
};
services.home-assistant.config.zone = [
{
name = "University";
icon = "mdi:school";
latitude = "52.408866";
longitude = "12.97513";
radius = "500";
}
{
name = "Parents";
icon = "mdi:human-male-female-child";
latitude = "52.153352";
longitude = "9.919335";
radius = "100";
}
];
}

View file

@ -0,0 +1,51 @@
{
lib,
config,
pkgs,
...
}:
{
options.server.jellyfin.enable = lib.mkEnableOption "Enable Jellyfin+Jellyseerr";
config = lib.mkIf config.server.jellyfin.enable {
networking.firewall.allowedTCPPorts = [
5055
8096
];
networking.firewall.allowedUDPPorts = [ 8096 ];
# Jellyfin
environment.systemPackages = with pkgs; [
jellyfin
jellyfin-web
jellyfin-ffmpeg
];
services.jellyfin.enable = true;
hardware.graphics = {
enable = true;
extraPackages = with pkgs; [
intel-media-driver
intel-vaapi-driver # previously vaapiIntel
vaapiVdpau
intel-compute-runtime # OpenCL filter support (hardware tonemapping and subtitle burn-in)
vpl-gpu-rt # QSV on 11th gen or newer
intel-media-sdk # QSV up to 11th gen
];
};
# services.jellyseerr = {
# enable = true;
# port = 5055;
# openFirewall = true;
# };
virtualisation.oci-containers.containers."jellyseerr-music" = {
image = "fallenbagel/jellyseerr:preview-music-support";
extraOptions = [ "--network=host" ];
volumes = [ "/var/lib/jellyseerr/config:/app/config" ];
};
};
}

33
server/nfs-server.nix Normal file
View file

@ -0,0 +1,33 @@
{ lib, config, ... }:
{
options.server.nfs.enable = lib.mkEnableOption "Enable NFS shares";
config = lib.mkIf config.server.nfs.enable {
services.nfs.server = {
enable = true;
exports = ''
/export 192.168.178.111/24(rw,fsid=0,no_subtree_check)
/export/share 192.168.178.111/24(rw,nohide,insecure,no_subtree_check)
/export/torrent 192.168.178.111/24(rw,nohide,insecure,no_subtree_check)
'';
};
services.nfs.settings.main = {
UDP = false;
vers2 = false;
vers3 = false;
};
fileSystems."/export/share" = {
device = "/data/share";
options = [ "bind" ];
};
fileSystems."/export/torrent" = {
device = "/data/torrent";
options = [ "bind" ];
};
networking.firewall.allowedTCPPorts = [ 2049 ];
};
}

29
server/ollama.nix Normal file
View file

@ -0,0 +1,29 @@
{ config, lib, ... }:
{
options.server.ollama.enable = lib.mkEnableOption "Enable Ollama server /w GPU acceleration";
options.server.ollama.options = lib.mkOption {
description = "additional options to pass to ollama";
default = { };
};
config = lib.mkIf config.server.ollama.enable {
services.ollama = lib.attrsets.recursiveUpdate {
enable = true;
host = "0.0.0.0";
# acceleration = "rocm";
# rocmOverrideGfx = "11.0.0";
} config.server.ollama.options;
networking.firewall.allowedTCPPorts = [ 11434 ];
environment.persistence."/persist/cache".directories = [
{
directory = "/var/lib/private/ollama";
user = "nouser";
group = "nogroup";
mode = "u=rwx,g=,o=";
}
];
};
}

1
server/wireguard.nix Normal file
View file

@ -0,0 +1 @@
{ ... }: { }