├── pkgs ├── grafana-dashboards │ ├── fritzbox.json │ └── default.nix └── default.nix ├── .gitignore ├── .envrc ├── machines ├── core │ ├── default.nix │ ├── modules.nix │ ├── network.nix │ ├── core.nix │ ├── nix.nix │ └── users.nix ├── newton │ ├── system.nix │ ├── boot.nix │ ├── nixinate.nix │ ├── hardware-configuration.nix │ ├── configuration.nix │ ├── network.nix │ ├── disko-config.nix │ ├── syncthing.nix │ ├── services.nix │ └── secrets.yaml ├── serverle │ ├── system.nix │ ├── network.nix │ ├── nixinate.nix │ ├── hardware-configuration.nix │ ├── configuration.nix │ ├── wifi.nix │ ├── disko-config.nix │ ├── services.nix │ ├── syncthing.nix │ └── secrets.yaml ├── thinkman │ ├── system.nix │ ├── boot.nix │ ├── services.nix │ ├── remote-build.nix │ ├── network.nix │ ├── profiles.nix │ ├── configuration.nix │ ├── hardware-configuration.nix │ ├── disko-config.nix │ └── secrets.yaml ├── workman │ ├── system.nix │ ├── boot.nix │ ├── services.nix │ ├── remote-build.nix │ ├── configuration.nix │ ├── network.nix │ ├── profiles.nix │ ├── hardware-configuration.nix │ ├── disko-config.nix │ └── secrets.yaml ├── .sops.yaml └── configurations.nix ├── modules ├── default.nix ├── system │ ├── default.nix │ ├── spell-check │ │ └── default.nix │ ├── avahi │ │ └── default.nix │ ├── miracast │ │ └── default.nix │ ├── podman │ │ └── default.nix │ ├── kvm │ │ └── default.nix │ ├── fonts │ │ └── default.nix │ └── docker │ │ └── default.nix ├── hardware │ ├── default.nix │ ├── id-card │ │ └── default.nix │ ├── thunderbolt │ │ └── default.nix │ ├── monitor │ │ └── default.nix │ ├── yubikey │ │ └── default.nix │ ├── debug │ │ └── default.nix │ ├── keychron │ │ └── default.nix │ ├── bluetooth │ │ └── default.nix │ ├── sound │ │ └── default.nix │ ├── firmware │ │ └── default.nix │ ├── action-on-low-power │ │ └── default.nix │ ├── graphics │ │ └── default.nix │ └── drive-monitor │ │ └── default.nix └── services │ ├── alertmanager │ ├── config.nix │ └── default.nix │ ├── jellyfin │ ├── enable-metrics.patch │ └── default.nix │ ├── remote-build │ └── default.nix │ ├── minecraft-server │ └── default.nix │ ├── homepage │ └── default.nix │ ├── initrd-ssh │ └── default.nix │ ├── jellyseerr │ └── default.nix │ ├── tandoor-recipes │ └── default.nix │ ├── default.nix │ ├── rss-bridge │ └── default.nix │ ├── dyndns │ └── default.nix │ ├── passworts │ └── default.nix │ ├── acme │ └── default.nix │ ├── aria2 │ └── default.nix │ ├── octoprint │ └── default.nix │ ├── homer │ ├── default.nix │ └── config.nix │ ├── game-stream │ └── default.nix │ ├── ssh-server │ └── default.nix │ ├── mumble-server │ └── default.nix │ ├── finance │ └── default.nix │ ├── paperless │ └── default.nix │ ├── bazarr │ └── default.nix │ ├── prowlarr │ └── default.nix │ ├── radarr │ └── default.nix │ ├── fritzbox │ └── default.nix │ ├── freshrss │ └── default.nix │ ├── matrix-bot │ └── default.nix │ ├── sonarr │ └── default.nix │ ├── blocky │ └── default.nix │ ├── hedgedoc │ └── default.nix │ ├── grafana │ └── default.nix │ ├── vpn │ └── default.nix │ ├── photos │ └── default.nix │ ├── promtail │ └── default.nix │ ├── navidrome │ └── default.nix │ ├── git │ └── default.nix │ ├── home-automation │ └── default.nix │ ├── blackbox │ └── default.nix │ ├── nextcloud │ └── default.nix │ └── loki │ └── default.nix ├── .editorconfig ├── overlays └── default.nix ├── images ├── rpi4-image.nix ├── flake-module.nix └── base-config.nix ├── profiles ├── android │ └── default.nix ├── webcam │ └── default.nix ├── 3d-design │ └── default.nix ├── update │ └── default.nix ├── meeting │ └── default.nix ├── sway │ ├── autostart.nix │ ├── theme.nix │ ├── location.nix │ ├── screen-sharing.nix │ └── default.nix ├── sync │ └── default.nix ├── latex │ └── default.nix ├── default.nix ├── filesystem │ └── default.nix ├── clean │ └── default.nix ├── nix │ └── default.nix ├── usb-iso │ └── default.nix ├── printing │ └── default.nix ├── powersave │ └── default.nix ├── gnome │ └── default.nix ├── development │ └── default.nix ├── gaming │ └── default.nix ├── nautilus │ └── default.nix ├── media │ └── default.nix ├── desktop-apps │ └── default.nix └── desktop-dev │ └── default.nix ├── .github └── workflows │ └── nix.yml ├── flake.nix └── README.md /pkgs/grafana-dashboards/fritzbox.json: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | result* 2 | .direnv 3 | .pre-commit-config.yaml 4 | *.qcow2 5 | -------------------------------------------------------------------------------- /.envrc: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | export DIRENV_WARN_TIMEOUT=5m 3 | 4 | use flake 5 | -------------------------------------------------------------------------------- /pkgs/default.nix: -------------------------------------------------------------------------------- 1 | final: _prev: { 2 | grafana-dashboards = final.callPackage ./grafana-dashboards { }; 3 | } 4 | -------------------------------------------------------------------------------- /machines/core/default.nix: -------------------------------------------------------------------------------- 1 | [ 2 | ./core.nix 3 | ./modules.nix 4 | ./network.nix 5 | ./nix.nix 6 | ./users.nix 7 | ] 8 | -------------------------------------------------------------------------------- /modules/default.nix: -------------------------------------------------------------------------------- 1 | { ... }: 2 | { 3 | imports = [ 4 | ./hardware 5 | ./services 6 | ./system 7 | ]; 8 | } 9 | -------------------------------------------------------------------------------- /machines/newton/system.nix: -------------------------------------------------------------------------------- 1 | # enabled system services 2 | _: { 3 | my.system = { 4 | podman.enable = true; 5 | }; 6 | } 7 | -------------------------------------------------------------------------------- /machines/serverle/system.nix: -------------------------------------------------------------------------------- 1 | # enabled system services 2 | _: { 3 | my.system = { 4 | avahi.enable = true; 5 | docker.enable = true; 6 | }; 7 | } 8 | -------------------------------------------------------------------------------- /machines/newton/boot.nix: -------------------------------------------------------------------------------- 1 | _: { 2 | boot.loader = { 3 | timeout = 1; 4 | grub = { 5 | enable = true; 6 | device = "/dev/sda"; 7 | }; 8 | }; 9 | } 10 | -------------------------------------------------------------------------------- /machines/newton/nixinate.nix: -------------------------------------------------------------------------------- 1 | _: { 2 | _module.args.nixinate = { 3 | host = "buehler.rocks"; 4 | sshUser = "felix"; 5 | buildOn = "remote"; 6 | substituteOnTarget = true; 7 | }; 8 | } 9 | -------------------------------------------------------------------------------- /modules/system/default.nix: -------------------------------------------------------------------------------- 1 | { ... }: 2 | { 3 | imports = [ 4 | ./avahi 5 | ./docker 6 | ./fonts 7 | ./kvm 8 | ./miracast 9 | ./podman 10 | ./spell-check 11 | ]; 12 | } 13 | -------------------------------------------------------------------------------- /machines/serverle/network.nix: -------------------------------------------------------------------------------- 1 | _: { 2 | networking.firewall.allowedTCPPorts = [ 3 | 8080 # aria 4 | ]; 5 | 6 | networking = { 7 | domain = "stunkymonkey.de"; 8 | search = [ "stunkymonkey.de" ]; 9 | }; 10 | } 11 | -------------------------------------------------------------------------------- /machines/serverle/nixinate.nix: -------------------------------------------------------------------------------- 1 | _: { 2 | _module.args.nixinate = { 3 | host = "serverle.local"; 4 | sshUser = "felix"; 5 | buildOn = "remote"; 6 | substituteOnTarget = true; 7 | hermetic = false; 8 | }; 9 | } 10 | -------------------------------------------------------------------------------- /machines/thinkman/system.nix: -------------------------------------------------------------------------------- 1 | # enabled system services 2 | _: { 3 | my.system = { 4 | avahi.enable = true; 5 | fonts.enable = true; 6 | kvm = { 7 | enable = true; 8 | cpuFlavor = "intel"; 9 | }; 10 | podman.enable = true; 11 | spell-check.enable = true; 12 | }; 13 | } 14 | -------------------------------------------------------------------------------- /machines/workman/system.nix: -------------------------------------------------------------------------------- 1 | # enabled system services 2 | _: { 3 | my.system = { 4 | avahi.enable = true; 5 | fonts.enable = true; 6 | kvm = { 7 | enable = true; 8 | cpuFlavor = "amd"; 9 | }; 10 | podman.enable = true; 11 | spell-check.enable = true; 12 | }; 13 | } 14 | -------------------------------------------------------------------------------- /machines/thinkman/boot.nix: -------------------------------------------------------------------------------- 1 | _: { 2 | boot = { 3 | loader = { 4 | timeout = 1; 5 | systemd-boot = { 6 | enable = true; 7 | configurationLimit = 10; 8 | consoleMode = "keep"; 9 | editor = true; 10 | }; 11 | efi.canTouchEfiVariables = true; 12 | }; 13 | }; 14 | } 15 | -------------------------------------------------------------------------------- /machines/serverle/hardware-configuration.nix: -------------------------------------------------------------------------------- 1 | { pkgs, ... }: 2 | { 3 | hardware = { 4 | raspberry-pi."4".leds = { 5 | eth.disable = true; 6 | act.disable = true; 7 | pwr.disable = true; 8 | }; 9 | }; 10 | 11 | environment.systemPackages = with pkgs; [ 12 | libraspberrypi 13 | raspberrypi-eeprom 14 | ]; 15 | } 16 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | # Unix-style newlines with a newline ending every file, utf-8 charset 4 | [*] 5 | charset = utf-8 6 | end_of_line = lf 7 | insert_final_newline = true 8 | trim_trailing_whitespace = true 9 | 10 | # Match nix files, set indent to spaces with width of two 11 | [*.{nix,json,md}] 12 | indent_style = space 13 | indent_size = 2 14 | -------------------------------------------------------------------------------- /modules/hardware/default.nix: -------------------------------------------------------------------------------- 1 | # Hardware-related modules 2 | { ... }: 3 | { 4 | imports = [ 5 | ./action-on-low-power 6 | ./bluetooth 7 | ./debug 8 | ./drive-monitor 9 | ./firmware 10 | ./graphics 11 | ./id-card 12 | ./keychron 13 | ./monitor 14 | ./sound 15 | ./thunderbolt 16 | ./yubikey 17 | ]; 18 | } 19 | -------------------------------------------------------------------------------- /machines/core/modules.nix: -------------------------------------------------------------------------------- 1 | _: { 2 | boot.initrd = { 3 | availableKernelModules = [ 4 | "ahci" 5 | "e1000e" 6 | "ehci_pci" 7 | "nvme" 8 | "sd_mod" 9 | "uas" 10 | "usbhid" 11 | "usb_storage" 12 | "xhci_pci" 13 | ]; 14 | 15 | kernelModules = [ 16 | "e1000e" 17 | "nvme" 18 | ]; 19 | }; 20 | } 21 | -------------------------------------------------------------------------------- /overlays/default.nix: -------------------------------------------------------------------------------- 1 | # overlays for nixpkgs 2 | _self: _super: { 3 | # freshrss = _super.freshrss.overrideAttrs (old: { 4 | # version = "1.21.0"; 5 | # src = _super.fetchFromGitHub { 6 | # owner = "FreshRSS"; 7 | # repo = "FreshRSS"; 8 | # rev = "1.21.0"; 9 | # hash = "sha256-0+fMZ5ps0CkBbS+fcxlYrrkQi28tmrKTyl3kPuofqyI="; 10 | # }; 11 | # }); 12 | } 13 | -------------------------------------------------------------------------------- /modules/hardware/id-card/default.nix: -------------------------------------------------------------------------------- 1 | { config, lib, ... }: 2 | let 3 | cfg = config.my.hardware.id-card; 4 | in 5 | { 6 | options.my.hardware.id-card = { 7 | enable = lib.mkEnableOption "german id card authentication"; 8 | }; 9 | 10 | config = lib.mkIf cfg.enable { 11 | programs.ausweisapp = { 12 | enable = true; 13 | openFirewall = true; 14 | }; 15 | }; 16 | } 17 | -------------------------------------------------------------------------------- /images/rpi4-image.nix: -------------------------------------------------------------------------------- 1 | # nix build .#install-sd-aarch64 --system aarch64-linux 2 | # zstd -vdcfT6 /nix/store/...-aarch64-linux.img/sd-image/...-aarch64-linux.img.zst | dd of=/dev/sdX status=progress bs=64K 3 | { ... }: 4 | { 5 | nixpkgs.localSystem.system = "aarch64-linux"; 6 | imports = [ 7 | 8 | ./base-config.nix 9 | ]; 10 | } 11 | -------------------------------------------------------------------------------- /profiles/android/default.nix: -------------------------------------------------------------------------------- 1 | { 2 | config, 3 | lib, 4 | pkgs, 5 | ... 6 | }: 7 | let 8 | cfg = config.my.profiles.android; 9 | in 10 | { 11 | options.my.profiles.android = { 12 | enable = lib.mkEnableOption "android profile"; 13 | }; 14 | 15 | config = lib.mkIf cfg.enable { 16 | programs.adb.enable = true; 17 | environment.systemPackages = with pkgs; [ scrcpy ]; 18 | }; 19 | } 20 | -------------------------------------------------------------------------------- /machines/core/network.nix: -------------------------------------------------------------------------------- 1 | _: { 2 | networking.networkmanager = { 3 | enable = true; 4 | 5 | unmanaged = [ 6 | "interface-name:br-*" # Ignore docker compose network bridges 7 | "interface-name:docker?" # Ignore docker default bridge 8 | "interface-name:veth*" # Ignore docker compose network devices 9 | "interface-name:virbr?" # Ignore libvirt default bridge 10 | ]; 11 | }; 12 | 13 | } 14 | -------------------------------------------------------------------------------- /profiles/webcam/default.nix: -------------------------------------------------------------------------------- 1 | { 2 | config, 3 | lib, 4 | pkgs, 5 | ... 6 | }: 7 | let 8 | cfg = config.my.profiles.webcam; 9 | in 10 | { 11 | options.my.profiles.webcam = with lib; { 12 | enable = mkEnableOption "webcam profile"; 13 | }; 14 | 15 | config = lib.mkIf cfg.enable { 16 | environment.systemPackages = with pkgs; [ 17 | fswebcam 18 | # gnome.cheese does no longer work 19 | ]; 20 | }; 21 | } 22 | -------------------------------------------------------------------------------- /machines/newton/hardware-configuration.nix: -------------------------------------------------------------------------------- 1 | { 2 | config, 3 | lib, 4 | modulesPath, 5 | ... 6 | }: 7 | { 8 | imports = [ (modulesPath + "/profiles/qemu-guest.nix") ]; 9 | 10 | boot.initrd.availableKernelModules = [ 11 | "ata_piix" 12 | "sd_mod" 13 | "uhci_hcd" 14 | "virtio_pci" 15 | "virtio_scsi" 16 | ]; 17 | 18 | hardware.cpu.amd.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware; 19 | } 20 | -------------------------------------------------------------------------------- /modules/hardware/thunderbolt/default.nix: -------------------------------------------------------------------------------- 1 | { 2 | config, 3 | lib, 4 | pkgs, 5 | ... 6 | }: 7 | let 8 | cfg = config.my.hardware.thunderbolt; 9 | in 10 | { 11 | options.my.hardware.thunderbolt = { 12 | enable = lib.mkEnableOption "thunderbolt configuration"; 13 | }; 14 | 15 | config = lib.mkIf cfg.enable { 16 | environment.systemPackages = with pkgs; [ thunderbolt ]; 17 | services.hardware.bolt.enable = true; 18 | }; 19 | } 20 | -------------------------------------------------------------------------------- /profiles/3d-design/default.nix: -------------------------------------------------------------------------------- 1 | { 2 | config, 3 | lib, 4 | pkgs, 5 | ... 6 | }: 7 | let 8 | cfg = config.my.profiles."3d-design"; 9 | in 10 | { 11 | options.my.profiles."3d-design" = { 12 | enable = lib.mkEnableOption "3d-design profile"; 13 | }; 14 | 15 | config = lib.mkIf cfg.enable { 16 | environment.systemPackages = with pkgs; [ 17 | meshlab 18 | openscad-unstable 19 | prusa-slicer 20 | ]; 21 | }; 22 | } 23 | -------------------------------------------------------------------------------- /profiles/update/default.nix: -------------------------------------------------------------------------------- 1 | { 2 | config, 3 | lib, 4 | pkgs, 5 | ... 6 | }: 7 | let 8 | cfg = config.my.profiles.update; 9 | in 10 | { 11 | options.my.profiles.update = with lib; { 12 | enable = mkEnableOption "update profile"; 13 | }; 14 | 15 | config = lib.mkIf cfg.enable { 16 | # Enable firmware update daemon 17 | services.fwupd.enable = true; 18 | 19 | environment.systemPackages = with pkgs; [ topgrade ]; 20 | }; 21 | } 22 | -------------------------------------------------------------------------------- /modules/hardware/monitor/default.nix: -------------------------------------------------------------------------------- 1 | { 2 | config, 3 | lib, 4 | pkgs, 5 | ... 6 | }: 7 | let 8 | cfg = config.my.hardware.monitor; 9 | in 10 | { 11 | options.my.hardware.monitor = { 12 | enable = lib.mkEnableOption "monitor configuration"; 13 | }; 14 | 15 | config = lib.mkIf cfg.enable { 16 | hardware.i2c.enable = true; 17 | 18 | environment.systemPackages = with pkgs; [ 19 | ddcutil 20 | ddcui 21 | ]; 22 | }; 23 | } 24 | -------------------------------------------------------------------------------- /profiles/meeting/default.nix: -------------------------------------------------------------------------------- 1 | { 2 | config, 3 | lib, 4 | pkgs, 5 | ... 6 | }: 7 | let 8 | cfg = config.my.profiles.meeting; 9 | in 10 | { 11 | options.my.profiles.meeting = with lib; { 12 | enable = mkEnableOption "meeting profile"; 13 | }; 14 | 15 | config = lib.mkIf cfg.enable { 16 | environment.systemPackages = with pkgs; [ 17 | element-desktop 18 | fractal 19 | mumble 20 | teamspeak6-client 21 | ]; 22 | }; 23 | } 24 | -------------------------------------------------------------------------------- /profiles/sway/autostart.nix: -------------------------------------------------------------------------------- 1 | { config, lib, ... }: 2 | let 3 | cfg = config.my.profiles.sway-autostart; 4 | in 5 | { 6 | options.my.profiles.sway-autostart = with lib; { 7 | enable = mkEnableOption "sway-autostart profile"; 8 | }; 9 | 10 | config = lib.mkIf cfg.enable { 11 | 12 | # start sway if login happens 13 | environment.interactiveShellInit = '' 14 | if test `tty` = /dev/tty1; then 15 | exec sway 16 | fi 17 | ''; 18 | }; 19 | } 20 | -------------------------------------------------------------------------------- /profiles/sync/default.nix: -------------------------------------------------------------------------------- 1 | { 2 | config, 3 | lib, 4 | pkgs, 5 | ... 6 | }: 7 | let 8 | cfg = config.my.profiles.sync; 9 | in 10 | { 11 | options.my.profiles.sync = with lib; { 12 | enable = mkEnableOption "sync profile"; 13 | }; 14 | 15 | config = lib.mkIf cfg.enable { 16 | environment.systemPackages = with pkgs; [ 17 | croc 18 | magic-wormhole 19 | nextcloud-client 20 | syncthing 21 | vdirsyncer 22 | ]; 23 | }; 24 | } 25 | -------------------------------------------------------------------------------- /profiles/latex/default.nix: -------------------------------------------------------------------------------- 1 | { 2 | config, 3 | lib, 4 | pkgs, 5 | ... 6 | }: 7 | let 8 | cfg = config.my.profiles.latex; 9 | in 10 | { 11 | options.my.profiles.latex = with lib; { 12 | enable = mkEnableOption "latex profile"; 13 | }; 14 | 15 | config = lib.mkIf cfg.enable { 16 | environment.systemPackages = with pkgs; [ 17 | biber 18 | pdfpc 19 | qtikz 20 | texlive.combined.scheme-full 21 | texstudio 22 | ]; 23 | }; 24 | } 25 | -------------------------------------------------------------------------------- /machines/workman/boot.nix: -------------------------------------------------------------------------------- 1 | { 2 | pkgs, 3 | ... 4 | }: 5 | { 6 | boot = { 7 | loader = { 8 | timeout = 1; 9 | systemd-boot = { 10 | enable = true; 11 | configurationLimit = 10; 12 | consoleMode = "keep"; 13 | editor = true; 14 | }; 15 | efi.canTouchEfiVariables = true; 16 | }; 17 | plymouth = { 18 | enable = true; 19 | theme = "framework"; 20 | themePackages = [ pkgs.framework-plymouth ]; 21 | }; 22 | }; 23 | } 24 | -------------------------------------------------------------------------------- /machines/newton/configuration.nix: -------------------------------------------------------------------------------- 1 | { ... }: 2 | { 3 | imports = [ 4 | ./boot.nix 5 | ./disko-config.nix 6 | ./hardware-configuration.nix 7 | ./network.nix 8 | ./nixinate.nix 9 | ./services.nix 10 | ./syncthing.nix 11 | ./system.nix 12 | ]; 13 | 14 | networking.hostName = "newton"; 15 | 16 | sops = { 17 | defaultSopsFile = ./secrets.yaml; 18 | gnupg.sshKeyPaths = [ ]; 19 | }; 20 | 21 | system = { 22 | stateVersion = "24.05"; 23 | autoUpgrade.enable = true; 24 | }; 25 | } 26 | -------------------------------------------------------------------------------- /profiles/default.nix: -------------------------------------------------------------------------------- 1 | # Configuration that spans across system and home, or are collections of modules 2 | { 3 | imports = [ 4 | ./3d-design 5 | ./android 6 | ./clean 7 | ./desktop-apps 8 | ./desktop-dev 9 | ./development 10 | ./filesystem 11 | ./gaming 12 | ./gnome 13 | ./latex 14 | ./media 15 | ./meeting 16 | ./nautilus 17 | ./nix 18 | ./powersave 19 | ./printing 20 | ./sway 21 | ./sync 22 | ./update 23 | ./usb-iso 24 | ./webcam 25 | ]; 26 | } 27 | -------------------------------------------------------------------------------- /profiles/filesystem/default.nix: -------------------------------------------------------------------------------- 1 | { 2 | config, 3 | lib, 4 | pkgs, 5 | ... 6 | }: 7 | let 8 | cfg = config.my.profiles.filesystem; 9 | in 10 | { 11 | options.my.profiles.filesystem = with lib; { 12 | enable = mkEnableOption "filesystem profile"; 13 | }; 14 | 15 | config = lib.mkIf cfg.enable { 16 | environment.systemPackages = with pkgs; [ 17 | davfs2 18 | exfat 19 | fuse3 20 | hfsprogs 21 | mtpfs 22 | nfs-utils 23 | ntfs3g 24 | sshfs 25 | ]; 26 | }; 27 | } 28 | -------------------------------------------------------------------------------- /modules/hardware/yubikey/default.nix: -------------------------------------------------------------------------------- 1 | { 2 | config, 3 | lib, 4 | pkgs, 5 | ... 6 | }: 7 | let 8 | cfg = config.my.hardware.yubikey; 9 | in 10 | { 11 | options.my.hardware.yubikey = { 12 | enable = lib.mkEnableOption "yubikey configuration"; 13 | }; 14 | 15 | config = lib.mkIf cfg.enable { 16 | programs = { 17 | yubikey-manager.enable = true; 18 | ssh.startAgent = false; 19 | gnupg.agent = { 20 | enable = true; 21 | enableSSHSupport = true; 22 | }; 23 | }; 24 | }; 25 | } 26 | -------------------------------------------------------------------------------- /profiles/clean/default.nix: -------------------------------------------------------------------------------- 1 | { 2 | config, 3 | lib, 4 | pkgs, 5 | ... 6 | }: 7 | let 8 | cfg = config.my.profiles.clean; 9 | in 10 | { 11 | options.my.profiles.clean = { 12 | enable = lib.mkEnableOption "clean profile"; 13 | }; 14 | 15 | config = lib.mkIf cfg.enable { 16 | services.angrr = { 17 | enable = true; 18 | timer.enable = true; 19 | }; 20 | 21 | environment.systemPackages = with pkgs; [ 22 | baobab 23 | dupeguru 24 | jdupes 25 | kondo 26 | ]; 27 | }; 28 | } 29 | -------------------------------------------------------------------------------- /modules/hardware/debug/default.nix: -------------------------------------------------------------------------------- 1 | { 2 | config, 3 | lib, 4 | pkgs, 5 | ... 6 | }: 7 | let 8 | cfg = config.my.hardware.debug; 9 | in 10 | { 11 | options.my.hardware.debug = { 12 | enable = lib.mkEnableOption "hardware-debug configuration"; 13 | }; 14 | 15 | config = lib.mkIf cfg.enable { 16 | environment.systemPackages = with pkgs; [ 17 | dmidecode 18 | f3 19 | hdparm 20 | lm_sensors 21 | nvme-cli 22 | pciutils 23 | smartmontools 24 | testdisk 25 | ]; 26 | }; 27 | } 28 | -------------------------------------------------------------------------------- /profiles/nix/default.nix: -------------------------------------------------------------------------------- 1 | { 2 | config, 3 | lib, 4 | pkgs, 5 | ... 6 | }: 7 | let 8 | cfg = config.my.profiles.nix; 9 | in 10 | { 11 | options.my.profiles.nix = with lib; { 12 | enable = mkEnableOption "nix profile"; 13 | }; 14 | 15 | config = lib.mkIf cfg.enable { 16 | environment.systemPackages = with pkgs; [ 17 | nix-index 18 | nix-init 19 | nix-prefetch 20 | nix-update 21 | nixfmt-rfc-style 22 | nixpkgs-hammering 23 | nixpkgs-review 24 | shh 25 | ]; 26 | }; 27 | } 28 | -------------------------------------------------------------------------------- /modules/hardware/keychron/default.nix: -------------------------------------------------------------------------------- 1 | { 2 | config, 3 | lib, 4 | pkgs, 5 | ... 6 | }: 7 | let 8 | cfg = config.my.hardware.keychron; 9 | in 10 | { 11 | options.my.hardware.keychron = { 12 | enable = lib.mkEnableOption "keychron configuration"; 13 | }; 14 | 15 | config = lib.mkIf cfg.enable { 16 | hardware.keyboard.qmk.enable = true; 17 | 18 | services.udev.packages = with pkgs; [ 19 | via 20 | ]; 21 | 22 | environment.systemPackages = with pkgs; [ 23 | qmk 24 | via 25 | ]; 26 | }; 27 | } 28 | -------------------------------------------------------------------------------- /modules/system/spell-check/default.nix: -------------------------------------------------------------------------------- 1 | # spell-checking 2 | { 3 | config, 4 | lib, 5 | pkgs, 6 | ... 7 | }: 8 | let 9 | cfg = config.my.system.spell-check; 10 | in 11 | { 12 | options.my.system.spell-check = { 13 | enable = lib.mkEnableOption "spell-check configuration"; 14 | }; 15 | 16 | config = lib.mkIf cfg.enable { 17 | environment.systemPackages = with pkgs; [ 18 | aspell 19 | aspellDicts.de 20 | aspellDicts.en 21 | aspellDicts.en-computers 22 | aspellDicts.en-science 23 | ]; 24 | }; 25 | } 26 | -------------------------------------------------------------------------------- /profiles/usb-iso/default.nix: -------------------------------------------------------------------------------- 1 | { 2 | config, 3 | lib, 4 | pkgs, 5 | ... 6 | }: 7 | let 8 | cfg = config.my.profiles.usb-iso; 9 | in 10 | { 11 | options.my.profiles.usb-iso = with lib; { 12 | enable = mkEnableOption "usb-iso profile"; 13 | }; 14 | 15 | config = lib.mkIf cfg.enable { 16 | # binary blobs are needed for ventoy 17 | nixpkgs.config.permittedInsecurePackages = [ 18 | "ventoy-1.1.07" 19 | ]; 20 | environment.systemPackages = with pkgs; [ 21 | ventoy-full # general 22 | woeusb-ng # windows 23 | ]; 24 | }; 25 | } 26 | -------------------------------------------------------------------------------- /machines/serverle/configuration.nix: -------------------------------------------------------------------------------- 1 | { ... }: 2 | { 3 | imports = [ 4 | ./disko-config.nix 5 | ./hardware-configuration.nix 6 | ./network.nix 7 | ./nixinate.nix 8 | ./services.nix 9 | ./syncthing.nix 10 | ./system.nix 11 | ./wifi.nix 12 | ]; 13 | 14 | networking.hostName = "serverle"; 15 | 16 | sops = { 17 | defaultSopsFile = ./secrets.yaml; 18 | # disable gpg and thereby enable age 19 | gnupg.sshKeyPaths = [ ]; 20 | }; 21 | 22 | system = { 23 | stateVersion = "24.05"; 24 | autoUpgrade.enable = true; 25 | }; 26 | } 27 | -------------------------------------------------------------------------------- /machines/thinkman/services.nix: -------------------------------------------------------------------------------- 1 | # Deployed services 2 | { config, ... }: 3 | let 4 | inherit (config.sops) secrets; 5 | in 6 | { 7 | sops.secrets."borgbackup/password" = { }; 8 | sops.secrets."borgbackup/ssh_key" = { }; 9 | 10 | # List services that you want to enable: 11 | my.services = { 12 | backup = { 13 | enable = true; 14 | OnFailureNotification = true; 15 | passwordFile = secrets."borgbackup/password".path; 16 | sshKeyFile = secrets."borgbackup/ssh_key".path; 17 | paths = [ "/" ]; 18 | }; 19 | vpn.enable = true; 20 | }; 21 | } 22 | -------------------------------------------------------------------------------- /machines/workman/services.nix: -------------------------------------------------------------------------------- 1 | # Deployed services 2 | { config, ... }: 3 | let 4 | inherit (config.sops) secrets; 5 | in 6 | { 7 | sops.secrets."borgbackup/password" = { }; 8 | sops.secrets."borgbackup/ssh_key" = { }; 9 | 10 | # List services that you want to enable: 11 | my.services = { 12 | backup = { 13 | enable = true; 14 | OnFailureNotification = true; 15 | passwordFile = secrets."borgbackup/password".path; 16 | sshKeyFile = secrets."borgbackup/ssh_key".path; 17 | paths = [ "/" ]; 18 | }; 19 | vpn.enable = true; 20 | }; 21 | } 22 | -------------------------------------------------------------------------------- /machines/workman/remote-build.nix: -------------------------------------------------------------------------------- 1 | # enabled remote-build service 2 | { config, ... }: 3 | let 4 | inherit (config.sops) secrets; 5 | in 6 | { 7 | sops.secrets."nixremote/ssh_key" = { }; 8 | nix.buildMachines = [ 9 | { 10 | hostName = "buehler.rocks"; 11 | system = "x86_64-linux"; 12 | supportedFeatures = [ 13 | "benchmark" 14 | "kvm" 15 | "big-parallel" 16 | ]; 17 | sshUser = "nixremote"; 18 | sshKey = secrets."nixremote/ssh_key".path; 19 | maxJobs = 4; 20 | } 21 | ]; 22 | 23 | nix.distributedBuilds = true; 24 | } 25 | -------------------------------------------------------------------------------- /machines/thinkman/remote-build.nix: -------------------------------------------------------------------------------- 1 | # enabled remote-build service 2 | { config, ... }: 3 | let 4 | inherit (config.sops) secrets; 5 | in 6 | { 7 | sops.secrets."nixremote/ssh_key" = { }; 8 | nix.buildMachines = [ 9 | { 10 | hostName = "buehler.rocks"; 11 | system = "x86_64-linux"; 12 | supportedFeatures = [ 13 | "benchmark" 14 | "kvm" 15 | "big-parallel" 16 | ]; 17 | sshUser = "nixremote"; 18 | sshKey = secrets."nixremote/ssh_key".path; 19 | maxJobs = 4; 20 | } 21 | ]; 22 | 23 | nix.distributedBuilds = true; 24 | } 25 | -------------------------------------------------------------------------------- /modules/system/avahi/default.nix: -------------------------------------------------------------------------------- 1 | # avahi related settings 2 | { 3 | config, 4 | lib, 5 | ... 6 | }: 7 | let 8 | cfg = config.my.system.avahi; 9 | in 10 | { 11 | options.my.system.avahi = { 12 | enable = lib.mkEnableOption "avahi configuration"; 13 | }; 14 | 15 | config = lib.mkIf cfg.enable { 16 | services.avahi = { 17 | enable = true; 18 | nssmdns4 = true; 19 | nssmdns6 = true; 20 | publish = { 21 | enable = true; 22 | addresses = true; 23 | workstation = true; 24 | userServices = true; 25 | }; 26 | }; 27 | }; 28 | } 29 | -------------------------------------------------------------------------------- /profiles/printing/default.nix: -------------------------------------------------------------------------------- 1 | { 2 | config, 3 | lib, 4 | pkgs, 5 | ... 6 | }: 7 | let 8 | cfg = config.my.profiles.printing; 9 | in 10 | { 11 | options.my.profiles.printing = with lib; { 12 | enable = mkEnableOption "printing profile"; 13 | }; 14 | 15 | config = lib.mkIf cfg.enable { 16 | services.printing.enable = true; 17 | services.printing.drivers = with pkgs; [ 18 | gutenprint 19 | gutenprintBin 20 | hplip 21 | ]; 22 | programs.system-config-printer.enable = true; 23 | 24 | environment.systemPackages = with pkgs; [ simple-scan ]; 25 | }; 26 | } 27 | -------------------------------------------------------------------------------- /modules/system/miracast/default.nix: -------------------------------------------------------------------------------- 1 | # miracast related settings 2 | { 3 | config, 4 | lib, 5 | pkgs, 6 | ... 7 | }: 8 | let 9 | cfg = config.my.system.miracast; 10 | in 11 | { 12 | options.my.system.miracast = { 13 | enable = lib.mkEnableOption "miracast configuration"; 14 | }; 15 | 16 | config = lib.mkIf cfg.enable { 17 | environment.systemPackages = with pkgs; [ 18 | gnome-network-displays 19 | ]; 20 | 21 | networking.firewall.allowedTCPPorts = [ 22 | 7236 23 | 7250 24 | ]; 25 | networking.firewall.allowedUDPPorts = [ 26 | 7236 27 | 5353 28 | ]; 29 | }; 30 | } 31 | -------------------------------------------------------------------------------- /profiles/powersave/default.nix: -------------------------------------------------------------------------------- 1 | { 2 | config, 3 | lib, 4 | pkgs, 5 | ... 6 | }: 7 | let 8 | cfg = config.my.profiles.powersave; 9 | in 10 | { 11 | options.my.profiles.powersave = with lib; { 12 | enable = mkEnableOption "powersave profile"; 13 | }; 14 | 15 | config = lib.mkIf cfg.enable { 16 | environment.systemPackages = with pkgs; [ 17 | powertop 18 | s-tui 19 | ]; 20 | 21 | powerManagement = { 22 | cpuFreqGovernor = "powersave"; 23 | powertop.enable = true; 24 | }; 25 | 26 | services = { 27 | thermald.enable = true; 28 | upower.enable = true; 29 | }; 30 | }; 31 | } 32 | -------------------------------------------------------------------------------- /modules/services/alertmanager/config.nix: -------------------------------------------------------------------------------- 1 | { 2 | global = { 3 | smtp_smarthost = "localhost:25"; 4 | smtp_from = "server@buehler.rocks"; 5 | }; 6 | # templates = [ ]; 7 | route = { 8 | receiver = "default"; 9 | group_wait = "30s"; 10 | group_interval = "5m"; 11 | repeat_interval = "4h"; 12 | routes = [ ]; 13 | }; 14 | receivers = [ 15 | { 16 | name = "default"; 17 | email_configs = [ { to = "server@buehler.rocks"; } ]; 18 | webhook_configs = [ 19 | { 20 | url = "http://localhost:4050/alert"; 21 | send_resolved = true; 22 | } 23 | ]; 24 | } 25 | ]; 26 | } 27 | -------------------------------------------------------------------------------- /.github/workflows/nix.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # yamllint disable-line rule:truthy 3 | on: 4 | pull_request: 5 | push: 6 | branches: [main, master] 7 | 8 | jobs: 9 | build: 10 | name: Build Nix targets 11 | runs-on: ubuntu-22.04 12 | steps: 13 | - name: Git checkout 14 | uses: actions/checkout@v4 15 | - name: Install Nix 16 | uses: cachix/install-nix-action@v30 17 | - name: Check Nix flake inputs 18 | uses: DeterminateSystems/flake-checker-action@v9 19 | with: 20 | ignore-missing-flake-lock: false 21 | fail-mode: true 22 | - name: Check Nix flake 23 | run: nix flake check --all-systems 24 | -------------------------------------------------------------------------------- /modules/hardware/bluetooth/default.nix: -------------------------------------------------------------------------------- 1 | { 2 | config, 3 | lib, 4 | pkgs, 5 | ... 6 | }: 7 | let 8 | cfg = config.my.hardware.bluetooth; 9 | in 10 | { 11 | options.my.hardware.bluetooth = { 12 | enable = lib.mkEnableOption "bluetooth configuration"; 13 | }; 14 | 15 | config = lib.mkIf cfg.enable { 16 | hardware.bluetooth = { 17 | enable = true; 18 | package = pkgs.bluez; 19 | settings = { 20 | General = { 21 | Enable = "Source,Sink,Media,Socket"; 22 | }; 23 | }; 24 | }; 25 | services.blueman.enable = true; 26 | environment.systemPackages = with pkgs; [ sony-headphones-client ]; 27 | }; 28 | } 29 | -------------------------------------------------------------------------------- /machines/thinkman/network.nix: -------------------------------------------------------------------------------- 1 | # network settings 2 | _: { 3 | # hotfixes for dns settings 4 | networking.extraHosts = 5 | let 6 | serverle_ip = "192.168.178.60"; 7 | in 8 | '' 9 | ${serverle_ip} stunkymonkey.de 10 | ${serverle_ip} automation.stunkymonkey.de 11 | ${serverle_ip} download.stunkymonkey.de 12 | ${serverle_ip} esphome.stunkymonkey.de 13 | ${serverle_ip} indexer.stunkymonkey.de 14 | ${serverle_ip} media.stunkymonkey.de 15 | ${serverle_ip} movies.stunkymonkey.de 16 | ${serverle_ip} series.stunkymonkey.de 17 | ${serverle_ip} subtitles.stunkymonkey.de 18 | ${serverle_ip} view.stunkymonkey.de 19 | ''; 20 | } 21 | -------------------------------------------------------------------------------- /machines/thinkman/profiles.nix: -------------------------------------------------------------------------------- 1 | # enabled profiles 2 | _: { 3 | my.profiles = { 4 | "3d-design".enable = true; 5 | android.enable = true; 6 | clean.enable = true; 7 | desktop-apps.enable = true; 8 | desktop-dev.enable = true; 9 | development.enable = true; 10 | filesystem.enable = true; 11 | gaming.enable = true; 12 | latex.enable = true; 13 | media.enable = true; 14 | meeting.enable = true; 15 | nautilus.enable = true; 16 | powersave.enable = true; 17 | printing.enable = true; 18 | sway.enable = true; 19 | sync.enable = true; 20 | update.enable = true; 21 | usb-iso.enable = true; 22 | webcam.enable = true; 23 | }; 24 | } 25 | -------------------------------------------------------------------------------- /machines/workman/configuration.nix: -------------------------------------------------------------------------------- 1 | { ... }: 2 | { 3 | imports = [ 4 | ./boot.nix 5 | ./disko-config.nix 6 | ./hardware-configuration.nix 7 | ./network.nix 8 | ./profiles.nix 9 | ./remote-build.nix 10 | ./services.nix 11 | ./system.nix 12 | ]; 13 | 14 | networking.hostName = "workman"; 15 | 16 | sops = { 17 | defaultSopsFile = ./secrets.yaml; 18 | age.sshKeyPaths = [ "/etc/ssh/ssh_host_ed25519_key" ]; 19 | gnupg.sshKeyPaths = [ ]; 20 | }; 21 | 22 | # needed for cross-compilation 23 | boot.binfmt.emulatedSystems = [ "aarch64-linux" ]; 24 | 25 | system = { 26 | stateVersion = "24.11"; 27 | autoUpgrade.enable = true; 28 | }; 29 | } 30 | -------------------------------------------------------------------------------- /machines/workman/network.nix: -------------------------------------------------------------------------------- 1 | # network settings 2 | _: { 3 | # hotfixes for dns settings 4 | networking.extraHosts = 5 | let 6 | serverle_ip = "192.168.178.60"; 7 | in 8 | '' 9 | ${serverle_ip} stunkymonkey.de 10 | ${serverle_ip} automation.stunkymonkey.de 11 | ${serverle_ip} download.stunkymonkey.de 12 | ${serverle_ip} esphome.stunkymonkey.de 13 | ${serverle_ip} indexer.stunkymonkey.de 14 | ${serverle_ip} media.stunkymonkey.de 15 | ${serverle_ip} movies.stunkymonkey.de 16 | ${serverle_ip} series.stunkymonkey.de 17 | ${serverle_ip} subtitles.stunkymonkey.de 18 | ${serverle_ip} view.stunkymonkey.de 19 | ''; 20 | } 21 | -------------------------------------------------------------------------------- /machines/thinkman/configuration.nix: -------------------------------------------------------------------------------- 1 | { ... }: 2 | { 3 | imports = [ 4 | ./boot.nix 5 | ./disko-config.nix 6 | ./hardware-configuration.nix 7 | ./network.nix 8 | ./profiles.nix 9 | ./remote-build.nix 10 | ./services.nix 11 | ./system.nix 12 | ]; 13 | 14 | networking.hostName = "thinkman"; 15 | 16 | sops = { 17 | defaultSopsFile = ./secrets.yaml; 18 | age.sshKeyPaths = [ "/etc/ssh/ssh_host_ed25519_key" ]; 19 | gnupg.sshKeyPaths = [ ]; 20 | }; 21 | 22 | # needed for cross-compilation 23 | boot.binfmt.emulatedSystems = [ "aarch64-linux" ]; 24 | 25 | system = { 26 | stateVersion = "24.05"; 27 | autoUpgrade.enable = true; 28 | }; 29 | } 30 | -------------------------------------------------------------------------------- /profiles/sway/theme.nix: -------------------------------------------------------------------------------- 1 | { 2 | config, 3 | lib, 4 | pkgs, 5 | ... 6 | }: 7 | let 8 | cfg = config.my.profiles.sway-theme; 9 | in 10 | { 11 | options.my.profiles.sway-theme = with lib; { 12 | enable = mkEnableOption "sway-theme profile"; 13 | }; 14 | 15 | config = lib.mkIf cfg.enable { 16 | environment.systemPackages = with pkgs; [ 17 | gtk-engine-murrine 18 | gtk_engines 19 | gsettings-desktop-schemas 20 | lxappearance 21 | qgnomeplatform 22 | numix-cursor-theme 23 | numix-icon-theme 24 | numix-icon-theme-circle 25 | adwaita-qt 26 | arc-kde-theme 27 | arc-theme 28 | ]; 29 | qt.platformTheme = "qt5ct"; 30 | }; 31 | } 32 | -------------------------------------------------------------------------------- /machines/workman/profiles.nix: -------------------------------------------------------------------------------- 1 | # enabled profiles 2 | _: { 3 | my.profiles = { 4 | "3d-design".enable = true; 5 | android.enable = true; 6 | clean.enable = true; 7 | desktop-apps.enable = true; 8 | desktop-dev.enable = true; 9 | development.enable = true; 10 | filesystem.enable = true; 11 | gaming.enable = true; 12 | latex.enable = true; 13 | media.enable = true; 14 | meeting.enable = true; 15 | nautilus.enable = true; 16 | nix.enable = true; 17 | powersave.enable = true; 18 | printing.enable = true; 19 | sway.enable = true; 20 | sync.enable = true; 21 | update.enable = true; 22 | usb-iso.enable = true; 23 | webcam.enable = true; 24 | }; 25 | } 26 | -------------------------------------------------------------------------------- /profiles/sway/location.nix: -------------------------------------------------------------------------------- 1 | { config, lib, ... }: 2 | let 3 | cfg = config.my.profiles.sway-location; 4 | in 5 | { 6 | options.my.profiles.sway-location = with lib; { 7 | enable = mkEnableOption "sway-location profile"; 8 | }; 9 | 10 | config = lib.mkIf cfg.enable { 11 | location.provider = "geoclue2"; 12 | 13 | services.geoclue2 = { 14 | enable = true; 15 | enableDemoAgent = true; 16 | 17 | appConfig."gammastep" = { 18 | desktopID = "gammastep"; 19 | isAllowed = true; 20 | isSystem = false; 21 | }; 22 | appConfig."gammastep-indicator" = { 23 | desktopID = "gammastep-indicator"; 24 | isAllowed = true; 25 | isSystem = false; 26 | }; 27 | }; 28 | }; 29 | } 30 | -------------------------------------------------------------------------------- /modules/services/jellyfin/enable-metrics.patch: -------------------------------------------------------------------------------- 1 | diff --git a/MediaBrowser.Model/Configuration/ServerConfiguration.cs b/MediaBrowser.Model/Configuration/ServerConfiguration.cs 2 | index 52f7e53b81..b149e3251a 100644 3 | --- a/MediaBrowser.Model/Configuration/ServerConfiguration.cs 4 | +++ b/MediaBrowser.Model/Configuration/ServerConfiguration.cs 5 | @@ -67,7 +67,7 @@ public class ServerConfiguration : BaseApplicationConfiguration 6 | /// 7 | /// Gets or sets a value indicating whether to enable prometheus metrics exporting. 8 | /// 9 | - public bool EnableMetrics { get; set; } = false; 10 | + public bool EnableMetrics { get; set; } = true; 11 | 12 | public bool EnableNormalizedItemByNameIds { get; set; } = true; 13 | 14 | -------------------------------------------------------------------------------- /machines/core/core.nix: -------------------------------------------------------------------------------- 1 | { pkgs, ... }: 2 | { 3 | # Packages 4 | environment.systemPackages = with pkgs; [ 5 | bandwhich 6 | bind # dig 7 | borgbackup 8 | cryptsetup 9 | delta 10 | fd # find replacement 11 | file 12 | fzf 13 | gettext 14 | git 15 | gptfdisk 16 | htop 17 | jq 18 | killall 19 | lsof 20 | mosh 21 | mtr 22 | multipath-tools # kpartx 23 | nmap 24 | nmon 25 | ouch # de-/compress 26 | pciutils 27 | progress 28 | pv 29 | reptyr 30 | rsync 31 | screen 32 | sd # sed replacement 33 | stress-ng 34 | tmux 35 | unzip 36 | usbutils 37 | vim 38 | wget 39 | whois 40 | xcp 41 | zip 42 | ]; 43 | 44 | time.timeZone = "Europe/Berlin"; 45 | } 46 | -------------------------------------------------------------------------------- /modules/services/remote-build/default.nix: -------------------------------------------------------------------------------- 1 | # manages remote builds 2 | { 3 | config, 4 | lib, 5 | pkgs, 6 | ... 7 | }: 8 | let 9 | cfg = config.my.services.remote-build; 10 | in 11 | { 12 | options.my.services.remote-build = { 13 | enable = lib.mkEnableOption "remote-build user"; 14 | }; 15 | 16 | config = lib.mkIf cfg.enable { 17 | # Create user for distributed nix builds 18 | users.groups.nixremote = { }; 19 | users.users.nixremote = { 20 | isSystemUser = true; 21 | group = "nixremote"; 22 | shell = pkgs.bashInteractive; 23 | openssh.authorizedKeys.keys = [ 24 | "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGYSzDdxqaNHmaaLqEvOK/vB65zvqoCebI3Nxzgg5smq root@workman" 25 | ]; 26 | }; 27 | nix.settings.trusted-users = [ "nixremote" ]; 28 | }; 29 | } 30 | -------------------------------------------------------------------------------- /modules/services/minecraft-server/default.nix: -------------------------------------------------------------------------------- 1 | # sandbox video game 2 | { 3 | config, 4 | lib, 5 | pkgs, 6 | ... 7 | }: 8 | let 9 | cfg = config.my.services.minecraft-server; 10 | in 11 | { 12 | options.my.services.minecraft-server = { 13 | enable = lib.mkEnableOption "Minecraft Server"; 14 | }; 15 | 16 | config = lib.mkIf cfg.enable { 17 | services.minecraft-server = { 18 | enable = true; 19 | eula = true; 20 | package = pkgs.unstable.minecraft-server; 21 | openFirewall = true; 22 | 23 | jvmOpts = "-Xms8G -Xmx8G -XX:+UseG1GC -XX:+UnlockExperimentalVMOptions -XX:MaxGCPauseMillis=100 -XX:+DisableExplicitGC -XX:TargetSurvivorRatio=90 -XX:G1NewSizePercent=50 -XX:G1MaxNewSizePercent=80 -XX:G1MixedGCLiveThresholdPercent=50 -XX:+AlwaysPreTouch"; 24 | }; 25 | }; 26 | } 27 | -------------------------------------------------------------------------------- /modules/services/homepage/default.nix: -------------------------------------------------------------------------------- 1 | # My own personal homepage 2 | { 3 | config, 4 | lib, 5 | inputs, 6 | ... 7 | }: 8 | let 9 | cfg = config.my.services.homepage; 10 | inherit (config.networking) domain; 11 | in 12 | { 13 | options.my.services.homepage = { 14 | enable = lib.mkEnableOption "Stunkymonkey-Hompage"; 15 | }; 16 | 17 | config = lib.mkIf cfg.enable { 18 | 19 | my.services.webserver.virtualHosts = [ 20 | { 21 | subdomain = "blog"; 22 | root = inputs.stunkymonkey.packages.${config.nixpkgs.system}.default; 23 | } 24 | ]; 25 | 26 | webapps.apps.homepage = { 27 | dashboard = { 28 | name = "Homepage"; 29 | category = "other"; 30 | icon = "blog"; 31 | url = "https://blog.${domain}"; 32 | }; 33 | }; 34 | }; 35 | } 36 | -------------------------------------------------------------------------------- /modules/services/initrd-ssh/default.nix: -------------------------------------------------------------------------------- 1 | # The Free Software Media System 2 | { config, lib, ... }: 3 | let 4 | cfg = config.my.services.initrd-ssh; 5 | in 6 | { 7 | options.my.services.initrd-ssh = { 8 | enable = lib.mkEnableOption "Enable initrd-ssh service"; 9 | }; 10 | 11 | config = lib.mkIf cfg.enable { 12 | boot.initrd.network = { 13 | enable = true; 14 | 15 | ssh = { 16 | enable = true; 17 | port = 2222; 18 | hostKeys = [ "/etc/secrets/initrd/ssh_host_ed25519_key" ]; 19 | authorizedKeys = [ 20 | "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOFx6OLwL9MbkD3mnMsv+xrzZHN/rwCTgVs758SCLG0h felix@workman" 21 | ]; 22 | }; 23 | 24 | postCommands = '' 25 | echo 'cryptsetup-askpass' >> /root/.profile 26 | ''; 27 | }; 28 | }; 29 | } 30 | -------------------------------------------------------------------------------- /machines/core/nix.nix: -------------------------------------------------------------------------------- 1 | { inputs, ... }: 2 | { 3 | nix = { 4 | daemonCPUSchedPolicy = "idle"; 5 | daemonIOSchedClass = "idle"; 6 | 7 | settings = { 8 | trusted-users = [ 9 | "root" 10 | "@wheel" 11 | ]; 12 | auto-optimise-store = true; 13 | builders-use-substitutes = true; 14 | }; 15 | 16 | gc = { 17 | automatic = true; 18 | options = "--delete-older-than 30d"; 19 | }; 20 | 21 | extraOptions = '' 22 | experimental-features = nix-command flakes 23 | ''; 24 | 25 | registry = { 26 | nixpkgs.flake = inputs.nixpkgs; 27 | unstable.flake = inputs.nixpkgs-unstable; 28 | }; 29 | }; 30 | 31 | # auto upgrade with own flakes 32 | system.autoUpgrade = { 33 | enable = true; 34 | flake = "github:Stunkymonkey/nixos"; 35 | }; 36 | } 37 | -------------------------------------------------------------------------------- /profiles/sway/screen-sharing.nix: -------------------------------------------------------------------------------- 1 | { 2 | config, 3 | lib, 4 | pkgs, 5 | ... 6 | }: 7 | let 8 | cfg = config.my.profiles.sway-screen-sharing; 9 | in 10 | { 11 | options.my.profiles.sway-screen-sharing = with lib; { 12 | enable = mkEnableOption "sway-screen-sharing profile"; 13 | }; 14 | 15 | config = lib.mkIf cfg.enable { 16 | environment.systemPackages = with pkgs; [ remmina ]; 17 | 18 | services.pipewire.enable = true; 19 | 20 | xdg.portal = { 21 | enable = true; 22 | wlr.enable = true; 23 | extraPortals = with pkgs; [ xdg-desktop-portal-gtk ]; 24 | }; 25 | 26 | # for firefox 27 | environment.sessionVariables = { 28 | MOZ_ENABLE_WAYLAND = "1"; 29 | XDG_CURRENT_DESKTOP = "sway"; 30 | XDG_SESSION_TYPE = "wayland"; 31 | GTK_USE_PORTAL = "1"; 32 | }; 33 | }; 34 | } 35 | -------------------------------------------------------------------------------- /modules/services/jellyseerr/default.nix: -------------------------------------------------------------------------------- 1 | # manages and downloads films 2 | { config, lib, ... }: 3 | let 4 | cfg = config.my.services.jellyseerr; 5 | inherit (config.networking) domain; 6 | in 7 | { 8 | options.my.services.jellyseerr = { 9 | enable = lib.mkEnableOption "Sonarr for films management"; 10 | }; 11 | 12 | config = lib.mkIf cfg.enable { 13 | services.jellyseerr = { 14 | enable = true; 15 | }; 16 | 17 | my.services.webserver.virtualHosts = [ 18 | { 19 | subdomain = "view"; 20 | inherit (config.services.jellyseerr) port; 21 | } 22 | ]; 23 | 24 | webapps.apps.jellyseerr = { 25 | dashboard = { 26 | name = "View"; 27 | category = "media"; 28 | icon = "users-viewfinder"; 29 | url = "https://view.${domain}"; 30 | }; 31 | }; 32 | }; 33 | } 34 | -------------------------------------------------------------------------------- /machines/thinkman/hardware-configuration.nix: -------------------------------------------------------------------------------- 1 | _: 2 | let 3 | cpuFlavor = "intel"; 4 | in 5 | { 6 | # video driver 7 | boot.initrd.kernelModules = [ "i915" ]; 8 | 9 | # fix audio 10 | boot.extraModprobeConfig = '' 11 | options snd-hda-intel dmic_detect=0 12 | ''; 13 | 14 | # Special power management settings for ThinkPads 15 | services.tlp.enable = true; 16 | 17 | my.hardware = { 18 | bluetooth.enable = true; 19 | debug.enable = true; 20 | drive-monitor.enable = true; 21 | firmware = { 22 | enable = true; 23 | inherit cpuFlavor; 24 | }; 25 | graphics = { 26 | enable = true; 27 | inherit cpuFlavor; 28 | }; 29 | id-card.enable = true; 30 | keychron.enable = true; 31 | monitor.enable = true; 32 | sound.enable = true; 33 | thunderbolt.enable = true; 34 | yubikey.enable = true; 35 | }; 36 | } 37 | -------------------------------------------------------------------------------- /modules/hardware/sound/default.nix: -------------------------------------------------------------------------------- 1 | { 2 | config, 3 | lib, 4 | pkgs, 5 | ... 6 | }: 7 | let 8 | cfg = config.my.hardware.sound; 9 | in 10 | { 11 | options.my.hardware.sound = { 12 | enable = lib.mkEnableOption "sound configuration with pipewire"; 13 | }; 14 | 15 | config = lib.mkIf cfg.enable { 16 | 17 | # RealtimeKit is recommended 18 | security.rtkit.enable = true; 19 | 20 | services.pipewire = { 21 | enable = true; 22 | 23 | alsa = { 24 | enable = true; 25 | support32Bit = true; 26 | }; 27 | 28 | pulse.enable = true; 29 | 30 | jack.enable = true; 31 | }; 32 | 33 | programs.noisetorch.enable = true; 34 | 35 | environment.systemPackages = with pkgs; [ 36 | noisetorch 37 | pavucontrol 38 | playerctl 39 | pulseaudio # provide pactl to enable keyboard shortcuts 40 | ]; 41 | }; 42 | } 43 | -------------------------------------------------------------------------------- /modules/services/tandoor-recipes/default.nix: -------------------------------------------------------------------------------- 1 | # self-hosted recipe manager 2 | { config, lib, ... }: 3 | let 4 | cfg = config.my.services.tandoor-recipes; 5 | inherit (config.networking) domain; 6 | in 7 | { 8 | options.my.services.tandoor-recipes = { 9 | enable = lib.mkEnableOption "Tandoor Recipes"; 10 | }; 11 | 12 | config = lib.mkIf cfg.enable { 13 | services.tandoor-recipes = { 14 | enable = true; 15 | }; 16 | 17 | # Proxy to Tandoor-Recipes 18 | my.services.webserver.virtualHosts = [ 19 | { 20 | subdomain = "recipes"; 21 | inherit (config.services.tandoor-recipes) port; 22 | } 23 | ]; 24 | 25 | webapps.apps.tandoor-recipes = { 26 | dashboard = { 27 | name = "Recipes"; 28 | category = "app"; 29 | icon = "utensils"; 30 | url = "https://recipes.${domain}"; 31 | }; 32 | }; 33 | }; 34 | } 35 | -------------------------------------------------------------------------------- /modules/system/podman/default.nix: -------------------------------------------------------------------------------- 1 | # Podman related settings 2 | { 3 | config, 4 | lib, 5 | pkgs, 6 | ... 7 | }: 8 | let 9 | cfg = config.my.system.podman; 10 | in 11 | { 12 | options.my.system.podman = { 13 | enable = lib.mkEnableOption "podman configuration"; 14 | }; 15 | 16 | config = lib.mkIf cfg.enable { 17 | 18 | environment.systemPackages = with pkgs; [ podman-compose ]; 19 | 20 | virtualisation.podman = { 21 | enable = true; 22 | 23 | # Use fake `docker` command to redirect to `podman` 24 | # but only if docker is not enabled 25 | dockerCompat = !config.my.system.docker.enable; 26 | 27 | # Expose a docker-like socket 28 | dockerSocket.enable = true; 29 | 30 | # Allow DNS resolution in the default network 31 | defaultNetwork.settings.dns_enabled = true; 32 | 33 | autoPrune.enable = true; 34 | }; 35 | }; 36 | } 37 | -------------------------------------------------------------------------------- /modules/services/default.nix: -------------------------------------------------------------------------------- 1 | { ... }: 2 | { 3 | imports = [ 4 | ./acme 5 | ./alertmanager 6 | ./aria2 7 | ./backup 8 | ./bazarr 9 | ./blackbox 10 | ./blocky 11 | ./dyndns 12 | ./finance 13 | ./freshrss 14 | ./fritzbox 15 | ./game-stream 16 | ./git 17 | ./grafana 18 | ./hedgedoc 19 | ./home-automation 20 | ./homepage 21 | ./homer 22 | ./initrd-ssh 23 | ./jellyfin 24 | ./jellyseerr 25 | ./loki 26 | ./minecraft-server 27 | ./matrix-bot 28 | ./mumble-server 29 | ./navidrome 30 | ./nextcloud 31 | ./node-exporter 32 | ./octoprint 33 | ./paperless 34 | ./passworts 35 | ./photos 36 | ./prometheus 37 | ./promtail 38 | ./prowlarr 39 | ./radarr 40 | ./remote-build 41 | ./rss-bridge 42 | ./sonarr 43 | ./ssh-server 44 | ./tandoor-recipes 45 | ./vpn 46 | ./webserver 47 | ]; 48 | } 49 | -------------------------------------------------------------------------------- /profiles/gnome/default.nix: -------------------------------------------------------------------------------- 1 | { 2 | config, 3 | lib, 4 | pkgs, 5 | ... 6 | }: 7 | let 8 | cfg = config.my.profiles.gnome; 9 | in 10 | { 11 | options.my.profiles.gnome = with lib; { 12 | enable = mkEnableOption "gnome profile"; 13 | }; 14 | 15 | config = lib.mkIf cfg.enable { 16 | programs.gnome-disks.enable = true; 17 | 18 | xdg.mime.enable = true; 19 | 20 | # make gnome settings persistent 21 | programs.dconf.enable = true; 22 | 23 | # gnome services 24 | services = { 25 | udisks2.enable = true; 26 | dbus.packages = [ pkgs.dconf ]; 27 | udev.packages = [ pkgs.gnome-settings-daemon ]; 28 | gnome.gnome-keyring.enable = true; 29 | }; 30 | 31 | environment.systemPackages = with pkgs; [ 32 | glib 33 | adwaita-icon-theme 34 | dconf-editor 35 | eog 36 | file-roller 37 | gnome-calculator 38 | polkit_gnome 39 | xdg-utils 40 | ]; 41 | }; 42 | } 43 | -------------------------------------------------------------------------------- /modules/system/kvm/default.nix: -------------------------------------------------------------------------------- 1 | { config, lib, ... }: 2 | let 3 | cfg = config.my.system.kvm; 4 | in 5 | { 6 | options.my.system.kvm = { 7 | enable = lib.mkEnableOption "kvm configuration"; 8 | 9 | cpuFlavor = lib.mkOption { 10 | type = lib.types.nullOr ( 11 | lib.types.enum [ 12 | "intel" 13 | "amd" 14 | ] 15 | ); 16 | default = null; 17 | example = "intel"; 18 | description = "Which kind of CPU to activate kernelModules"; 19 | }; 20 | }; 21 | 22 | config = lib.mkIf cfg.enable ( 23 | lib.mkMerge [ 24 | { 25 | virtualisation.libvirtd.enable = true; 26 | 27 | programs.virt-manager.enable = true; 28 | } 29 | 30 | # Intel CPU 31 | (lib.mkIf (cfg.cpuFlavor == "intel") { boot.kernelModules = [ "kvm-intel" ]; }) 32 | 33 | # AMD CPU 34 | (lib.mkIf (cfg.cpuFlavor == "amd") { boot.kernelModules = [ "kvm-amd" ]; }) 35 | ] 36 | ); 37 | } 38 | -------------------------------------------------------------------------------- /modules/services/rss-bridge/default.nix: -------------------------------------------------------------------------------- 1 | # Get RSS feeds from websites that don't natively have one 2 | { 3 | config, 4 | lib, 5 | ... 6 | }: 7 | let 8 | cfg = config.my.services.rss-bridge; 9 | domain = "rss-bridge.${config.networking.domain}"; 10 | in 11 | { 12 | options.my.services.rss-bridge = { 13 | enable = lib.mkEnableOption "RSS-Bridge service"; 14 | }; 15 | 16 | config = lib.mkIf cfg.enable { 17 | services.rss-bridge = { 18 | enable = true; 19 | config.system.enabled_bridges = [ "*" ]; # Whitelist all 20 | virtualHost = domain; 21 | webserver = "caddy"; 22 | }; 23 | 24 | services.caddy.virtualHosts."${domain}".extraConfig = lib.mkAfter '' 25 | import common 26 | ''; 27 | 28 | webapps.apps.rss-bridge = { 29 | dashboard = { 30 | name = "RSS-Bridge"; 31 | category = "other"; 32 | icon = "rss"; 33 | url = "https://${domain}"; 34 | }; 35 | }; 36 | }; 37 | } 38 | -------------------------------------------------------------------------------- /machines/.sops.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | keys: 4 | - &admin_felix age1hf8m9upp00dr7qv2kmqdr50fpvd9ejzkfu8yknqnuda2aas2tvrs4l3u7m 5 | - &workman age1f2e644jteyeppfaatajtvjmsupl0e7nzx97ded6m0cgzw04l84ks5xl9l2 6 | - &thinkman age1spt854cdscqs757a8kazth52rv4p9udh54suw9lpzlqg5savyapq2u0c03 7 | - &serverle age14nt7qcsrye0vrpk0xcgcfmhkxwwumna39fpn83g3x0zml62skatqpnmhk4 8 | - &newton age1s9spl75rwhgm3cvvqsr9rze5m0kuxqes2tsxjmq07xg5ycn5j47s2m0dlu 9 | creation_rules: 10 | - path_regex: workman/secrets.yaml$ 11 | key_groups: 12 | - age: 13 | - *admin_felix 14 | - *workman 15 | - path_regex: thinkman/secrets.yaml$ 16 | key_groups: 17 | - age: 18 | - *admin_felix 19 | - *thinkman 20 | - path_regex: newton/secrets.yaml$ 21 | key_groups: 22 | - age: 23 | - *admin_felix 24 | - *newton 25 | - path_regex: serverle/secrets.yaml$ 26 | key_groups: 27 | - age: 28 | - *admin_felix 29 | - *serverle 30 | -------------------------------------------------------------------------------- /modules/services/dyndns/default.nix: -------------------------------------------------------------------------------- 1 | # running dyndns updates 2 | { config, lib, ... }: 3 | let 4 | cfg = config.my.services.dyndns; 5 | inherit (config.networking) domain; 6 | in 7 | { 8 | options.my.services.dyndns = { 9 | enable = lib.mkEnableOption "Dyndns"; 10 | 11 | username = lib.mkOption { 12 | type = lib.types.str; 13 | description = "Username for the dyndns."; 14 | example = "admin"; 15 | default = "Stunkymonkey-dyndns"; 16 | }; 17 | passwordFile = lib.mkOption { 18 | type = lib.types.path; 19 | description = "Password for the username for dyndns."; 20 | example = "/run/secrets/freshrss"; 21 | }; 22 | }; 23 | 24 | config = lib.mkIf cfg.enable { 25 | services.inadyn = { 26 | enable = true; 27 | settings.provider = { 28 | "default@inwx.com" = { 29 | inherit (cfg) username; 30 | include = cfg.passwordFile; 31 | hostname = "serverle.${domain}"; 32 | }; 33 | }; 34 | }; 35 | }; 36 | } 37 | -------------------------------------------------------------------------------- /modules/services/passworts/default.nix: -------------------------------------------------------------------------------- 1 | # a password-generator using the marokov model 2 | { config, lib, ... }: 3 | let 4 | cfg = config.my.services.passworts; 5 | inherit (config.networking) domain; 6 | in 7 | { 8 | options.my.services.passworts = { 9 | enable = lib.mkEnableOption "Passwords Server"; 10 | port = lib.mkOption { 11 | type = lib.types.port; 12 | default = 5010; 13 | example = 8080; 14 | description = "Internal port for webui"; 15 | }; 16 | }; 17 | 18 | config = lib.mkIf cfg.enable { 19 | services.passworts = { 20 | enable = true; 21 | inherit (cfg) port; 22 | }; 23 | 24 | my.services.webserver.virtualHosts = [ 25 | { 26 | subdomain = "passworts"; 27 | inherit (cfg) port; 28 | } 29 | ]; 30 | 31 | webapps.apps.passworts = { 32 | dashboard = { 33 | name = "Passworts"; 34 | category = "other"; 35 | icon = "lock"; 36 | url = "https://passworts.${domain}"; 37 | }; 38 | }; 39 | }; 40 | } 41 | -------------------------------------------------------------------------------- /profiles/development/default.nix: -------------------------------------------------------------------------------- 1 | { 2 | config, 3 | lib, 4 | pkgs, 5 | ... 6 | }: 7 | let 8 | cfg = config.my.profiles.development; 9 | in 10 | { 11 | options.my.profiles.development = { 12 | enable = lib.mkEnableOption "development profile"; 13 | }; 14 | 15 | config = lib.mkIf cfg.enable { 16 | environment.systemPackages = with pkgs; [ 17 | # tools 18 | cloc 19 | direnv 20 | entr 21 | ripgrep 22 | # general 23 | clang 24 | cmake 25 | gnumake 26 | meson 27 | ninja 28 | # websites 29 | hugo 30 | # scripts 31 | (python3.withPackages ( 32 | ps: with ps; [ 33 | jupyter # notebooks 34 | matplotlib 35 | numpy 36 | pandas 37 | pillow 38 | plotly 39 | scikit-learn 40 | scipy 41 | tqdm # progressbar in pandas 42 | wheel # python development 43 | ] 44 | )) 45 | # linter 46 | shellcheck 47 | typos 48 | ]; 49 | }; 50 | } 51 | -------------------------------------------------------------------------------- /modules/services/acme/default.nix: -------------------------------------------------------------------------------- 1 | # automatic certificates 2 | { config, lib, ... }: 3 | let 4 | cfg = config.my.services.acme; 5 | inherit (config.networking) domain; 6 | in 7 | { 8 | options.my.services.acme = { 9 | enable = lib.mkEnableOption "ACME certificates"; 10 | 11 | credentialsFile = lib.mkOption { 12 | type = lib.types.str; 13 | example = "/var/lib/acme/creds.env"; 14 | description = '' 15 | INWX API key file as an 'EnvironmentFile' (see `systemd.exec(5)`) 16 | ''; 17 | }; 18 | }; 19 | 20 | config = lib.mkIf cfg.enable { 21 | security.acme = { 22 | defaults.email = "server@buehler.rocks"; 23 | # this is specially needed for inwx and does not work without it 24 | defaults.dnsResolver = "ns.inwx.de"; 25 | acceptTerms = true; 26 | # Use DNS wildcard certificate 27 | certs = { 28 | "${domain}" = { 29 | extraDomainNames = [ "*.${domain}" ]; 30 | dnsProvider = "inwx"; 31 | inherit (cfg) credentialsFile; 32 | }; 33 | }; 34 | }; 35 | }; 36 | } 37 | -------------------------------------------------------------------------------- /modules/services/aria2/default.nix: -------------------------------------------------------------------------------- 1 | # to download things 2 | { 3 | config, 4 | lib, 5 | pkgs, 6 | ... 7 | }: 8 | let 9 | cfg = config.my.services.aria2; 10 | inherit (config.networking) domain; 11 | in 12 | { 13 | options.my.services.aria2 = { 14 | enable = lib.mkEnableOption "Aria2 for downloads"; 15 | 16 | downloadDir = lib.mkOption { 17 | type = lib.types.path; 18 | description = '' 19 | Directory to store downloaded files. 20 | ''; 21 | }; 22 | }; 23 | 24 | config = lib.mkIf cfg.enable { 25 | services.aria2 = { 26 | enable = true; 27 | openPorts = true; 28 | inherit (cfg) downloadDir; 29 | }; 30 | 31 | my.services.webserver.virtualHosts = [ 32 | { 33 | subdomain = "download"; 34 | root = "${pkgs.ariang}/share/ariang"; 35 | } 36 | ]; 37 | 38 | webapps.apps.aria2 = { 39 | dashboard = { 40 | name = "Download"; 41 | category = "app"; 42 | icon = "download"; 43 | url = "https://download.${domain}"; 44 | }; 45 | }; 46 | }; 47 | } 48 | -------------------------------------------------------------------------------- /modules/hardware/firmware/default.nix: -------------------------------------------------------------------------------- 1 | { config, lib, ... }: 2 | let 3 | cfg = config.my.hardware.firmware; 4 | in 5 | { 6 | options.my.hardware.firmware = { 7 | enable = lib.mkEnableOption "firmware configuration"; 8 | 9 | cpuFlavor = lib.mkOption { 10 | type = lib.types.nullOr ( 11 | lib.types.enum [ 12 | "intel" 13 | "amd" 14 | ] 15 | ); 16 | default = null; 17 | example = "intel"; 18 | description = "Which kind of CPU to activate micro-code updates"; 19 | }; 20 | }; 21 | 22 | config = lib.mkIf cfg.enable ( 23 | lib.mkMerge [ 24 | { 25 | hardware = { 26 | enableRedistributableFirmware = true; 27 | }; 28 | } 29 | 30 | # Intel CPU 31 | (lib.mkIf (cfg.cpuFlavor == "intel") { 32 | hardware = { 33 | cpu.intel.updateMicrocode = true; 34 | }; 35 | }) 36 | 37 | # AMD CPU 38 | (lib.mkIf (cfg.cpuFlavor == "amd") { 39 | hardware = { 40 | cpu.amd.updateMicrocode = true; 41 | }; 42 | }) 43 | ] 44 | ); 45 | } 46 | -------------------------------------------------------------------------------- /modules/services/octoprint/default.nix: -------------------------------------------------------------------------------- 1 | # 3d-printing software 2 | { config, lib, ... }: 3 | let 4 | cfg = config.my.services.octoprint; 5 | in 6 | { 7 | options.my.services.octoprint = { 8 | enable = lib.mkEnableOption "Octoprint Server"; 9 | 10 | plugins = lib.mkOption { 11 | type = lib.types.listOf lib.types.package; 12 | default = [ ]; 13 | defaultText = lib.literalExpression "plugins: []"; 14 | example = lib.literalExpression "plugins: with plugins; [ themeify stlviewer ]"; 15 | description = "Additional plugins to be used. Available plugins are passed through the plugins input."; 16 | }; 17 | }; 18 | 19 | config = lib.mkIf cfg.enable { 20 | services.octoprint = { 21 | enable = true; 22 | plugins = 23 | plugins: 24 | with plugins; 25 | [ 26 | costestimation 27 | displayprogress 28 | m86motorsoff 29 | stlviewer 30 | telegram 31 | titlestatus 32 | ] 33 | ++ cfg.plugins; 34 | }; 35 | networking.firewall.allowedTCPPorts = [ 5000 ]; 36 | }; 37 | } 38 | -------------------------------------------------------------------------------- /images/flake-module.nix: -------------------------------------------------------------------------------- 1 | { self, ... }: 2 | let 3 | inherit (self.inputs) nixos-generators; 4 | defaultModule = { 5 | imports = [ ./base-config.nix ]; 6 | _module.args.inputs = self.inputs; 7 | }; 8 | in 9 | { 10 | perSystem = 11 | { pkgs, ... }: 12 | { 13 | packages = { 14 | install-iso = nixos-generators.nixosGenerate { 15 | system = "x86_64-linux"; 16 | inherit pkgs; 17 | modules = [ defaultModule ]; 18 | format = "install-iso"; 19 | }; 20 | 21 | # install-sd-aarch64 = nixos-generators.nixosGenerate { 22 | # system = "aarch64-linux"; 23 | # inherit pkgs; 24 | # modules = [ 25 | # defaultModule 26 | # ]; 27 | # format = "sd-aarch64-installer"; 28 | # }; 29 | }; 30 | }; 31 | # for debugging 32 | #flake.nixosConfigurations = { 33 | # sd-image = lib.nixosSystem { 34 | # modules = [ 35 | # { 36 | # nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux"; 37 | # } 38 | # defaultModule 39 | # ]; 40 | # }; 41 | #}; 42 | } 43 | -------------------------------------------------------------------------------- /machines/serverle/wifi.nix: -------------------------------------------------------------------------------- 1 | { pkgs, ... }: 2 | { 3 | sops.secrets."wifi/bismarck" = { 4 | path = "/etc/NetworkManager/system-connections/Bismarck WLAN.nmconnection"; 5 | }; 6 | 7 | # Try fix wifi disconnect 8 | networking.networkmanager.wifi.powersave = false; 9 | networking.networkmanager.wifi.scanRandMacAddress = false; 10 | 11 | # pragmatic fix for wifi loss 12 | systemd.timers."reconnect-wifi" = { 13 | wantedBy = [ "timers.target" ]; 14 | timerConfig = { 15 | OnBootSec = "5m"; 16 | OnUnitActiveSec = "5m"; 17 | Unit = "reconnect-wifi.service"; 18 | }; 19 | }; 20 | 21 | systemd.services."reconnect-wifi" = { 22 | script = '' 23 | set +e # allow exit codes other then zero to check if online 24 | set -u 25 | 26 | ${pkgs.networkmanager}/bin/nm-online -t 10 27 | 28 | if [ $? != 0 ] 29 | then 30 | ${pkgs.coreutils}/bin/echo "reconnect wifi" 31 | ${pkgs.networkmanager}/bin/nmcli connection up 'Bismarck WLAN' 32 | fi 33 | ''; 34 | serviceConfig = { 35 | Type = "oneshot"; 36 | User = "root"; 37 | }; 38 | }; 39 | } 40 | -------------------------------------------------------------------------------- /profiles/gaming/default.nix: -------------------------------------------------------------------------------- 1 | { 2 | config, 3 | lib, 4 | pkgs, 5 | ... 6 | }: 7 | let 8 | cfg = config.my.profiles.gaming; 9 | in 10 | { 11 | options.my.profiles.gaming = with lib; { 12 | enable = mkEnableOption "gaming profile"; 13 | }; 14 | 15 | config = lib.mkIf cfg.enable { 16 | environment.systemPackages = with pkgs; [ 17 | blobby 18 | discord 19 | gamemode 20 | luanti 21 | moonlight-qt # steam-link stream 22 | openttd 23 | prismlauncher # replace minecraft 24 | SDL 25 | SDL2 26 | steam 27 | superTuxKart 28 | wine 29 | winetricks 30 | ]; 31 | 32 | programs.steam = { 33 | enable = true; 34 | # fix gamemode: https://github.com/NixOS/nixpkgs/issues/389142 35 | package = pkgs.steam.override { 36 | extraPkgs = 37 | pkgs: with pkgs; [ 38 | gamemode 39 | ]; 40 | }; 41 | }; 42 | 43 | hardware = { 44 | graphics.enable32Bit = true; 45 | graphics.extraPackages32 = with pkgs.pkgsi686Linux; [ libva ]; 46 | }; 47 | services.pulseaudio.support32Bit = true; 48 | }; 49 | } 50 | -------------------------------------------------------------------------------- /modules/hardware/action-on-low-power/default.nix: -------------------------------------------------------------------------------- 1 | # low power action 2 | { 3 | config, 4 | lib, 5 | ... 6 | }: 7 | let 8 | cfg = config.my.hardware.action-on-low-power; 9 | in 10 | { 11 | options.my.hardware.action-on-low-power = { 12 | enable = lib.mkEnableOption "action on low power"; 13 | 14 | action = lib.mkOption { 15 | type = lib.types.enum [ 16 | "hibernate" 17 | "hybrid-sleep" 18 | "poweroff" 19 | "sleep" 20 | "suspend-then-hibernate" 21 | "suspend" 22 | ]; 23 | default = "sleep"; 24 | description = '' 25 | Action to take when power is low. 26 | ''; 27 | }; 28 | 29 | powerInPercent = lib.mkOption { 30 | type = lib.types.int; 31 | default = 10; 32 | description = '' 33 | Power percentage threshold to trigger the action. 34 | ''; 35 | }; 36 | }; 37 | 38 | config = lib.mkIf cfg.enable { 39 | services.udev.extraRules = '' 40 | SUBSYSTEM=="power_supply", ATTR{status}=="Discharging", ATTR{capacity}=="${toString cfg.powerInPercent}", RUN+="${config.systemd.package}/bin/systemctl ${cfg.action}" 41 | ''; 42 | }; 43 | } 44 | -------------------------------------------------------------------------------- /profiles/nautilus/default.nix: -------------------------------------------------------------------------------- 1 | { 2 | config, 3 | lib, 4 | pkgs, 5 | ... 6 | }: 7 | let 8 | cfg = config.my.profiles.nautilus; 9 | in 10 | { 11 | options.my.profiles.nautilus = with lib; { 12 | enable = mkEnableOption "nautilus profile"; 13 | }; 14 | 15 | config = lib.mkIf cfg.enable { 16 | # make sure gnome parts are there for storing settings 17 | my.profiles.gnome.enable = true; 18 | 19 | # enable trash & network-mount 20 | services.gvfs.enable = true; 21 | 22 | services.gnome.glib-networking.enable = true; # network-mount 23 | 24 | # default-programs 25 | xdg.mime.enable = true; 26 | xdg.icons.enable = true; 27 | 28 | environment = { 29 | systemPackages = with pkgs; [ 30 | nautilus 31 | 32 | ffmpegthumbnailer # thumbnails 33 | nautilus-python # enable plugins 34 | gst_all_1.gst-libav # thumbnails 35 | nautilus-open-any-terminal # terminal-context-entry 36 | ]; 37 | 38 | pathsToLink = [ "/share/nautilus-python/extensions" ]; 39 | }; 40 | 41 | programs.nautilus-open-any-terminal = { 42 | enable = true; 43 | terminal = "foot"; 44 | }; 45 | }; 46 | } 47 | -------------------------------------------------------------------------------- /modules/services/homer/default.nix: -------------------------------------------------------------------------------- 1 | # Dashboard site 2 | { 3 | config, 4 | lib, 5 | ... 6 | }: 7 | let 8 | cfg = config.my.services.homer; 9 | inherit (config.networking) domain; 10 | 11 | homeConfig = { 12 | header = false; 13 | footer = false; 14 | columns = "auto"; 15 | services = config.lib.webapps.homerServices; 16 | }; 17 | in 18 | { 19 | imports = [ ./config.nix ]; 20 | 21 | options.my.services.homer = { 22 | enable = lib.mkEnableOption "Homer Dashboard"; 23 | }; 24 | 25 | config = lib.mkIf cfg.enable { 26 | services.homer = { 27 | enable = true; 28 | virtualHost.caddy.enable = true; 29 | virtualHost.domain = domain; 30 | settings = homeConfig; 31 | }; 32 | 33 | webapps = { 34 | dashboardCategories = [ 35 | { 36 | name = "Applications"; 37 | tag = "app"; 38 | } 39 | { 40 | name = "Media"; 41 | tag = "media"; 42 | } 43 | { 44 | name = "Infrastructure"; 45 | tag = "infra"; 46 | } 47 | { 48 | name = "Others"; 49 | tag = "other"; 50 | } 51 | ]; 52 | }; 53 | }; 54 | } 55 | -------------------------------------------------------------------------------- /machines/workman/hardware-configuration.nix: -------------------------------------------------------------------------------- 1 | { 2 | pkgs, 3 | ... 4 | }: 5 | let 6 | cpuFlavor = "amd"; 7 | in 8 | { 9 | boot.kernelPackages = pkgs.linuxPackages_latest; 10 | 11 | services.power-profiles-daemon.enable = true; 12 | services.tlp.enable = false; 13 | 14 | systemd.services.audio-off = { 15 | description = "Mute audio before suspend"; 16 | wantedBy = [ "sleep.target" ]; 17 | serviceConfig = { 18 | Type = "oneshot"; 19 | Environment = "XDG_RUNTIME_DIR=/run/user/1000"; 20 | User = "felix"; 21 | RemainAfterExit = "yes"; 22 | ExecStart = "${pkgs.pamixer}/bin/pamixer --mute"; 23 | }; 24 | }; 25 | 26 | my.hardware = { 27 | action-on-low-power.enable = true; 28 | bluetooth.enable = true; 29 | debug.enable = true; 30 | drive-monitor.enable = true; 31 | firmware = { 32 | enable = true; 33 | inherit cpuFlavor; 34 | }; 35 | graphics = { 36 | enable = true; 37 | inherit cpuFlavor; 38 | }; 39 | id-card.enable = true; 40 | keychron.enable = true; 41 | monitor.enable = true; 42 | sound.enable = true; 43 | thunderbolt.enable = true; 44 | yubikey.enable = true; 45 | }; 46 | } 47 | -------------------------------------------------------------------------------- /modules/services/game-stream/default.nix: -------------------------------------------------------------------------------- 1 | { 2 | config, 3 | lib, 4 | pkgs, 5 | ... 6 | }: 7 | let 8 | cfg = config.my.services.game-stream; 9 | in 10 | { 11 | options.my.services.game-stream = { 12 | enable = lib.mkEnableOption "game-streaming-server"; 13 | }; 14 | 15 | config = lib.mkIf cfg.enable { 16 | environment.systemPackages = [ 17 | pkgs.sunshine 18 | ]; 19 | services.udev.packages = [ pkgs.sunshine ]; 20 | networking.firewall = { 21 | enable = true; 22 | allowedTCPPorts = [ 23 | 47984 24 | 47989 25 | 47990 26 | 48010 27 | ]; 28 | allowedUDPPortRanges = [ 29 | { 30 | from = 47998; 31 | to = 48000; 32 | } 33 | { 34 | from = 8000; 35 | to = 8010; 36 | } 37 | ]; 38 | }; 39 | # Prevents this error: 40 | # Fatal: You must run [sudo setcap cap_sys_admin+p $(readlink -f sunshine)] for KMS display capture to work! 41 | security.wrappers.sunshine = { 42 | owner = "root"; 43 | group = "root"; 44 | capabilities = "cap_sys_admin+p"; 45 | source = "${pkgs.sunshine}/bin/sunshine"; 46 | }; 47 | }; 48 | } 49 | -------------------------------------------------------------------------------- /profiles/media/default.nix: -------------------------------------------------------------------------------- 1 | { 2 | config, 3 | lib, 4 | pkgs, 5 | ... 6 | }: 7 | let 8 | cfg = config.my.profiles.media; 9 | in 10 | { 11 | options.my.profiles.media = with lib; { 12 | enable = mkEnableOption "media profile"; 13 | }; 14 | 15 | config = lib.mkIf cfg.enable { 16 | environment.systemPackages = with pkgs; [ 17 | audacity # audio editing 18 | chromaprint # music-brainz fingerprint 19 | ffmpeg # general purpose 20 | gallery-dl # image donwloader 21 | handbrake # video converter 22 | image_optim # image compressors 23 | imagemagick # image converter 24 | inkscape # vector image editing 25 | mat2 # metadata-cleaning 26 | mediaelch # video sorting 27 | metadata-cleaner # mat2-gui 28 | mp3gain # audio volume 29 | mp3val # audio validation 30 | pdfgrep # grep in pdfs 31 | pdfsam-basic # pdf editing 32 | picard # music tagging 33 | projectm-sdl-cpp # visualization of music 34 | puddletag # audio tagging 35 | shotwell # photo management 36 | sonixd # cloud-music-player 37 | soundconverter # audio converter 38 | varia # download 39 | (yt-dlp.override { withAlias = true; }) # video download 40 | ]; 41 | }; 42 | } 43 | -------------------------------------------------------------------------------- /profiles/desktop-apps/default.nix: -------------------------------------------------------------------------------- 1 | { 2 | config, 3 | lib, 4 | pkgs, 5 | ... 6 | }: 7 | let 8 | cfg = config.my.profiles.desktop-apps; 9 | in 10 | { 11 | options.my.profiles.desktop-apps = { 12 | enable = lib.mkEnableOption "desktop-apps profile"; 13 | }; 14 | 15 | config = lib.mkIf cfg.enable { 16 | environment.systemPackages = with pkgs; [ 17 | firefox 18 | (gimp-with-plugins.override { 19 | plugins = with gimpPlugins; [ 20 | resynthesizer 21 | ]; 22 | }) 23 | kdePackages.ghostwriter 24 | keepassxc 25 | libreoffice 26 | (mpv.override { 27 | scripts = with mpvScripts; [ 28 | convert 29 | mpris 30 | simple-mpv-webui 31 | sponsorblock 32 | ]; 33 | }) 34 | newsflash 35 | papers 36 | rhythmbox 37 | telegram-desktop 38 | thunderbird 39 | vlc 40 | zathura 41 | zeal 42 | # terminal 43 | socat 44 | sshuttle 45 | libnotify 46 | keychain 47 | ]; 48 | 49 | programs = { 50 | wayvnc.enable = true; 51 | wireshark = { 52 | enable = true; 53 | package = pkgs.wireshark; # enable the gui 54 | }; 55 | 56 | }; 57 | }; 58 | } 59 | -------------------------------------------------------------------------------- /modules/services/ssh-server/default.nix: -------------------------------------------------------------------------------- 1 | # An SSH server, using 'mosh' 2 | { config, lib, ... }: 3 | let 4 | cfg = config.my.services.ssh-server; 5 | in 6 | { 7 | options.my.services.ssh-server = { 8 | enable = lib.mkEnableOption "SSH Server using 'mosh'"; 9 | }; 10 | 11 | config = lib.mkIf cfg.enable { 12 | services.openssh = { 13 | # Enable the OpenSSH daemon. 14 | enable = true; 15 | # Be more secure 16 | settings.PasswordAuthentication = false; 17 | }; 18 | 19 | # Opens the relevant UDP ports. 20 | programs.mosh.enable = true; 21 | 22 | # WARNING: if you remove this, then you need to assign a password to your user, otherwise 23 | # `sudo` won't work. You can do that either by using `passwd` after the first rebuild or 24 | # by setting an hashed password in the `users.users.felix` block as `initialHashedPassword`. 25 | # additionally needed for deployment 26 | security.sudo.wheelNeedsPassword = false; 27 | 28 | my.services.loki.rules = { 29 | sshd_closed = { 30 | condition = ''count_over_time({unit="sshd.service"} |~ "Connection closed by authenticating user" [15m]) > 25''; 31 | description = "More then 25 users have tried logging in the last 15 min without success"; 32 | }; 33 | }; 34 | }; 35 | } 36 | -------------------------------------------------------------------------------- /modules/hardware/graphics/default.nix: -------------------------------------------------------------------------------- 1 | { 2 | config, 3 | lib, 4 | pkgs, 5 | ... 6 | }: 7 | let 8 | cfg = config.my.hardware.graphics; 9 | in 10 | { 11 | options.my.hardware.graphics = { 12 | enable = lib.mkEnableOption "graphics configuration"; 13 | cpuFlavor = lib.mkOption { 14 | type = lib.types.nullOr ( 15 | lib.types.enum [ 16 | "amd" 17 | "intel" 18 | ] 19 | ); 20 | default = null; 21 | example = "intel"; 22 | description = "Which kind of GPU"; 23 | }; 24 | }; 25 | 26 | config = lib.mkIf cfg.enable ( 27 | lib.mkMerge [ 28 | { 29 | hardware.graphics.enable = true; 30 | } 31 | # Intel GPU 32 | (lib.mkIf (cfg.cpuFlavor == "intel") { 33 | nixpkgs.config.packageOverrides = pkgs: { 34 | intel-vaapi-driver = pkgs.intel-vaapi-driver.override { enableHybridCodec = true; }; 35 | }; 36 | hardware.graphics.extraPackages = with pkgs; [ 37 | intel-media-driver # LIBVA_DRIVER_NAME=iHD 38 | intel-vaapi-driver # LIBVA_DRIVER_NAME=i965 (older but works better for Firefox/Chromium) 39 | libva-vdpau-driver 40 | libvdpau-va-gl 41 | ]; 42 | }) 43 | 44 | (lib.mkIf (cfg.cpuFlavor == "amd") { 45 | }) 46 | ] 47 | ); 48 | } 49 | -------------------------------------------------------------------------------- /modules/system/fonts/default.nix: -------------------------------------------------------------------------------- 1 | { 2 | config, 3 | lib, 4 | pkgs, 5 | ... 6 | }: 7 | let 8 | cfg = config.my.system.fonts; 9 | in 10 | { 11 | options.my.system.fonts = { 12 | enable = lib.mkEnableOption "fonts configuration"; 13 | 14 | additionalFonts = lib.mkOption { 15 | type = lib.types.listOf lib.types.package; 16 | default = [ ]; 17 | example = "fira"; 18 | description = "Which additional fonts should be added as well"; 19 | }; 20 | }; 21 | 22 | config = lib.mkIf cfg.enable { 23 | fonts = { 24 | fontconfig.defaultFonts = { 25 | sansSerif = [ "Ubuntu" ]; 26 | monospace = [ "Ubuntu Mono" ]; 27 | }; 28 | 29 | packages = 30 | with pkgs; 31 | [ 32 | cantarell-fonts # gnome default 33 | fira 34 | fira-code # coding 35 | fira-code-symbols # ligatures 36 | fira-mono 37 | font-awesome # icons 38 | joypixels # emojis 39 | liberation_ttf # main microsoft fonts 40 | noto-fonts 41 | noto-fonts-cjk-sans 42 | noto-fonts-color-emoji 43 | ubuntu-classic 44 | unifont # unicode fallback 45 | ] 46 | ++ cfg.additionalFonts; 47 | }; 48 | nixpkgs.config.joypixels.acceptLicense = true; 49 | }; 50 | } 51 | -------------------------------------------------------------------------------- /modules/hardware/drive-monitor/default.nix: -------------------------------------------------------------------------------- 1 | { config, lib, ... }: 2 | let 3 | cfg = config.my.hardware.drive-monitor; 4 | in 5 | { 6 | options.my.hardware.drive-monitor = { 7 | enable = lib.mkEnableOption "drive-monitor configuration"; 8 | 9 | OnFailureMail = lib.mkOption { 10 | type = lib.types.nullOr lib.types.str; 11 | description = "Mail address where to send the error report"; 12 | default = null; 13 | example = "alarm@mail.com"; 14 | }; 15 | }; 16 | 17 | config = lib.mkIf cfg.enable { 18 | services = { 19 | postfix.enable = cfg.OnFailureMail != null; 20 | smartd = { 21 | enable = true; 22 | notifications.mail = lib.mkIf (cfg.OnFailureMail != null) { 23 | enable = true; 24 | recipient = cfg.OnFailureMail; 25 | }; 26 | }; 27 | }; 28 | 29 | # monitoring 30 | services.prometheus.exporters.smartctl.enable = config.services.prometheus.enable; 31 | services.prometheus.scrapeConfigs = [ 32 | { 33 | job_name = "smartctl"; 34 | static_configs = [ 35 | { 36 | targets = [ "localhost:${toString config.services.prometheus.exporters.smartctl.port}" ]; 37 | labels = { 38 | instance = config.networking.hostName; 39 | }; 40 | } 41 | ]; 42 | } 43 | ]; 44 | }; 45 | } 46 | -------------------------------------------------------------------------------- /modules/services/mumble-server/default.nix: -------------------------------------------------------------------------------- 1 | # Have a good quality voice chat 2 | { 3 | config, 4 | lib, 5 | ... 6 | }: 7 | let 8 | cfg = config.my.services.mumble-server; 9 | inherit (config.networking) domain; 10 | in 11 | { 12 | options.my.services.mumble-server = { 13 | enable = lib.mkEnableOption "mumble server service"; 14 | }; 15 | 16 | config = lib.mkIf cfg.enable { 17 | services.murmur = { 18 | enable = true; 19 | openFirewall = true; 20 | welcometext = "Welcome to the Mumble-Server!"; 21 | sslCert = "${config.security.acme.certs.${domain}.directory}/fullchain.pem"; 22 | sslKey = "${config.security.acme.certs.${domain}.directory}/key.pem"; 23 | }; 24 | 25 | # create a separate certificate for the mumble server 26 | security.acme = { 27 | certs.${domain} = { 28 | reloadServices = [ "murmur" ]; 29 | group = "caddyandmurmur"; 30 | }; 31 | }; 32 | users.groups.caddyandmurmur.members = [ 33 | "caddy" 34 | "murmur" 35 | ]; 36 | 37 | my.services = { 38 | acme.enable = true; 39 | prometheus.rules = { 40 | mumble_not_running = { 41 | condition = ''systemd_unit_state{name="murmur.service", state!="active"} > 0''; 42 | description = "{{$labels.host}} should have a running {{$labels.name}}"; 43 | }; 44 | }; 45 | }; 46 | }; 47 | } 48 | -------------------------------------------------------------------------------- /modules/system/docker/default.nix: -------------------------------------------------------------------------------- 1 | # Docker related settings 2 | { 3 | config, 4 | lib, 5 | pkgs, 6 | ... 7 | }: 8 | let 9 | cfg = config.my.system.docker; 10 | in 11 | { 12 | options.my.system.docker = { 13 | enable = lib.mkEnableOption "docker configuration"; 14 | }; 15 | 16 | config = lib.mkIf cfg.enable { 17 | environment.systemPackages = with pkgs; [ 18 | docker 19 | docker-compose 20 | ]; 21 | 22 | virtualisation.docker = { 23 | enable = true; 24 | autoPrune.enable = true; 25 | liveRestore = false; 26 | }; 27 | 28 | services = { 29 | cadvisor.enable = config.services.prometheus.enable; 30 | 31 | prometheus = { 32 | scrapeConfigs = [ 33 | { 34 | job_name = "docker"; 35 | static_configs = [ 36 | { 37 | targets = [ "localhost:${toString config.services.cadvisor.port}" ]; 38 | labels = { 39 | instance = config.networking.hostName; 40 | }; 41 | } 42 | ]; 43 | } 44 | ]; 45 | }; 46 | # dashboard untested 47 | grafana.provision = { 48 | dashboards.settings.providers = [ 49 | { 50 | name = "Docker"; 51 | options.path = pkgs.grafana-dashboards.cadvisor; 52 | disableDeletion = true; 53 | } 54 | ]; 55 | }; 56 | }; 57 | }; 58 | } 59 | -------------------------------------------------------------------------------- /profiles/desktop-dev/default.nix: -------------------------------------------------------------------------------- 1 | { 2 | config, 3 | lib, 4 | pkgs, 5 | ... 6 | }: 7 | let 8 | cfg = config.my.profiles.desktop-dev; 9 | in 10 | { 11 | options.my.profiles.desktop-dev = { 12 | enable = lib.mkEnableOption "desktop-dev profile"; 13 | }; 14 | 15 | config = lib.mkIf cfg.enable { 16 | environment.systemPackages = with pkgs; [ 17 | bruno 18 | chromium 19 | dbeaver-bin 20 | filezilla 21 | fritzing 22 | gnome-font-viewer 23 | imhex # hex editor 24 | inlyne 25 | meld 26 | qgis 27 | sqlitebrowser 28 | (vscode-with-extensions.override { 29 | vscode = vscodium; 30 | vscodeExtensions = 31 | with vscode-extensions; 32 | [ 33 | bbenoist.nix 34 | editorconfig.editorconfig 35 | github.copilot 36 | mkhl.direnv 37 | ms-azuretools.vscode-docker 38 | ms-python.python 39 | ms-vscode-remote.remote-ssh 40 | pkief.material-icon-theme 41 | hiukky.flate 42 | ] 43 | ++ pkgs.vscode-utils.extensionsFromVscodeMarketplace [ 44 | # { 45 | # name = "vsc-material-theme"; 46 | # publisher = "Equinusocio"; 47 | # version = "33.8.0"; 48 | # sha256 = "sha256-+I4AUwsrElT62XNvmuAC2iBfHfjNYY0bmAqzQvfwUYM="; 49 | # } 50 | ]; 51 | }) 52 | ]; 53 | }; 54 | } 55 | -------------------------------------------------------------------------------- /machines/core/users.nix: -------------------------------------------------------------------------------- 1 | { config, ... }: 2 | { 3 | sops.secrets."users/felix/password".neededForUsers = true; 4 | sops.secrets."users/felix/password" = { }; 5 | 6 | users.users.felix = { 7 | isNormalUser = true; 8 | home = "/home/felix"; 9 | group = "felix"; 10 | extraGroups = [ 11 | "adbusers" # adb control 12 | "audio" # sound control 13 | "cdrom" # emulate cds 14 | "dialout" # serial-console 15 | "docker" # usage of `docker` socket 16 | "input" # mouse control 17 | "libvirtd" # kvm control 18 | "networkmanager" # wireless configuration 19 | "podman" # usage of `podman` socket 20 | "seat" # access to input devices 21 | "video" # screen control 22 | "wheel" # `sudo` for the user. 23 | ]; 24 | hashedPasswordFile = config.sops.secrets."users/felix/password".path; 25 | openssh.authorizedKeys.keys = [ 26 | "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOFx6OLwL9MbkD3mnMsv+xrzZHN/rwCTgVs758SCLG0h felix@workman" 27 | "no-touch-required sk-ssh-ed25519@openssh.com AAAAGnNrLXNzaC1lZDI1NTE5QG9wZW5zc2guY29tAAAAIHhjrfqyOS+M9ATSTVr9JXPERBXOow/ZmkWICjbtbEgXAAAAFHNzaDpmZWxpeC1wZXJzb25hbC0x ssh:felix-personal-1" 28 | "no-touch-required sk-ssh-ed25519@openssh.com AAAAGnNrLXNzaC1lZDI1NTE5QG9wZW5zc2guY29tAAAAIMHExVOrEevQ+bwrrW3cXCO7Y/SyA+7wG+b6ZvAWY4MJAAAAFHNzaDpmZWxpeC1wZXJzb25hbC0y ssh:felix-personal-2" 29 | ]; 30 | }; 31 | 32 | users.groups.felix = { 33 | gid = 1000; 34 | }; 35 | } 36 | -------------------------------------------------------------------------------- /modules/services/finance/default.nix: -------------------------------------------------------------------------------- 1 | # finance overview 2 | { 3 | config, 4 | lib, 5 | ... 6 | }: 7 | let 8 | cfg = config.my.services.finance; 9 | inherit (config.networking) domain; 10 | in 11 | { 12 | options.my.services.finance = { 13 | enable = lib.mkEnableOption "Finance firefly service."; 14 | 15 | appKeyFile = lib.mkOption { 16 | type = lib.types.path; 17 | description = "appkey for the service."; 18 | example = "/run/secrets/freshrss"; 19 | default = "base64:ICs6jizTJnu4U8Sl/+GKIUC6TSK+0i+Lu84CicRhTNE="; 20 | }; 21 | }; 22 | 23 | config = lib.mkIf cfg.enable { 24 | services.firefly-iii = { 25 | enable = true; 26 | virtualHost = "finance"; 27 | user = "caddy"; 28 | group = "caddy"; 29 | settings = { 30 | APP_KEY_FILE = cfg.appKeyFile; 31 | SITE_OWNER = "server@buehler.rocks"; 32 | }; 33 | }; 34 | 35 | my.services.webserver.virtualHosts = [ 36 | { 37 | subdomain = "finance"; 38 | extraConfig = '' 39 | file_server 40 | root * "${config.services.firefly-iii.package}/public" 41 | php_fastcgi unix/${config.services.phpfpm.pools."firefly-iii".socket} { 42 | env modHeadersAvailable true 43 | } 44 | ''; 45 | } 46 | ]; 47 | 48 | webapps.apps.finance = { 49 | dashboard = { 50 | name = "Finance"; 51 | category = "app"; 52 | icon = "coins"; 53 | url = "https://finance.${domain}"; 54 | }; 55 | }; 56 | }; 57 | } 58 | -------------------------------------------------------------------------------- /machines/newton/network.nix: -------------------------------------------------------------------------------- 1 | { config, ... }: 2 | 3 | let 4 | ifname = "ens18"; 5 | 6 | ip4_addr = "38.242.193.132"; 7 | ip4_mask = "255.255.240.0"; 8 | ip4_mask_len = 20; 9 | 10 | ip4_gw = "38.242.192.1"; 11 | ip4_dns = [ 12 | "8.8.8.8" 13 | "79.143.182.242" 14 | "178.238.234.231" 15 | "5.189.191.29" 16 | ]; 17 | 18 | ip6_addr = "2a02:c206:3009:3317::1"; 19 | ip6_mask_len = 64; 20 | 21 | ip6_gw = "fe80::1"; 22 | ip6_dns = [ 23 | "2a02:c205:0:0882::1" 24 | "2a02:c205:0:0891::1" 25 | "2a02:c207:0:0842::1" 26 | ]; 27 | in 28 | { 29 | # kernel parameters are needed for initrd 30 | boot.kernelParams = [ 31 | "ip=${ip4_addr}::${ip4_gw}:${ip4_mask}:${config.networking.hostName}:${ifname}:off" 32 | ]; 33 | networking = { 34 | nameservers = ip4_dns ++ ip6_dns; 35 | domain = "buehler.rocks"; 36 | search = [ "buehler.rocks" ]; 37 | 38 | defaultGateway = { 39 | address = ip4_gw; 40 | interface = ifname; 41 | }; 42 | 43 | defaultGateway6 = { 44 | address = ip6_gw; 45 | interface = ifname; 46 | }; 47 | 48 | interfaces."${ifname}" = { 49 | ipv4.addresses = [ 50 | { 51 | address = ip4_addr; 52 | prefixLength = ip4_mask_len; 53 | } 54 | ]; 55 | ipv6.addresses = [ 56 | { 57 | address = ip6_addr; 58 | prefixLength = ip6_mask_len; 59 | } 60 | ]; 61 | # Do not use the temporary addresses on this interface 62 | # The machine is rather a server 63 | tempAddress = "disabled"; 64 | }; 65 | }; 66 | } 67 | -------------------------------------------------------------------------------- /modules/services/paperless/default.nix: -------------------------------------------------------------------------------- 1 | # document management system 2 | { config, lib, ... }: 3 | let 4 | cfg = config.my.services.paperless; 5 | inherit (config.networking) domain; 6 | in 7 | { 8 | options.my.services.paperless = { 9 | enable = lib.mkEnableOption "Paperless Server"; 10 | 11 | passwordFile = lib.mkOption { 12 | type = lib.types.path; 13 | description = "Password for the defaultUser for FreshRSS."; 14 | example = "/run/secrets/freshrss"; 15 | }; 16 | 17 | mediaDir = lib.mkOption { 18 | type = lib.types.path; 19 | description = "Location of the FreshRSS data."; 20 | example = "/data/docs"; 21 | }; 22 | 23 | settings = lib.mkOption { 24 | type = lib.types.attrs; 25 | default = { }; 26 | description = "additional extraConfig"; 27 | }; 28 | }; 29 | 30 | config = lib.mkIf cfg.enable { 31 | services.paperless = { 32 | enable = true; 33 | inherit (cfg) mediaDir passwordFile; 34 | settings = { 35 | PAPERLESS_OCR_LANGUAGE = "deu+eng"; 36 | } 37 | // cfg.settings; 38 | }; 39 | 40 | # monitoring is not really useful, because it only contains the http-worker infos -> skipped for now 41 | 42 | my.services.webserver.virtualHosts = [ 43 | { 44 | subdomain = "docs"; 45 | inherit (config.services.paperless) port; 46 | } 47 | ]; 48 | 49 | webapps.apps.paperless = { 50 | dashboard = { 51 | name = "Documents"; 52 | category = "media"; 53 | icon = "book"; 54 | url = "https://docs.${domain}"; 55 | }; 56 | }; 57 | }; 58 | } 59 | -------------------------------------------------------------------------------- /modules/services/bazarr/default.nix: -------------------------------------------------------------------------------- 1 | # manages and downloads subtitles 2 | { config, lib, ... }: 3 | let 4 | cfg = config.my.services.bazarr; 5 | inherit (config.networking) domain; 6 | port = config.services.bazarr.listenPort; 7 | in 8 | { 9 | options.my.services.bazarr = { 10 | enable = lib.mkEnableOption "Bazarr for subtitle management"; 11 | 12 | apiKeyFile = lib.mkOption { 13 | type = lib.types.path; 14 | description = '' 15 | File containing the api-key. 16 | ''; 17 | }; 18 | }; 19 | 20 | config = lib.mkIf cfg.enable { 21 | services = { 22 | bazarr = { 23 | enable = true; 24 | }; 25 | prometheus.exporters.exportarr-bazarr = { 26 | inherit (config.services.prometheus) enable; 27 | port = port + 1; 28 | url = "http://localhost:${toString port}"; 29 | inherit (cfg) apiKeyFile; 30 | }; 31 | prometheus.scrapeConfigs = [ 32 | { 33 | job_name = "bazarr"; 34 | static_configs = [ 35 | { 36 | targets = [ "localhost:${toString port + 1}" ]; 37 | labels = { 38 | instance = config.networking.hostName; 39 | }; 40 | } 41 | ]; 42 | } 43 | ]; 44 | }; 45 | 46 | my.services.webserver.virtualHosts = [ 47 | { 48 | subdomain = "subtitles"; 49 | inherit port; 50 | } 51 | ]; 52 | 53 | webapps.apps.bazarr = { 54 | dashboard = { 55 | name = "Subtitles"; 56 | category = "app"; 57 | icon = "closed-captioning"; 58 | url = "https://subtitles.${domain}"; 59 | }; 60 | }; 61 | }; 62 | } 63 | -------------------------------------------------------------------------------- /modules/services/prowlarr/default.nix: -------------------------------------------------------------------------------- 1 | # manages indexes 2 | { config, lib, ... }: 3 | let 4 | cfg = config.my.services.prowlarr; 5 | inherit (config.networking) domain; 6 | port = 9696; 7 | in 8 | { 9 | options.my.services.prowlarr = { 10 | enable = lib.mkEnableOption "Prowlarr for indexing"; 11 | 12 | apiKeyFile = lib.mkOption { 13 | type = lib.types.path; 14 | description = '' 15 | File containing the api-key. 16 | ''; 17 | }; 18 | }; 19 | 20 | config = lib.mkIf cfg.enable { 21 | services = { 22 | prowlarr = { 23 | enable = true; 24 | settings.server.port = port; 25 | }; 26 | prometheus.exporters.exportarr-prowlarr = { 27 | inherit (config.services.prometheus) enable; 28 | port = port + 1; 29 | url = "http://localhost:${toString port}"; 30 | inherit (cfg) apiKeyFile; 31 | }; 32 | prometheus.scrapeConfigs = [ 33 | { 34 | job_name = "prowlarr"; 35 | static_configs = [ 36 | { 37 | targets = [ "localhost:${toString port + 1}" ]; 38 | labels = { 39 | instance = config.networking.hostName; 40 | }; 41 | } 42 | ]; 43 | } 44 | ]; 45 | }; 46 | 47 | my.services.webserver.virtualHosts = [ 48 | { 49 | subdomain = "indexer"; 50 | inherit port; 51 | } 52 | ]; 53 | 54 | webapps.apps.prowlarr = { 55 | dashboard = { 56 | name = "Indexer"; 57 | category = "app"; 58 | icon = "sync-alt"; 59 | url = "https://indexer.${domain}"; 60 | method = "get"; 61 | }; 62 | }; 63 | }; 64 | } 65 | -------------------------------------------------------------------------------- /modules/services/radarr/default.nix: -------------------------------------------------------------------------------- 1 | # manages and downloads films 2 | { config, lib, ... }: 3 | let 4 | cfg = config.my.services.radarr; 5 | inherit (config.networking) domain; 6 | port = 7878; 7 | in 8 | { 9 | options.my.services.radarr = { 10 | enable = lib.mkEnableOption "Radarr for film management"; 11 | 12 | apiKeyFile = lib.mkOption { 13 | type = lib.types.path; 14 | description = '' 15 | File containing the api-key. 16 | ''; 17 | }; 18 | }; 19 | 20 | config = lib.mkIf cfg.enable { 21 | services = { 22 | radarr = { 23 | enable = true; 24 | settings.server.port = port; 25 | }; 26 | prometheus.exporters.exportarr-radarr = { 27 | inherit (config.services.prometheus) enable; 28 | port = port + 1; 29 | url = "http://localhost:${toString port}"; 30 | inherit (cfg) apiKeyFile; 31 | }; 32 | prometheus.scrapeConfigs = [ 33 | { 34 | job_name = "radarr"; 35 | static_configs = [ 36 | { 37 | targets = [ "localhost:${toString port + 1}" ]; 38 | labels = { 39 | instance = config.networking.hostName; 40 | }; 41 | } 42 | ]; 43 | } 44 | ]; 45 | }; 46 | 47 | my.services.webserver.virtualHosts = [ 48 | { 49 | subdomain = "movies"; 50 | inherit port; 51 | } 52 | ]; 53 | 54 | webapps.apps.radarr = { 55 | dashboard = { 56 | name = "Movies"; 57 | category = "media"; 58 | icon = "film"; 59 | url = "https://movies.${domain}"; 60 | method = "get"; 61 | }; 62 | }; 63 | }; 64 | } 65 | -------------------------------------------------------------------------------- /modules/services/fritzbox/default.nix: -------------------------------------------------------------------------------- 1 | { 2 | config, 3 | lib, 4 | pkgs, 5 | ... 6 | }: 7 | let 8 | cfg = config.my.services.fritzbox; 9 | in 10 | { 11 | options.my.services.fritzbox = { 12 | enable = lib.mkEnableOption "Fritzbox-Monitoring"; 13 | 14 | username = lib.mkOption { 15 | type = lib.types.str; 16 | default = "prometheus"; 17 | example = "admin"; 18 | description = "Admin username"; 19 | }; 20 | 21 | passwordFile = lib.mkOption { 22 | type = lib.types.str; 23 | example = "/var/lib/fritz/password.txt"; 24 | description = "password stored in a file"; 25 | }; 26 | }; 27 | 28 | config = lib.mkIf cfg.enable { 29 | services = { 30 | prometheus.exporters.fritz = { 31 | inherit (cfg) enable; 32 | settings.devices = [ 33 | { 34 | inherit (cfg) username; 35 | password_file = cfg.passwordFile; 36 | host_info = true; 37 | } 38 | ]; 39 | }; 40 | prometheus.scrapeConfigs = [ 41 | { 42 | job_name = "fritzbox"; 43 | static_configs = [ 44 | { 45 | targets = [ "localhost:${toString config.services.prometheus.exporters.fritzbox.port}" ]; 46 | labels = { 47 | instance = config.networking.hostName; 48 | }; 49 | } 50 | ]; 51 | } 52 | ]; 53 | grafana.provision = { 54 | dashboards.settings.providers = [ 55 | { 56 | name = "Fritzbox"; 57 | options.path = pkgs.grafana-dashboards.fritzbox; 58 | disableDeletion = true; 59 | } 60 | ]; 61 | }; 62 | }; 63 | }; 64 | } 65 | -------------------------------------------------------------------------------- /modules/services/freshrss/default.nix: -------------------------------------------------------------------------------- 1 | # RSS aggregator and reader 2 | { 3 | config, 4 | lib, 5 | pkgs, 6 | ... 7 | }: 8 | let 9 | cfg = config.my.services.freshrss; 10 | inherit (config.networking) domain; 11 | in 12 | { 13 | options.my.services.freshrss = { 14 | enable = lib.mkEnableOption "FreshRSS feed reader"; 15 | 16 | package = lib.mkOption { 17 | type = lib.types.package; 18 | default = pkgs.freshrss; 19 | description = "Which FreshRSS package to use."; 20 | }; 21 | 22 | defaultUser = lib.mkOption { 23 | type = lib.types.str; 24 | default = "admin"; 25 | description = "Default username for FreshRSS."; 26 | example = "eva"; 27 | }; 28 | 29 | passwordFile = lib.mkOption { 30 | type = lib.types.path; 31 | description = "Password for the defaultUser for FreshRSS."; 32 | example = "/run/secrets/freshrss"; 33 | }; 34 | 35 | language = lib.mkOption { 36 | type = lib.types.str; 37 | default = "en"; 38 | description = "Default language for FreshRSS."; 39 | example = "de"; 40 | }; 41 | }; 42 | 43 | config = lib.mkIf cfg.enable { 44 | services.freshrss = { 45 | enable = true; 46 | baseUrl = "https://news.${domain}"; 47 | inherit (cfg) language passwordFile defaultUser; 48 | virtualHost = "news.${domain}"; 49 | webserver = "caddy"; 50 | }; 51 | 52 | services.caddy.virtualHosts."news.${domain}".extraConfig = lib.mkAfter '' 53 | import common 54 | ''; 55 | 56 | webapps.apps.freshrss = { 57 | dashboard = { 58 | name = "News"; 59 | category = "app"; 60 | icon = "newspaper"; 61 | url = "https://news.${domain}"; 62 | }; 63 | }; 64 | }; 65 | } 66 | -------------------------------------------------------------------------------- /modules/services/matrix-bot/default.nix: -------------------------------------------------------------------------------- 1 | { 2 | config, 3 | lib, 4 | pkgs, 5 | ... 6 | }: 7 | let 8 | cfg = config.my.services.matrix-bot; 9 | in 10 | { 11 | options.my.services.matrix-bot = { 12 | enable = lib.mkEnableOption "enable matrix forwarding bot"; 13 | Username = lib.mkOption { 14 | type = lib.types.str; 15 | description = "Matrix bot name."; 16 | example = "@bot:matrix.org"; 17 | default = "@stunkymonkey-bot:matrix.org"; 18 | }; 19 | PasswortFile = lib.mkOption { 20 | type = lib.types.path; 21 | description = '' 22 | Password for the bot. 23 | format: MX_TOKEN= 24 | ''; 25 | example = "/run/secrets/password"; 26 | }; 27 | RoomID = lib.mkOption { 28 | type = lib.types.str; 29 | description = "Matrix room id."; 30 | example = "!abcdefghijklmnopqr:matrix.org"; 31 | default = "!ZWnKiKLuQNBkBGMPCl:matrix.org"; 32 | }; 33 | }; 34 | 35 | config = lib.mkIf cfg.enable { 36 | systemd.services.matrix-hook = { 37 | description = "Matrix Hook"; 38 | after = [ "network.target" ]; 39 | wantedBy = [ "multi-user.target" ]; 40 | environment = { 41 | HTTP_ADDRESS = "[::1]"; 42 | HTTP_PORT = "4050"; 43 | MX_HOMESERVER = "https://matrix.org"; 44 | MX_ID = cfg.Username; 45 | MX_ROOMID = cfg.RoomID; 46 | MX_MSG_TEMPLATE = "${pkgs.matrix-hook}/message.html.tmpl"; 47 | }; 48 | serviceConfig = { 49 | EnvironmentFile = [ cfg.PasswortFile ]; 50 | Type = "simple"; 51 | ExecStart = lib.getExe pkgs.matrix-hook; 52 | Restart = "always"; 53 | RestartSec = "10"; 54 | DynamicUser = true; 55 | User = "matrix-hook"; 56 | Group = "matrix-hook"; 57 | }; 58 | }; 59 | }; 60 | } 61 | -------------------------------------------------------------------------------- /modules/services/jellyfin/default.nix: -------------------------------------------------------------------------------- 1 | # The Free Software Media System 2 | { 3 | config, 4 | lib, 5 | pkgs, 6 | ... 7 | }: 8 | let 9 | cfg = config.my.services.jellyfin; 10 | inherit (config.networking) domain; 11 | # enable monitoring 12 | jellyfin-with-metrics = pkgs.jellyfin.overrideAttrs (attrs: { 13 | patches = 14 | let 15 | existingPatches = if attrs ? patches && builtins.isList attrs.patches then attrs.patches else [ ]; 16 | in 17 | # with this patch the default setting for metrics is changed 18 | existingPatches ++ [ ./enable-metrics.patch ]; 19 | }); 20 | in 21 | { 22 | options.my.services.jellyfin = { 23 | enable = lib.mkEnableOption "Jellyfin Media Server"; 24 | }; 25 | 26 | config = lib.mkIf cfg.enable { 27 | services.jellyfin = { 28 | enable = true; 29 | package = jellyfin-with-metrics; 30 | }; 31 | 32 | services.prometheus = { 33 | scrapeConfigs = [ 34 | { 35 | job_name = "jellyfin"; 36 | static_configs = [ 37 | { 38 | targets = [ "localhost:${toString cfg.port}" ]; 39 | labels = { 40 | instance = config.networking.hostName; 41 | }; 42 | } 43 | ]; 44 | } 45 | ]; 46 | }; 47 | # sadly the metrics do not contain application specific metrics, only c# -> no dashboard 48 | 49 | my.services.webserver.virtualHosts = [ 50 | { 51 | subdomain = "media"; 52 | # jellyfin does not allow modification 53 | port = 8096; 54 | } 55 | ]; 56 | 57 | webapps.apps.jellyfin = { 58 | dashboard = { 59 | name = "Media"; 60 | category = "media"; 61 | icon = "eye"; 62 | url = "https://media.${domain}"; 63 | }; 64 | }; 65 | }; 66 | } 67 | -------------------------------------------------------------------------------- /modules/services/sonarr/default.nix: -------------------------------------------------------------------------------- 1 | # manages and downloads series 2 | { config, lib, ... }: 3 | let 4 | cfg = config.my.services.sonarr; 5 | inherit (config.networking) domain; 6 | port = 8989; 7 | in 8 | { 9 | options.my.services.sonarr = { 10 | enable = lib.mkEnableOption "Sonarr for series management"; 11 | 12 | apiKeyFile = lib.mkOption { 13 | type = lib.types.path; 14 | description = '' 15 | File containing the api-key. 16 | ''; 17 | }; 18 | }; 19 | 20 | config = lib.mkIf cfg.enable { 21 | # TODO: remove when sonarr is updated to 5.x 22 | nixpkgs.config.permittedInsecurePackages = [ 23 | "dotnet-sdk-6.0.428" 24 | "aspnetcore-runtime-6.0.36" 25 | ]; 26 | 27 | services = { 28 | sonarr = { 29 | enable = true; 30 | settings.server.port = port; 31 | }; 32 | prometheus.exporters.exportarr-sonarr = { 33 | inherit (config.services.prometheus) enable; 34 | port = port + 1; 35 | url = "http://localhost:${toString port}"; 36 | inherit (cfg) apiKeyFile; 37 | }; 38 | prometheus.scrapeConfigs = [ 39 | { 40 | job_name = "sonarr"; 41 | static_configs = [ 42 | { 43 | targets = [ "localhost:${toString port + 1}" ]; 44 | labels = { 45 | instance = config.networking.hostName; 46 | }; 47 | } 48 | ]; 49 | } 50 | ]; 51 | }; 52 | 53 | my.services.webserver.virtualHosts = [ 54 | { 55 | subdomain = "series"; 56 | inherit port; 57 | } 58 | ]; 59 | 60 | webapps.apps.sonarr = { 61 | dashboard = { 62 | name = "Series"; 63 | category = "media"; 64 | icon = "tv"; 65 | url = "https://series.${domain}"; 66 | method = "get"; 67 | }; 68 | }; 69 | }; 70 | } 71 | -------------------------------------------------------------------------------- /machines/thinkman/disko-config.nix: -------------------------------------------------------------------------------- 1 | { 2 | disko.devices = { 3 | disk = { 4 | vdb = { 5 | type = "disk"; 6 | device = "/dev/disk/by-id/nvme-eui.0025385b01410682"; 7 | content = { 8 | type = "gpt"; 9 | partitions = { 10 | boot = { 11 | size = "1M"; 12 | type = "EF02"; # for grub MBR 13 | }; 14 | ESP = { 15 | size = "512M"; 16 | type = "EF00"; 17 | content = { 18 | type = "filesystem"; 19 | format = "vfat"; 20 | mountpoint = "/boot"; 21 | mountOptions = [ "defaults" ]; 22 | }; 23 | }; 24 | luks = { 25 | size = "100%"; 26 | content = { 27 | type = "luks"; 28 | name = "encrypted"; 29 | settings.allowDiscards = true; 30 | passwordFile = "/tmp/disk.key"; 31 | content = { 32 | type = "lvm_pv"; 33 | vg = "pool"; 34 | }; 35 | }; 36 | }; 37 | }; 38 | }; 39 | }; 40 | }; 41 | lvm_vg = { 42 | pool = { 43 | type = "lvm_vg"; 44 | lvs = { 45 | root = { 46 | size = "100G"; 47 | content = { 48 | type = "filesystem"; 49 | format = "ext4"; 50 | mountpoint = "/"; 51 | }; 52 | }; 53 | home = { 54 | size = "450G"; 55 | content = { 56 | type = "filesystem"; 57 | format = "ext4"; 58 | mountpoint = "/home"; 59 | }; 60 | }; 61 | swap = { 62 | size = "32G"; 63 | content = { 64 | type = "swap"; 65 | resumeDevice = true; 66 | }; 67 | }; 68 | }; 69 | }; 70 | }; 71 | }; 72 | } 73 | -------------------------------------------------------------------------------- /machines/workman/disko-config.nix: -------------------------------------------------------------------------------- 1 | { 2 | disko.devices = { 3 | disk = { 4 | vdb = { 5 | type = "disk"; 6 | device = "/dev/disk/by-id/nvme-eui.e8238fa6bf530001001b444a456de595"; 7 | content = { 8 | type = "gpt"; 9 | partitions = { 10 | boot = { 11 | size = "1M"; 12 | type = "EF02"; # for grub MBR 13 | }; 14 | ESP = { 15 | size = "512M"; 16 | type = "EF00"; 17 | content = { 18 | type = "filesystem"; 19 | format = "vfat"; 20 | mountpoint = "/boot"; 21 | mountOptions = [ "defaults" ]; 22 | }; 23 | }; 24 | luks = { 25 | size = "100%"; 26 | content = { 27 | type = "luks"; 28 | name = "encrypted"; 29 | settings.allowDiscards = true; 30 | passwordFile = "/tmp/disk.key"; 31 | content = { 32 | type = "lvm_pv"; 33 | vg = "pool"; 34 | }; 35 | }; 36 | }; 37 | }; 38 | }; 39 | }; 40 | }; 41 | lvm_vg = { 42 | pool = { 43 | type = "lvm_vg"; 44 | lvs = { 45 | root = { 46 | size = "100G"; 47 | content = { 48 | type = "filesystem"; 49 | format = "ext4"; 50 | mountpoint = "/"; 51 | }; 52 | }; 53 | home = { 54 | size = "500G"; 55 | content = { 56 | type = "filesystem"; 57 | format = "ext4"; 58 | mountpoint = "/home"; 59 | }; 60 | }; 61 | swap = { 62 | size = "64G"; 63 | content = { 64 | type = "swap"; 65 | resumeDevice = true; 66 | }; 67 | }; 68 | }; 69 | }; 70 | }; 71 | }; 72 | } 73 | -------------------------------------------------------------------------------- /machines/newton/disko-config.nix: -------------------------------------------------------------------------------- 1 | { 2 | disko.devices = { 3 | disk = { 4 | vdb = { 5 | type = "disk"; 6 | device = "/dev/disk/by-id/scsi-0QEMU_QEMU_HARDDISK_drive-scsi0"; 7 | content = { 8 | type = "gpt"; 9 | partitions = { 10 | boot = { 11 | size = "1M"; 12 | type = "EF02"; # for grub MBR 13 | }; 14 | ESP = { 15 | size = "512M"; 16 | type = "EF00"; 17 | content = { 18 | type = "filesystem"; 19 | format = "vfat"; 20 | mountpoint = "/boot"; 21 | mountOptions = [ "defaults" ]; 22 | }; 23 | }; 24 | luks = { 25 | size = "100%"; 26 | content = { 27 | type = "luks"; 28 | name = "encrypted"; 29 | settings.allowDiscards = true; 30 | passwordFile = "/tmp/disk.key"; 31 | content = { 32 | type = "lvm_pv"; 33 | vg = "pool"; 34 | }; 35 | }; 36 | }; 37 | }; 38 | }; 39 | }; 40 | }; 41 | lvm_vg = { 42 | pool = { 43 | type = "lvm_vg"; 44 | lvs = { 45 | root = { 46 | size = "150G"; 47 | content = { 48 | type = "filesystem"; 49 | format = "ext4"; 50 | mountpoint = "/"; 51 | }; 52 | }; 53 | data = { 54 | size = "350G"; 55 | content = { 56 | type = "filesystem"; 57 | format = "ext4"; 58 | mountpoint = "/data"; 59 | }; 60 | }; 61 | swap = { 62 | size = "4G"; 63 | content = { 64 | type = "swap"; 65 | randomEncryption = true; 66 | resumeDevice = true; 67 | }; 68 | }; 69 | }; 70 | }; 71 | }; 72 | }; 73 | } 74 | -------------------------------------------------------------------------------- /machines/serverle/disko-config.nix: -------------------------------------------------------------------------------- 1 | { 2 | disko.devices = { 3 | disk = { 4 | vdb = { 5 | type = "disk"; 6 | device = "/dev/disk/by-id/usb-Seagate_Expansion_2HC015KJ-0:0"; 7 | content = { 8 | type = "gpt"; 9 | partitions = { 10 | boot = { 11 | size = "1M"; 12 | type = "EF02"; # for grub MBR 13 | }; 14 | ESP = { 15 | size = "512M"; 16 | type = "EF00"; 17 | content = { 18 | type = "filesystem"; 19 | format = "vfat"; 20 | mountpoint = "/boot"; 21 | mountOptions = [ "defaults" ]; 22 | }; 23 | }; 24 | luks = { 25 | size = "100%"; 26 | content = { 27 | type = "luks"; 28 | name = "encrypted"; 29 | settings.allowDiscards = true; 30 | passwordFile = "/tmp/disk.key"; 31 | content = { 32 | type = "lvm_pv"; 33 | vg = "pool"; 34 | }; 35 | }; 36 | }; 37 | }; 38 | }; 39 | }; 40 | }; 41 | lvm_vg = { 42 | pool = { 43 | type = "lvm_vg"; 44 | lvs = { 45 | root = { 46 | size = "50G"; 47 | content = { 48 | type = "filesystem"; 49 | format = "ext4"; 50 | mountpoint = "/"; 51 | }; 52 | }; 53 | data = { 54 | size = "400G"; 55 | content = { 56 | type = "filesystem"; 57 | format = "ext4"; 58 | mountpoint = "/data"; 59 | }; 60 | }; 61 | swap = { 62 | size = "4G"; 63 | content = { 64 | type = "swap"; 65 | randomEncryption = true; 66 | resumeDevice = true; 67 | }; 68 | }; 69 | }; 70 | }; 71 | }; 72 | }; 73 | } 74 | -------------------------------------------------------------------------------- /profiles/sway/default.nix: -------------------------------------------------------------------------------- 1 | { 2 | config, 3 | lib, 4 | pkgs, 5 | ... 6 | }: 7 | let 8 | cfg = config.my.profiles.sway; 9 | in 10 | { 11 | imports = [ 12 | ./autostart.nix 13 | ./location.nix 14 | ./screen-sharing.nix 15 | ./theme.nix 16 | ]; 17 | 18 | options.my.profiles.sway = with lib; { 19 | enable = mkEnableOption "sway profile"; 20 | }; 21 | 22 | config = lib.mkIf cfg.enable { 23 | 24 | my.profiles = { 25 | sway-autostart.enable = true; 26 | sway-location.enable = true; 27 | sway-screen-sharing.enable = true; 28 | sway-theme.enable = true; 29 | }; 30 | 31 | environment.systemPackages = with pkgs; [ polkit_gnome ]; 32 | environment.pathsToLink = [ "/libexec" ]; 33 | 34 | services.seatd.enable = true; 35 | 36 | programs = { 37 | foot.enable = true; 38 | light.enable = true; 39 | wshowkeys.enable = true; 40 | 41 | sway = { 42 | enable = true; 43 | wrapperFeatures = { 44 | gtk = true; 45 | base = true; 46 | }; 47 | 48 | extraPackages = with pkgs; [ 49 | brightnessctl 50 | dmenu 51 | gammastep 52 | grim 53 | i3status-rust 54 | mako 55 | slurp 56 | swayidle 57 | swaylock 58 | wdisplays 59 | wezterm 60 | wf-recorder 61 | wl-clipboard 62 | wofi 63 | xwayland 64 | ]; 65 | 66 | extraSessionCommands = '' 67 | export XDG_SESSION_TYPE=wayland 68 | export XDG_CURRENT_DESKTOP=sway 69 | export SDL_VIDEODRIVER=wayland 70 | export QT_QPA_PLATFORM=wayland 71 | export QT_WAYLAND_DISABLE_WINDOWDECORATION="1" 72 | export _JAVA_AWT_WM_NONREPARENTING=1 73 | export CLUTTER_BACKEND=wayland 74 | export SAL_USE_VCLPLUGIN=gtk3 75 | export MOZ_ENABLE_WAYLAND=1 76 | export MOZ_USE_XINPUT2=1 77 | export NIXOS_OZONE_WL=1 78 | ''; 79 | }; 80 | }; 81 | }; 82 | } 83 | -------------------------------------------------------------------------------- /modules/services/blocky/default.nix: -------------------------------------------------------------------------------- 1 | # Fast and lightweight DNS proxy as ad-blocker for local network 2 | { 3 | config, 4 | lib, 5 | pkgs, 6 | ... 7 | }: 8 | let 9 | cfg = config.my.services.blocky; 10 | in 11 | { 12 | options.my.services.blocky = { 13 | enable = lib.mkEnableOption "Blocky DNS Server"; 14 | 15 | httpPort = lib.mkOption { 16 | type = lib.types.port; 17 | default = 8053; 18 | example = 8080; 19 | description = "port for requests"; 20 | }; 21 | 22 | settings = lib.mkOption { 23 | inherit (pkgs.formats.json { }) type; 24 | default = { }; 25 | example = lib.literalExpression '' 26 | { ports.http = "8053" }; 27 | ''; 28 | description = '' 29 | Override settings. 30 | ''; 31 | }; 32 | }; 33 | 34 | config = lib.mkIf cfg.enable { 35 | services = { 36 | blocky = { 37 | enable = true; 38 | 39 | settings = { 40 | ports = { 41 | tls = "853"; 42 | http = cfg.httpPort; 43 | }; 44 | upstream = { 45 | default = [ 46 | "dns2.digitalcourage.de2" # classic 47 | "tcp-tls:dns3.digitalcourage.de" # DoT 48 | "https://dns.digitale-gesellschaft.ch/dns-query" # DoH 49 | ]; 50 | }; 51 | prometheus.enable = config.services.prometheus.enable; 52 | } 53 | // cfg.settings; 54 | }; 55 | 56 | prometheus.scrapeConfigs = [ 57 | { 58 | job_name = "blocky"; 59 | static_configs = [ 60 | { 61 | targets = [ "localhost:${toString cfg.httpPort}" ]; 62 | labels = { 63 | instance = config.networking.hostName; 64 | }; 65 | } 66 | ]; 67 | } 68 | ]; 69 | 70 | # untested 71 | grafana.provision.dashboards.settings.providers = [ 72 | { 73 | name = "Blocky"; 74 | options.path = pkgs.grafana-dashboards.blocky; 75 | disableDeletion = true; 76 | } 77 | ]; 78 | }; 79 | }; 80 | } 81 | -------------------------------------------------------------------------------- /machines/newton/syncthing.nix: -------------------------------------------------------------------------------- 1 | { config, ... }: 2 | { 3 | sops.secrets."syncthing/key" = { }; 4 | sops.secrets."syncthing/cert" = { }; 5 | 6 | # make sure folders exist writable 7 | systemd.tmpfiles.rules = [ 8 | "d /data/ 0755 syncthing syncthing" 9 | "d /data/computer 0755 syncthing syncthing" 10 | "d /data/phone 0755 syncthing syncthing" 11 | "d /data/music 0755 syncthing syncthing" 12 | "d /data/photos 0755 syncthing syncthing" 13 | ]; 14 | 15 | services.syncthing = { 16 | enable = true; 17 | openDefaultPorts = true; 18 | key = config.sops.secrets."syncthing/key".path; 19 | cert = config.sops.secrets."syncthing/cert".path; 20 | settings = { 21 | options = { 22 | localAnnounceEnabled = false; 23 | urAccepted = 3; 24 | }; 25 | devices = { 26 | "workman" = { 27 | id = "KXSCPX3-JCCFZM4-S2LQZZL-3AM6WRL-IPNWVG2-IB5FEDJ-YYFUIRR-VMDO3AL"; 28 | }; 29 | "birdman" = { 30 | id = "34Z4J7W-MJIODUD-J6LDJY6-QILQLLB-CJ4GR7K-7TJM2K3-R7SIPRV-XQO5TAI"; 31 | }; 32 | "serverle" = { 33 | id = "PVPEIN7-PI226LR-ULSBYKT-JGRQ3PS-WSPLGBP-TKYRJVP-OTWE7IV-NLKTBA3"; 34 | }; 35 | }; 36 | folders = { 37 | "Computer" = { 38 | id = "djdxo-1akub"; 39 | path = "/data/computer"; 40 | devices = [ 41 | "workman" 42 | "birdman" 43 | "serverle" 44 | ]; 45 | }; 46 | "Phone" = { 47 | id = "4hds7-gpypp"; 48 | path = "/data/phone"; 49 | devices = [ 50 | "workman" 51 | "birdman" 52 | "serverle" 53 | ]; 54 | }; 55 | "Music" = { 56 | id = "mphdq-n6q7y"; 57 | path = "/data/music"; 58 | fsWatcherEnabled = false; 59 | devices = [ 60 | "workman" 61 | "birdman" 62 | "serverle" 63 | ]; 64 | }; 65 | "Pictures" = { 66 | id = "cujyo-yiabu"; 67 | path = "/data/photos"; 68 | fsWatcherEnabled = false; 69 | devices = [ 70 | "workman" 71 | "serverle" 72 | ]; 73 | }; 74 | }; 75 | }; 76 | }; 77 | } 78 | -------------------------------------------------------------------------------- /images/base-config.nix: -------------------------------------------------------------------------------- 1 | # based on: https://github.com/Mic92/dotfiles/blob/main/nixos/images/base-config.nix 2 | { 3 | lib, 4 | pkgs, 5 | ... 6 | }: 7 | { 8 | networking = { 9 | firewall.enable = false; 10 | 11 | nameservers = [ 12 | # digital courage 13 | "46.182.19.48" 14 | "2a02:2970:1002::18" 15 | ]; 16 | 17 | usePredictableInterfaceNames = false; 18 | useNetworkd = true; 19 | }; 20 | 21 | systemd = { 22 | network.enable = true; 23 | network.networks = 24 | lib.mapAttrs' 25 | ( 26 | num: _: 27 | lib.nameValuePair "eth${num}" { 28 | matchConfig.Name = "eth${num}"; 29 | networkConfig = { 30 | DHCP = "yes"; 31 | LLMNR = true; 32 | IPv4LLRoute = true; 33 | LLDP = true; 34 | IPv6AcceptRA = true; 35 | # used to have a stable address for zfs send 36 | Address = "fd42:4492:6a6d:43:1::${num}/64"; 37 | }; 38 | dhcpConfig = { 39 | UseHostname = false; 40 | RouteMetric = 512; 41 | }; 42 | ipv6AcceptRAConfig.Token = "::521a:c5ff:fefe:65d9"; 43 | } 44 | ) 45 | { 46 | "0" = { }; 47 | "1" = { }; 48 | "2" = { }; 49 | "3" = { }; 50 | }; 51 | }; 52 | 53 | imports = [ 54 | ../machines/core/core.nix 55 | ../machines/core/nix.nix 56 | ]; 57 | 58 | documentation = { 59 | enable = lib.mkDefault false; 60 | doc.enable = lib.mkDefault false; 61 | info.enable = lib.mkDefault false; 62 | nixos.enable = lib.mkDefault false; 63 | nixos.options.warningsAreErrors = false; 64 | }; 65 | 66 | # no auto-updates 67 | systemd.services.update-prefetch.enable = false; 68 | # disable rebuilding 69 | system.switch.enable = false; 70 | 71 | environment.systemPackages = with pkgs; [ 72 | diskrsync 73 | partclone 74 | ntfsprogs 75 | ntfs3g 76 | ]; 77 | 78 | systemd.services.sshd.wantedBy = lib.mkForce [ "multi-user.target" ]; 79 | 80 | users.extraUsers.root.openssh.authorizedKeys.keys = [ 81 | "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOFx6OLwL9MbkD3mnMsv+xrzZHN/rwCTgVs758SCLG0h felix@workman" 82 | ]; 83 | } 84 | -------------------------------------------------------------------------------- /modules/services/hedgedoc/default.nix: -------------------------------------------------------------------------------- 1 | # HedgeDoc is an open-source, web-based, self-hosted, collaborative markdown editor. 2 | { 3 | config, 4 | lib, 5 | pkgs, 6 | ... 7 | }: 8 | let 9 | cfg = config.my.services.hedgedoc; 10 | inherit (config.networking) domain; 11 | in 12 | { 13 | options.my.services.hedgedoc = { 14 | enable = lib.mkEnableOption "Hedgedoc Music Server"; 15 | 16 | settings = lib.mkOption { 17 | inherit (pkgs.formats.json { }) type; 18 | default = { }; 19 | example = { 20 | "LastFM.ApiKey" = "MYKEY"; 21 | "LastFM.Secret" = "MYSECRET"; 22 | "Spotify.ID" = "MYKEY"; 23 | "Spotify.Secret" = "MYSECRET"; 24 | }; 25 | description = '' 26 | Additional settings. 27 | ''; 28 | }; 29 | }; 30 | 31 | config = lib.mkIf cfg.enable { 32 | services = { 33 | hedgedoc = { 34 | enable = true; 35 | 36 | settings = { 37 | domain = "notes.${domain}"; 38 | port = 3080; 39 | protocolUseSSL = true; 40 | db = { 41 | dialect = "sqlite"; 42 | storage = "/var/lib/hedgedoc/hedgedoc.sqlite"; 43 | }; 44 | } 45 | // cfg.settings; 46 | }; 47 | 48 | prometheus = { 49 | scrapeConfigs = [ 50 | { 51 | job_name = "hedgedoc"; 52 | static_configs = [ 53 | { 54 | targets = [ "localhost:${toString config.services.hedgedoc.settings.port}" ]; 55 | labels = { 56 | instance = config.networking.hostName; 57 | }; 58 | } 59 | ]; 60 | } 61 | ]; 62 | }; 63 | 64 | grafana.provision.dashboards.settings.providers = [ 65 | { 66 | name = "Hedgedoc"; 67 | options.path = pkgs.grafana-dashboards.hedgedoc; 68 | disableDeletion = true; 69 | } 70 | ]; 71 | }; 72 | 73 | my.services.webserver.virtualHosts = [ 74 | { 75 | subdomain = "notes"; 76 | inherit (config.services.hedgedoc.settings) port; 77 | } 78 | ]; 79 | 80 | webapps.apps.hedgedoc = { 81 | dashboard = { 82 | name = "Notes"; 83 | category = "app"; 84 | icon = "edit"; 85 | url = "https://notes.${domain}"; 86 | }; 87 | }; 88 | }; 89 | } 90 | -------------------------------------------------------------------------------- /modules/services/grafana/default.nix: -------------------------------------------------------------------------------- 1 | # visualize monitoring services 2 | { 3 | config, 4 | lib, 5 | pkgs, 6 | ... 7 | }: 8 | let 9 | cfg = config.my.services.grafana; 10 | inherit (config.networking) domain; 11 | in 12 | { 13 | options.my.services.grafana = { 14 | enable = lib.mkEnableOption "Grafana for visualizing"; 15 | 16 | username = lib.mkOption { 17 | type = lib.types.str; 18 | default = "felix"; 19 | example = "admin"; 20 | description = "Admin username"; 21 | }; 22 | 23 | passwordFile = lib.mkOption { 24 | type = lib.types.str; 25 | example = "/var/lib/grafana/password.txt"; 26 | description = "Admin password stored in a file"; 27 | }; 28 | }; 29 | 30 | config = lib.mkIf cfg.enable { 31 | services.grafana = { 32 | enable = true; 33 | 34 | settings = { 35 | server = { 36 | domain = "visualization.${domain}"; 37 | root_url = "https://visualization.${domain}/"; 38 | }; 39 | 40 | security = { 41 | admin_user = cfg.username; 42 | admin_password = "$__file{${cfg.passwordFile}}"; 43 | }; 44 | }; 45 | 46 | provision = { 47 | enable = true; 48 | dashboards.settings.providers = [ 49 | { 50 | name = "Grafana"; 51 | options.path = pkgs.grafana-dashboards.grafana; 52 | disableDeletion = true; 53 | } 54 | ]; 55 | }; 56 | }; 57 | 58 | services.prometheus = { 59 | scrapeConfigs = [ 60 | { 61 | job_name = "grafana"; 62 | static_configs = [ 63 | { 64 | targets = [ "localhost:${toString config.services.grafana.settings.server.http_port}" ]; 65 | labels = { 66 | instance = config.networking.hostName; 67 | }; 68 | } 69 | ]; 70 | } 71 | ]; 72 | }; 73 | 74 | my.services.webserver.virtualHosts = [ 75 | { 76 | subdomain = "visualization"; 77 | port = config.services.grafana.settings.server.http_port; 78 | } 79 | ]; 80 | 81 | webapps.apps.grafana = { 82 | dashboard = { 83 | name = "Visualization"; 84 | category = "infra"; 85 | icon = "chart-line"; 86 | url = "https://visualization.${domain}"; 87 | }; 88 | }; 89 | }; 90 | } 91 | -------------------------------------------------------------------------------- /machines/serverle/services.nix: -------------------------------------------------------------------------------- 1 | # Deployed services 2 | { config, ... }: 3 | let 4 | inherit (config.sops) secrets; 5 | in 6 | { 7 | sops.secrets = { 8 | "acme/inwx" = { }; 9 | "bazarr/apikey" = { }; 10 | "borgbackup/password" = { }; 11 | "borgbackup/ssh_key" = { }; 12 | "dyndns/password" = { 13 | owner = config.users.users.inadyn.name; 14 | }; 15 | "fritzbox/password" = { 16 | owner = config.users.users.fritz-exporter.name; 17 | }; 18 | "prowlarr/apikey" = { }; 19 | "radarr/apikey" = { }; 20 | "sonarr/apikey" = { }; 21 | "sso/auth-key" = { }; 22 | "sso/felix/password-hash" = { }; 23 | "sso/felix/totp-secret" = { }; 24 | }; 25 | 26 | # List services that you want to enable: 27 | my.services = { 28 | backup = { 29 | enable = true; 30 | OnFailureMail = "server@buehler.rocks"; 31 | passwordFile = secrets."borgbackup/password".path; 32 | sshKeyFile = secrets."borgbackup/ssh_key".path; 33 | paths = [ "/" ]; 34 | }; 35 | 36 | dyndns = { 37 | enable = true; 38 | passwordFile = secrets."dyndns/password".path; 39 | }; 40 | 41 | # aria2 = { 42 | # enable = true; 43 | # downloadDir = "/data/tmp/aria2/"; 44 | # }; 45 | 46 | home-automation = { 47 | enable = true; 48 | }; 49 | 50 | blocky = { 51 | enable = true; 52 | }; 53 | 54 | prowlarr = { 55 | enable = true; 56 | apiKeyFile = secrets."prowlarr/apikey".path; 57 | }; 58 | radarr = { 59 | enable = true; 60 | apiKeyFile = secrets."radarr/apikey".path; 61 | }; 62 | sonarr = { 63 | enable = true; 64 | apiKeyFile = secrets."sonarr/apikey".path; 65 | }; 66 | bazarr = { 67 | enable = true; 68 | apiKeyFile = secrets."bazarr/apikey".path; 69 | }; 70 | 71 | ssh-server = { 72 | enable = true; 73 | }; 74 | 75 | jellyfin = { 76 | enable = true; 77 | }; 78 | jellyseerr = { 79 | enable = true; 80 | }; 81 | fritzbox = { 82 | enable = true; 83 | passwordFile = secrets."fritzbox/password".path; 84 | }; 85 | # Dashboard 86 | homer = { 87 | enable = true; 88 | }; 89 | webserver = { 90 | enable = true; 91 | }; 92 | acme = { 93 | enable = true; 94 | credentialsFile = secrets."acme/inwx".path; 95 | }; 96 | vpn.enable = true; 97 | }; 98 | } 99 | -------------------------------------------------------------------------------- /machines/serverle/syncthing.nix: -------------------------------------------------------------------------------- 1 | { config, ... }: 2 | { 3 | sops.secrets."syncthing/key" = { }; 4 | sops.secrets."syncthing/cert" = { }; 5 | 6 | # make sure folders exist writable 7 | systemd.tmpfiles.rules = [ 8 | "d /data/ 0755 syncthing syncthing" 9 | "d /data/computer 0755 syncthing syncthing" 10 | "d /data/phone 0755 syncthing syncthing" 11 | "d /data/music 0755 syncthing syncthing" 12 | "d /data/photos 0755 syncthing syncthing" 13 | "d /data/tmp/aria2 0755 syncthing syncthing" 14 | ]; 15 | 16 | services.syncthing = { 17 | enable = true; 18 | openDefaultPorts = true; 19 | key = config.sops.secrets."syncthing/key".path; 20 | cert = config.sops.secrets."syncthing/cert".path; 21 | settings = { 22 | options = { 23 | urAccepted = 3; 24 | }; 25 | devices = { 26 | "workman" = { 27 | id = "KXSCPX3-JCCFZM4-S2LQZZL-3AM6WRL-IPNWVG2-IB5FEDJ-YYFUIRR-VMDO3AL"; 28 | }; 29 | "birdman" = { 30 | id = "34Z4J7W-MJIODUD-J6LDJY6-QILQLLB-CJ4GR7K-7TJM2K3-R7SIPRV-XQO5TAI"; 31 | }; 32 | "newton" = { 33 | id = "5RISLVO-U5A5A7N-5BRYF2X-FTPNAI6-LOQDIMP-MVSM663-6W6VYBL-L7626A6"; 34 | }; 35 | }; 36 | folders = { 37 | "Computer" = { 38 | id = "djdxo-1akub"; 39 | path = "/data/computer"; 40 | devices = [ 41 | "workman" 42 | "birdman" 43 | "newton" 44 | ]; 45 | }; 46 | "Phone" = { 47 | id = "4hds7-gpypp"; 48 | path = "/data/phone"; 49 | devices = [ 50 | "workman" 51 | "birdman" 52 | "newton" 53 | ]; 54 | }; 55 | "Music" = { 56 | id = "mphdq-n6q7y"; 57 | path = "/data/music"; 58 | fsWatcherEnabled = false; 59 | devices = [ 60 | "workman" 61 | "birdman" 62 | "newton" 63 | ]; 64 | }; 65 | "Pictures" = { 66 | id = "cujyo-yiabu"; 67 | path = "/data/photos"; 68 | fsWatcherEnabled = false; 69 | devices = [ 70 | "workman" 71 | "newton" 72 | ]; 73 | }; 74 | "Aria2" = { 75 | id = "jjnzq-pgzua"; 76 | path = "/data/tmp/aria2"; 77 | devices = [ "workman" ]; 78 | }; 79 | }; 80 | }; 81 | }; 82 | } 83 | -------------------------------------------------------------------------------- /modules/services/vpn/default.nix: -------------------------------------------------------------------------------- 1 | # self-hosted vpn 2 | { config, lib, ... }: 3 | let 4 | cfg = config.my.services.vpn; 5 | inherit (config.networking) domain; 6 | in 7 | { 8 | options.my.services.vpn = { 9 | enable = lib.mkEnableOption "Headscale VPN"; 10 | isMaster = lib.mkEnableOption "Headscale Master"; 11 | port = lib.mkOption { 12 | type = lib.types.port; 13 | default = 8090; 14 | example = 8080; 15 | description = "Internal port"; 16 | }; 17 | }; 18 | 19 | config = lib.mkIf cfg.enable ( 20 | lib.mkMerge [ 21 | { 22 | services.tailscale.enable = true; 23 | } 24 | 25 | (lib.mkIf cfg.isMaster { 26 | 27 | services.headscale = { 28 | enable = true; 29 | inherit (cfg) port; 30 | settings = { 31 | dns.base_domain = "buehler.internal"; 32 | dns.override_local_dns = false; 33 | server_url = "https://vpn.${domain}"; 34 | metrics_listen_addr = "127.0.0.1:8091"; 35 | log.level = "warn"; 36 | }; 37 | }; 38 | 39 | services.prometheus = { 40 | scrapeConfigs = [ 41 | { 42 | job_name = "headscale"; 43 | static_configs = [ 44 | { 45 | targets = [ "localhost:8091" ]; 46 | labels = { 47 | instance = config.networking.hostName; 48 | }; 49 | } 50 | ]; 51 | } 52 | ]; 53 | }; 54 | 55 | # Proxy to Headscale 56 | my.services = { 57 | webserver.virtualHosts = [ 58 | { 59 | subdomain = "vpn"; 60 | inherit (cfg) port; 61 | } 62 | ]; 63 | 64 | prometheus.rules = { 65 | HeadscaleHighErrorRate = { 66 | condition = ''rate(headscale_http_requests_total{status=~"5.."}[5m]) > 0.1''; 67 | description = "The error rate for Headscale server {{ $labels.instance }} is above 10% in the last 2 minutes."; 68 | }; 69 | }; 70 | }; 71 | 72 | # waiting for a nice web-ui 73 | # webapps.apps.vpn = { 74 | # dashboard = { 75 | # name = "VPN"; 76 | # category = "infra"; 77 | # icon = "shield-halved"; 78 | # url = "https://vpn.${domain}"; 79 | # }; 80 | # }; 81 | }) 82 | ] 83 | ); 84 | } 85 | -------------------------------------------------------------------------------- /machines/configurations.nix: -------------------------------------------------------------------------------- 1 | { self, ... }: 2 | let 3 | inherit (self.inputs) 4 | disko 5 | framework-plymouth 6 | nixos-hardware 7 | nixpkgs 8 | nixpkgs-unstable 9 | passworts 10 | sops-nix 11 | ; 12 | nixosSystem = nixpkgs.lib.makeOverridable nixpkgs.lib.nixosSystem; 13 | overlay-unstable = final: _prev: { 14 | unstable = import nixpkgs-unstable { 15 | inherit (final.stdenv.hostPlatform) system; 16 | config.allowUnfree = true; 17 | }; 18 | }; 19 | 20 | customModules = import ./core/default.nix; 21 | baseModules = [ 22 | # make flake inputs accessible in NixOS 23 | { 24 | _module.args.self = self; 25 | _module.args.inputs = self.inputs; 26 | } 27 | { 28 | imports = [ 29 | ( 30 | { pkgs, ... }: 31 | { 32 | nixpkgs.config.allowUnfree = true; 33 | nixpkgs.overlays = [ 34 | overlay-unstable 35 | framework-plymouth.overlays.default 36 | (import ../overlays) 37 | (import ../pkgs) 38 | ]; 39 | nix.nixPath = [ "nixpkgs=${pkgs.path}" ]; 40 | documentation.info.enable = false; 41 | } 42 | ) 43 | disko.nixosModules.disko 44 | passworts.nixosModules.passworts 45 | sops-nix.nixosModules.sops 46 | ]; 47 | } 48 | ../modules 49 | ../profiles 50 | ]; 51 | defaultModules = baseModules ++ customModules; 52 | in 53 | { 54 | flake.nixosConfigurations = { 55 | # use your hardware- model from this list: https://github.com/NixOS/nixos-hardware/blob/master/flake.nix 56 | thinkman = nixosSystem { 57 | system = "x86_64-linux"; 58 | modules = defaultModules ++ [ 59 | nixos-hardware.nixosModules.lenovo-thinkpad-t14 60 | ./thinkman/configuration.nix 61 | ]; 62 | }; 63 | workman = nixosSystem { 64 | system = "x86_64-linux"; 65 | modules = defaultModules ++ [ 66 | nixos-hardware.nixosModules.framework-amd-ai-300-series 67 | ./workman/configuration.nix 68 | ]; 69 | }; 70 | newton = nixosSystem { 71 | system = "x86_64-linux"; 72 | modules = defaultModules ++ [ ./newton/configuration.nix ]; 73 | }; 74 | serverle = nixosSystem { 75 | system = "aarch64-linux"; 76 | modules = defaultModules ++ [ 77 | nixos-hardware.nixosModules.raspberry-pi-4 78 | ./serverle/configuration.nix 79 | ]; 80 | }; 81 | }; 82 | } 83 | -------------------------------------------------------------------------------- /modules/services/photos/default.nix: -------------------------------------------------------------------------------- 1 | # self-hosted photo gallery 2 | { config, lib, ... }: 3 | let 4 | cfg = config.my.services.photos; 5 | inherit (config.networking) domain; 6 | inherit (config.services.immich) port; 7 | in 8 | { 9 | options.my.services.photos = { 10 | enable = lib.mkEnableOption "Photos gallery"; 11 | 12 | secretsFile = lib.mkOption { 13 | type = lib.types.nullOr lib.types.path; 14 | default = null; 15 | description = '' 16 | pass secrets 17 | ''; 18 | }; 19 | 20 | settings = lib.mkOption { 21 | type = lib.types.anything; 22 | default = { }; 23 | description = '' 24 | see . 25 | ''; 26 | }; 27 | 28 | path = lib.mkOption { 29 | type = lib.types.path; 30 | default = null; 31 | example = "/data/photos"; 32 | description = '' 33 | Storage path of your original media files (photos and videos) 34 | ''; 35 | }; 36 | }; 37 | 38 | config = lib.mkIf cfg.enable { 39 | services.immich = { 40 | enable = true; 41 | # mediaLocation = path; 42 | inherit (cfg) secretsFile; 43 | settings = { 44 | ffmpeg.transcode = "disabled"; 45 | server.externalDomain = "https://photos.${domain}"; 46 | } 47 | // cfg.settings; 48 | environment = { 49 | IMMICH_TELEMETRY_INCLUDE = "all"; 50 | IMMICH_API_METRICS_PORT = toString (port + 1); 51 | IMMICH_MICROSERVICES_METRICS_PORT = toString (port + 2); 52 | }; 53 | }; 54 | 55 | services.prometheus = { 56 | scrapeConfigs = [ 57 | { 58 | job_name = "immich"; 59 | static_configs = [ 60 | { 61 | targets = [ "localhost:${toString (port + 1)}" ]; 62 | labels = { 63 | instance = config.networking.hostName; 64 | service = "api"; 65 | }; 66 | } 67 | { 68 | targets = [ "localhost:${toString (port + 2)}" ]; 69 | labels = { 70 | instance = config.networking.hostName; 71 | service = "server"; 72 | }; 73 | } 74 | ]; 75 | } 76 | ]; 77 | }; 78 | 79 | my.services.webserver.virtualHosts = [ 80 | { 81 | subdomain = "photos"; 82 | inherit port; 83 | } 84 | ]; 85 | 86 | webapps.apps.photos = { 87 | dashboard = { 88 | name = "Photos"; 89 | category = "media"; 90 | icon = "image"; 91 | url = "https://photos.${domain}"; 92 | method = "get"; 93 | }; 94 | }; 95 | }; 96 | } 97 | -------------------------------------------------------------------------------- /modules/services/promtail/default.nix: -------------------------------------------------------------------------------- 1 | # # log forwarding 2 | { config, lib, ... }: 3 | let 4 | cfg = config.my.services.promtail; 5 | inherit (config.networking) domain; 6 | in 7 | { 8 | options.my.services.promtail = { 9 | enable = lib.mkEnableOption "promtail log forwarding"; 10 | 11 | port = lib.mkOption { 12 | type = lib.types.port; 13 | default = 9081; 14 | example = 3002; 15 | description = "Internal port"; 16 | }; 17 | }; 18 | 19 | config = lib.mkIf cfg.enable { 20 | services.promtail = { 21 | enable = true; 22 | configuration = { 23 | server = { 24 | http_listen_port = cfg.port; 25 | grpc_listen_port = 0; # without it collides with loki; only used for pushing (not used) 26 | }; 27 | positions = { 28 | filename = "/tmp/positions.yaml"; 29 | }; 30 | clients = [ 31 | { 32 | url = "http://localhost:${toString config.services.loki.configuration.server.http_listen_port}/loki/api/v1/push"; 33 | } 34 | ]; 35 | scrape_configs = [ 36 | { 37 | job_name = "journal"; 38 | journal = { 39 | max_age = "24h"; 40 | labels = { 41 | job = "systemd-journal"; 42 | host = config.networking.hostName; 43 | }; 44 | }; 45 | relabel_configs = [ 46 | { 47 | source_labels = [ "__journal__systemd_unit" ]; 48 | target_label = "unit"; 49 | } 50 | ]; 51 | } 52 | ]; 53 | }; 54 | }; 55 | 56 | my.services.prometheus.rules = { 57 | promtail_request_errors = { 58 | condition = ''100 * sum(rate(promtail_request_duration_seconds_count{status_code=~"5..|failed"}[1m])) by (namespace, job, route, instance) / sum(rate(promtail_request_duration_seconds_count[1m])) by (namespace, job, route, instance) > 10''; 59 | time = "15m"; 60 | description = ''{{ $labels.job }} {{ $labels.route }} is experiencing {{ printf "%.2f" $value }}% errors''; 61 | }; 62 | 63 | promtail_file_lagging = { 64 | condition = ''abs(promtail_file_bytes_total - promtail_read_bytes_total) > 1e6''; 65 | time = "15m"; 66 | description = ''{{ $labels.instance }} {{ $labels.job }} {{ $labels.path }} has been lagging by more than 1MB for more than 15m''; 67 | }; 68 | }; 69 | 70 | my.services.webserver.virtualHosts = [ 71 | { 72 | subdomain = "log"; 73 | inherit (cfg) port; 74 | } 75 | ]; 76 | 77 | webapps.apps.promtail = { 78 | dashboard = { 79 | name = "Logging"; 80 | category = "infra"; 81 | icon = "book"; 82 | url = "https://log.${domain}"; 83 | }; 84 | }; 85 | }; 86 | } 87 | -------------------------------------------------------------------------------- /modules/services/navidrome/default.nix: -------------------------------------------------------------------------------- 1 | # A FLOSS self-hosted, subsonic compatible music server 2 | { 3 | config, 4 | lib, 5 | pkgs, 6 | ... 7 | }: 8 | let 9 | cfg = config.my.services.navidrome; 10 | inherit (config.networking) domain; 11 | in 12 | { 13 | options.my.services.navidrome = { 14 | enable = lib.mkEnableOption "Navidrome Music Server"; 15 | 16 | settings = lib.mkOption { 17 | inherit (pkgs.formats.json { }) type; 18 | default = { 19 | EnableSharing = true; 20 | }; 21 | example = { 22 | "LastFM.ApiKey" = "MYKEY"; 23 | "LastFM.Secret" = "MYSECRET"; 24 | "Spotify.ID" = "MYKEY"; 25 | "Spotify.Secret" = "MYSECRET"; 26 | }; 27 | description = '' 28 | Additional settings. 29 | ''; 30 | }; 31 | 32 | musicFolder = lib.mkOption { 33 | type = lib.types.str; 34 | example = "/mnt/music/"; 35 | description = "Music folder"; 36 | }; 37 | }; 38 | 39 | config = lib.mkIf cfg.enable { 40 | services = { 41 | navidrome = { 42 | enable = true; 43 | 44 | settings = cfg.settings // { 45 | MusicFolder = cfg.musicFolder; 46 | LogLevel = "info"; 47 | Prometheus.Enabled = config.services.prometheus.enable; 48 | }; 49 | }; 50 | 51 | prometheus = { 52 | scrapeConfigs = [ 53 | { 54 | job_name = "navidrome"; 55 | static_configs = [ 56 | { 57 | targets = [ "localhost:${toString config.services.navidrome.settings.Port}" ]; 58 | labels = { 59 | instance = config.networking.hostName; 60 | }; 61 | } 62 | ]; 63 | } 64 | ]; 65 | }; 66 | grafana.provision = { 67 | dashboards.settings.providers = [ 68 | { 69 | name = "Navidrome"; 70 | options.path = pkgs.grafana-dashboards.navidrome; 71 | disableDeletion = true; 72 | } 73 | ]; 74 | }; 75 | }; 76 | 77 | my.services.prometheus.rules = { 78 | navidrome_not_enough_albums = { 79 | condition = ''http_navidrome_album_count != 1''; 80 | description = "navidrome: not enough albums as expected: {{$value}}"; 81 | }; 82 | }; 83 | 84 | my.services.webserver.virtualHosts = [ 85 | { 86 | subdomain = "music"; 87 | port = config.services.navidrome.settings.Port; 88 | } 89 | ]; 90 | 91 | webapps.apps.navidrome = { 92 | dashboard = { 93 | name = "Music"; 94 | category = "media"; 95 | icon = "music"; 96 | url = "https://music.${domain}"; 97 | method = "get"; 98 | }; 99 | }; 100 | }; 101 | } 102 | -------------------------------------------------------------------------------- /machines/workman/secrets.yaml: -------------------------------------------------------------------------------- 1 | users: 2 | felix: 3 | password: ENC[AES256_GCM,data:3r1f8iUSgT3mDflSkvl3cyfciBZI5rDa9jMZcfW/mniNt9PJTNaaNbBC/cIxgbwcD991Y/Y0NKB0vxOmKMCIcclvGRUwLdKwFQ==,iv:Chya0lUhNtYcOp3GyNl+I1VyNV3KvSOZxeWUgz3SBnA=,tag:OhdIXdwbQAV0eZZS/kpW1Q==,type:str] 4 | borgbackup: 5 | password: ENC[AES256_GCM,data:4c7Hnoq+BX2iiCwv6wMXJEYz8toRV7fcVA==,iv:TW0qSl2qhBnaKtUH+Bfqzqz0rrjq10E/hpD4Ataazh8=,tag:RMwLBEjugmZFqI8WLBx8vg==,type:str] 6 | ssh_key: ENC[AES256_GCM,data:LhGZ2VzOWwcjO0gd1ia7Nb6Roy9+kZwYW2ucjWqpeq6e1xl6JXFRkJJ/bLIA++yUVZarLSabhDn0DDHVfGyO5fOdSuuduu0lCl+ffxz3zX7RjibInwstwnvqJo2ZrBbI+9QPBxI4IRo4uEllljN5N4Fvq3Pq6tMLX2ZpkqIEWayDa28gxM3Ib/at9DGSiEsyhJm2b6HTMUi/lXX9vIxPPOgaqmuWgpp7Tzkd8Ph08zvnloWvlRZaYxl/n7+VxEohqZY88RfFSNT/N5TtgnFFTTRX+9B0vCRqyJq9XQ3cf0fG0NJzb0kcO25k45kAGU7QOJ8V7YPgPPwPPWU7IAluhFi9x78QLsuAioXnl79aZli11NE7Gyn2n4FkkhVX1W078tbvLiNURSfVro7crcf6WCX1PBvbuDFdKg8I2r58aZ5vglD5QD0gBbhL0js13z80DRCdNCtyNQZfgMXvAIEq3Lw3UvTUspInMJos7Kgs0hCK/SoP87SymYEkGORpZzoGg/zlHf0kExOj3Fni+LK24sArc/CIeCBBc/BJ,iv:jDAB5ExuplfUtJqgub4oV/wbytpnjK3MjJko/rsJ0fM=,tag:s2aODtXR9Qu29tKjZvfyKA==,type:str] 7 | nixremote: 8 | ssh_key: ENC[AES256_GCM,data:vO3U1dW3VhHSNDDFlt6Slqf2+sf0xeZ/3ztyqGIkUKwU87flHP0cVVyPF71UCSowhFu7SSK/DEkzTdNjoCnx21HDQ5zc+JZFJjKIE7HotcJwWR6/a9hFZniY4FVLAnW+locWjbcOB+Mou/VHm1uo+a8wmNOJTw7FS3ZM6FfANLDZcjxcjJKu23UcUyXDxuMySkRKmLneyfwlk5aP1toyjBwnS9og1UPjSTy6ldM6gsfUv1mZ+BbCWgtiwxcGEy859ROOMuqKxUKGDbgHnby/aqs7dP8xzoVoZQXjHYoWLp1/7IforoyXb8GiUkOWRlCC/dJm6Kx9Y67M13LwKEpX2+WOfNCHqTjYO4234zfOZ1DRJtwCAMKH7y0sjAYp9bK6iHv3qKIjreDSA8S6xbQP5LnXvp55460Puq+Xe2HdnugRfd3d6pKMrHmnRE8mmstG6jDxdGqtoGo2VBvDGAC080J6BXxP0/xpeXwc99TSDdHEymqodZoz3jQuGMb+brxSyDgaw6FEDPNkpxdUFl0/vShTfROqgT0NVbH3,iv:SXIDRjHBQBcstSz1Pgv5jI8+XHbJA/QrqF9EOkIcvqE=,tag:ZakbophvoJmWlVX88hMA7A==,type:str] 9 | sops: 10 | age: 11 | - recipient: age1hf8m9upp00dr7qv2kmqdr50fpvd9ejzkfu8yknqnuda2aas2tvrs4l3u7m 12 | enc: | 13 | -----BEGIN AGE ENCRYPTED FILE----- 14 | YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSArdGpCSWlVMUt1M1hZM2FM 15 | WWVzVlA3bVRpOUlXU1hQMEhKUDlKMmtrRWhRCkNNZko1ZlowRWtTZzRwelBYTlY3 16 | Q1o2NE9pSFNUckJDQzBTZUxkbkR4SncKLS0tIHBrQVo0OGpvQnJVblRta0JHLy9I 17 | K2JaanpIRXVsR3VNejhWNmdEMjczUTQKfp7BI9UEl+r4iehbfoJk9x5KjXszsjVZ 18 | qEKeyj7z8w+rjspNXtAhh1CN2EcHsT8DiygrIheltSMZYfWk1Ai8Zw== 19 | -----END AGE ENCRYPTED FILE----- 20 | - recipient: age1f2e644jteyeppfaatajtvjmsupl0e7nzx97ded6m0cgzw04l84ks5xl9l2 21 | enc: | 22 | -----BEGIN AGE ENCRYPTED FILE----- 23 | YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBKTkhPcmJKVDc0RWRaeHA0 24 | U2F5WnVzMzcxSGZUakRCNlQzK0tyeEZ2bDEwCklRMUxVS3gyYlM3aWsxODV4MmVO 25 | S0JuVFNvaHNJMzFUVDkvbmZYa2MweTQKLS0tIDJ1eHp2OGI1QjJJTjJzK2ZpNm5n 26 | bEl0bWl3OHdOcDU2UEpIWjNUWjZpeEkK7J7WK/09l2gY9NixQL+I5B7wCWH2fJQ1 27 | +Zau6I2CDVhine3twNsGN0OpUQF7aHX2dwFhKyxPthFj3YTE1oR0yA== 28 | -----END AGE ENCRYPTED FILE----- 29 | lastmodified: "2025-06-02T18:34:10Z" 30 | mac: ENC[AES256_GCM,data:I3bnbm/2Us++x8k4eUeciBk0popNvIONOMbINiAwAhNm8xx3kYgqNjEA9/Ny9lx0GWSV6fkV+hDh2Azjy4++b59xOXeUXCYvaJwjWlqIorAgbU3ldm40yBbwab5oca/mRy7Lt8mAGPt4EQfDqFvJdCLi0Xzt8G3m/cREkv6qMis=,iv:oSaz+34s0VMjF/ZkLYv7EJSIIDnnbRiWA15gM0ObaBE=,tag:5uAzP45ISvb9t/aYxYduLg==,type:str] 31 | unencrypted_suffix: _unencrypted 32 | version: 3.10.2 33 | -------------------------------------------------------------------------------- /machines/thinkman/secrets.yaml: -------------------------------------------------------------------------------- 1 | users: 2 | felix: 3 | password: ENC[AES256_GCM,data:p6PEjIHC8uaKxzxskYB5dmaD9a+FPY0bRZg2Jal3z87IA7zyQtS52Y9apta0gYWjX6iP2X9u6uDTB2VxAnGWvm5pVZJq4SwUSQ==,iv:2m4GQpWqlnxT9o/58Gn1jaHa4txafEBcBxkl+fQMFg4=,tag:ej0opSh14ObE9ybG2WhISw==,type:str] 4 | borgbackup: 5 | password: ENC[AES256_GCM,data:RF385VVV1aUjZA5FcJGwizhiAlINW7HiFA==,iv:W1+khhHyKfDKD7vs0t442g28MJ+0zu5ZpKYMmrmvEyM=,tag:whQ4euwgukHaTTJIAwpYSg==,type:str] 6 | ssh_key: ENC[AES256_GCM,data:oEh9OKq14UvCfUIasqMTOX9cfLUTbWK7CWLjdlAw3yxgqSZFM79kr6a7OfVZ7Uv1ycqdH/qYt3OVU+CCvy9KFzn/zQFHJnN6r5vSwOYU2/TnnlAwAp6XBWwN1SNUt5Xcf/MQEu3BJlvGewVC0ApkutcZdvQKTUDbKuThhZ5P7n8+y2CZZvGBc1lpd0DfMpmGbBES27TRclycj5YHmyOPQOI0Nvbieh3GByKIouvoQY6LKuBVOrxVptJYKY7Wjv+EjpQrSc/SX/OIv2fYxrhrZgT+LjOnIp+bMduhIvDH5kI47KZSauyJxQXPK+ZqJ9C2SlABsODvHkY518qOZNFN56X486AnWnP+2CCx5VQxT13msUWzmtZagc0gKqZgNw+NnLtsG6m2rUj5peKjuxjCBK+BVloKbGyfvj6VgZvQQlAS1TEgcnWYRfeoT8gX4FmvxX4yMILiyZrkDfzchOI4giOlX2y6hvXGr5Qr0gTzQruwnSYZ9M+wxg+sZdLl0q+07nwRqjlPQHah5vd5Qz7q,iv:9N1aRbHAcSPt3v/ZrKyJODNDn3gw8ttMC/gmamHgxwU=,tag:NhZSGu7139hsPSprdvItPg==,type:str] 7 | nixremote: 8 | ssh_key: ENC[AES256_GCM,data:vO3U1dW3VhHSNDDFlt6Slqf2+sf0xeZ/3ztyqGIkUKwU87flHP0cVVyPF71UCSowhFu7SSK/DEkzTdNjoCnx21HDQ5zc+JZFJjKIE7HotcJwWR6/a9hFZniY4FVLAnW+locWjbcOB+Mou/VHm1uo+a8wmNOJTw7FS3ZM6FfANLDZcjxcjJKu23UcUyXDxuMySkRKmLneyfwlk5aP1toyjBwnS9og1UPjSTy6ldM6gsfUv1mZ+BbCWgtiwxcGEy859ROOMuqKxUKGDbgHnby/aqs7dP8xzoVoZQXjHYoWLp1/7IforoyXb8GiUkOWRlCC/dJm6Kx9Y67M13LwKEpX2+WOfNCHqTjYO4234zfOZ1DRJtwCAMKH7y0sjAYp9bK6iHv3qKIjreDSA8S6xbQP5LnXvp55460Puq+Xe2HdnugRfd3d6pKMrHmnRE8mmstG6jDxdGqtoGo2VBvDGAC080J6BXxP0/xpeXwc99TSDdHEymqodZoz3jQuGMb+brxSyDgaw6FEDPNkpxdUFl0/vShTfROqgT0NVbH3,iv:SXIDRjHBQBcstSz1Pgv5jI8+XHbJA/QrqF9EOkIcvqE=,tag:ZakbophvoJmWlVX88hMA7A==,type:str] 9 | sops: 10 | kms: [] 11 | gcp_kms: [] 12 | azure_kv: [] 13 | hc_vault: [] 14 | age: 15 | - recipient: age1hf8m9upp00dr7qv2kmqdr50fpvd9ejzkfu8yknqnuda2aas2tvrs4l3u7m 16 | enc: | 17 | -----BEGIN AGE ENCRYPTED FILE----- 18 | YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBuMHpBZmU4ZVo5UitOQ1la 19 | UUNZNEU5OVFqM2hsY2g0YzRkemRvbUdyYVVjCjhFbDR2cUxTYUU2dU1Oekc4VEZ5 20 | K2JEd1JZMkFhUXhFaTVnTytFeFU4TmcKLS0tIG5zTGZ4OG9GN2tNVE5zUG95dXFk 21 | cG01NlA0YlpzcENqWjJMUkQwZXJMcUEKv94rjj5iHY1HAZQiE5yleC4f0WABcXbm 22 | Wf4xYYCCWUmcTKXabIyPWn9eCNYCQgy29YTcTKu4/8BvebrGkRHuHw== 23 | -----END AGE ENCRYPTED FILE----- 24 | - recipient: age1spt854cdscqs757a8kazth52rv4p9udh54suw9lpzlqg5savyapq2u0c03 25 | enc: | 26 | -----BEGIN AGE ENCRYPTED FILE----- 27 | YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBLSUlHb04rMGR5YWFhUUZq 28 | QXVHYnZQUUdQc0FzbTgyalBPZktBdDVvZ2lRClpzcDltOFA0eFlqTGRUNFptcW1C 29 | WGZyamIyNEFTQXQ4R2pmdU9FS3lma0kKLS0tIFphS0ZhbmxDb3A5MnVvaVJGT1Iw 30 | bzY2ZTY1QUtSRjlOZ1E0Vkw5Q1cxYmMKqwvWUv2XpRIenGwCpZuwKQc0ZsiX2AAx 31 | pmIh4f10G7wr1rLeodRi2KxYIrrudPbxEWIuzmBRyHc7+3EPpzLetw== 32 | -----END AGE ENCRYPTED FILE----- 33 | lastmodified: "2024-03-14T23:58:29Z" 34 | mac: ENC[AES256_GCM,data:LbsoPjZifAaODMKtBz7h2/kA/GcSHNNHQfEyl4NMAJMd//45orc72R3KlyNKoNjx32xTXX/8uNX+9iCVegawJ+pdHrQTM+gCEhuxeN5fXNrl3jwi5p1A34EyLOxM+05QQiUYlBLJhTYOU0A6/o9zCClTXHbXnnUxRWNclkYo1oI=,iv:bOlcGrTzz6LpheOsE7ASkjA9w4I6l6FTYd5RSG2uI2U=,tag:vR/7HUFTCwU4YOHgqjC3mw==,type:str] 35 | pgp: [] 36 | unencrypted_suffix: _unencrypted 37 | version: 3.8.1 38 | -------------------------------------------------------------------------------- /modules/services/git/default.nix: -------------------------------------------------------------------------------- 1 | # self-hosted git service 2 | { 3 | config, 4 | lib, 5 | pkgs, 6 | ... 7 | }: 8 | let 9 | cfg = config.my.services.git; 10 | inherit (config.networking) domain; 11 | in 12 | { 13 | options.my.services.git = { 14 | enable = lib.mkEnableOption "Git server"; 15 | 16 | passwordFile = lib.mkOption { 17 | type = lib.types.path; 18 | example = "/var/lib/somewhere/password.txt"; 19 | description = '' 20 | Path to a file containing the admin's password. 21 | ''; 22 | }; 23 | }; 24 | 25 | config = lib.mkIf cfg.enable { 26 | 27 | # configure admin user 28 | systemd.services.forgejo.preStart = 29 | let 30 | adminCmd = "${lib.getExe config.services.forgejo.package} admin user"; 31 | user = "felix"; 32 | in 33 | '' 34 | admin="${adminCmd}" 35 | if ! $admin list | grep "${user}"; then 36 | ${adminCmd} create --admin --email "server@localhost" --username ${user} --password "$(tr -d '\n' < ${cfg.passwordFile})" || true 37 | else 38 | ${adminCmd} change-password --username ${user} --password "$(tr -d '\n' < ${cfg.passwordFile})" || true 39 | fi 40 | ''; 41 | 42 | services = { 43 | forgejo = { 44 | enable = true; 45 | settings = { 46 | server = { 47 | HTTP_PORT = 3042; 48 | ROOT_URL = "https://code.${domain}"; 49 | }; 50 | session.COOKIE_SECURE = true; 51 | service.DISABLE_REGISTRATION = true; 52 | ui.DEFAULT_THEME = "arc-green"; 53 | log.LEVEL = "Warn"; 54 | metrics.ENABLED = config.services.prometheus.enable; 55 | }; 56 | lfs.enable = true; 57 | }; 58 | 59 | prometheus = { 60 | scrapeConfigs = [ 61 | { 62 | job_name = "forgejo"; 63 | static_configs = [ 64 | { 65 | targets = [ "localhost:${toString config.services.forgejo.settings.server.HTTP_PORT}" ]; 66 | labels = { 67 | instance = config.networking.hostName; 68 | }; 69 | } 70 | ]; 71 | } 72 | ]; 73 | }; 74 | grafana.provision = { 75 | dashboards.settings.providers = [ 76 | { 77 | name = "Forgejo"; 78 | options.path = pkgs.grafana-dashboards.forgejo; 79 | disableDeletion = true; 80 | } 81 | ]; 82 | }; 83 | }; 84 | 85 | # Proxy to forgejo 86 | my.services = { 87 | webserver.virtualHosts = [ 88 | { 89 | subdomain = "code"; 90 | port = config.services.forgejo.settings.server.HTTP_PORT; 91 | } 92 | ]; 93 | 94 | backup = { 95 | paths = [ 96 | config.services.forgejo.lfs.contentDir 97 | config.services.forgejo.repositoryRoot 98 | ]; 99 | }; 100 | 101 | prometheus.rules = { 102 | forgejo = { 103 | condition = ''rate(promhttp_metric_handler_requests_total{job="forgejo", code="500"}[5m]) > 3''; 104 | description = "{{$labels.instance}}: forgejo instances error rate went up: {{$value}} errors in 5 minutes"; 105 | }; 106 | }; 107 | }; 108 | 109 | webapps.apps.git = { 110 | dashboard = { 111 | name = "Code"; 112 | category = "app"; 113 | icon = "code-branch"; 114 | url = "https://code.${domain}"; 115 | }; 116 | }; 117 | }; 118 | } 119 | -------------------------------------------------------------------------------- /flake.nix: -------------------------------------------------------------------------------- 1 | { 2 | description = "NixOS configuration"; 3 | inputs = { 4 | nixpkgs.url = "nixpkgs/nixos-25.11"; 5 | nixpkgs-unstable.url = "nixpkgs/nixos-unstable"; 6 | 7 | flake-parts.url = "github:hercules-ci/flake-parts"; 8 | 9 | git-hooks = { 10 | url = "github:cachix/git-hooks.nix"; 11 | inputs.nixpkgs.follows = "nixpkgs"; 12 | }; 13 | 14 | nixos-hardware.url = "github:NixOS/nixos-hardware"; 15 | 16 | nixinate = { 17 | url = "github:matthewcroughan/nixinate"; 18 | inputs.nixpkgs.follows = "nixpkgs"; 19 | }; 20 | 21 | sops-nix = { 22 | url = "github:Mic92/sops-nix"; 23 | inputs.nixpkgs.follows = "nixpkgs"; 24 | }; 25 | 26 | nixos-generators = { 27 | url = "github:nix-community/nixos-generators"; 28 | inputs.nixpkgs.follows = "nixpkgs"; 29 | }; 30 | 31 | disko = { 32 | url = "github:nix-community/disko"; 33 | inputs.nixpkgs.follows = "nixpkgs"; 34 | }; 35 | 36 | framework-plymouth = { 37 | url = "github:j-pap/framework-plymouth"; 38 | }; 39 | 40 | # own flakes 41 | stunkymonkey = { 42 | url = "github:Stunkymonkey/stunkymonkey.de"; 43 | inputs.nixpkgs.follows = "nixpkgs"; 44 | }; 45 | 46 | passworts = { 47 | url = "github:Stunkymonkey/passworts"; 48 | inputs.nixpkgs.follows = "nixpkgs"; 49 | }; 50 | }; 51 | 52 | outputs = 53 | inputs@{ 54 | self, 55 | flake-parts, 56 | nixinate, 57 | ... 58 | }: 59 | flake-parts.lib.mkFlake { inherit inputs; } { 60 | 61 | imports = [ 62 | ./machines/configurations.nix 63 | ./images/flake-module.nix 64 | inputs.git-hooks.flakeModule 65 | ]; 66 | 67 | systems = [ 68 | "x86_64-linux" 69 | "aarch64-linux" 70 | ]; 71 | 72 | perSystem = 73 | { 74 | inputs', 75 | config, 76 | pkgs, 77 | system, 78 | ... 79 | }: 80 | { 81 | # make pkgs available to all `perSystem` functions 82 | _module.args.pkgs = import inputs.nixpkgs { 83 | inherit system; 84 | }; 85 | 86 | # enable pre-commit checks 87 | pre-commit.settings = { 88 | hooks = { 89 | actionlint.enable = true; 90 | deadnix = { 91 | enable = true; 92 | settings.noLambdaPatternNames = true; 93 | }; 94 | editorconfig-checker.enable = true; 95 | end-of-file-fixer.enable = true; 96 | flake-checker.enable = true; 97 | keep-sorted.enable = true; 98 | markdownlint.enable = true; 99 | nil.enable = true; 100 | nixfmt-rfc-style.enable = true; 101 | shellcheck.enable = true; 102 | statix.enable = true; 103 | typos = { 104 | enable = true; 105 | excludes = [ 106 | "secrets\\.yaml" 107 | "\\.sops\\.yaml" 108 | ]; 109 | settings.ignored-words = [ 110 | "flate" 111 | "hda" 112 | ]; 113 | }; 114 | yamllint = { 115 | enable = true; 116 | excludes = [ "secrets\\.yaml" ]; 117 | }; 118 | }; 119 | }; 120 | 121 | devShells.default = pkgs.mkShell { 122 | inputsFrom = [ config.pre-commit.devShell ]; 123 | nativeBuildInputs = [ 124 | # inputs'.nix.packages.nix 125 | inputs'.sops-nix.packages.sops-import-keys-hook 126 | inputs'.disko.packages.disko 127 | ]; 128 | }; 129 | # workaround for https://github.com/MatthewCroughan/nixinate/issues/12 130 | apps = (nixinate.nixinate.${system} self).nixinate; 131 | }; 132 | }; 133 | } 134 | -------------------------------------------------------------------------------- /machines/newton/services.nix: -------------------------------------------------------------------------------- 1 | # Deployed services 2 | { config, ... }: 3 | let 4 | inherit (config.sops) secrets; 5 | in 6 | { 7 | sops.secrets = { 8 | "acme/inwx" = { }; 9 | "borgbackup/password" = { }; 10 | "borgbackup/ssh_key" = { }; 11 | "sso/auth-key" = { }; 12 | "sso/felix/password-hash" = { }; 13 | "sso/felix/totp-secret" = { }; 14 | "paperless/password" = { }; 15 | "git/password" = { 16 | owner = config.users.users.forgejo.name; 17 | }; 18 | "nextcloud/password" = { 19 | owner = config.users.users.nextcloud.name; 20 | }; 21 | "nextcloud-exporter/password" = { 22 | owner = config.users.users.nextcloud-exporter.name; 23 | }; 24 | "freshrss/password" = { 25 | owner = config.users.users.freshrss.name; 26 | }; 27 | "photos/secrets" = { }; 28 | "grafana/password" = { 29 | owner = config.users.users.grafana.name; 30 | }; 31 | "matrix-bot/password" = { }; 32 | }; 33 | 34 | # List services that you want to enable: 35 | my.services = { 36 | backup = { 37 | enable = true; 38 | OnFailureMail = "server@buehler.rocks"; 39 | passwordFile = secrets."borgbackup/password".path; 40 | sshKeyFile = secrets."borgbackup/ssh_key".path; 41 | paths = [ "/" ]; 42 | }; 43 | # My own personal homepage 44 | homepage = { 45 | enable = true; 46 | }; 47 | # Dashboard 48 | homer = { 49 | enable = true; 50 | }; 51 | # remote build 52 | remote-build.enable = true; 53 | # RSS provider for websites that do not provide any feeds 54 | rss-bridge = { 55 | enable = true; 56 | }; 57 | # voice-chat server 58 | mumble-server = { 59 | enable = true; 60 | }; 61 | # sandbox video game 62 | # minecraft-server = { 63 | # enable = true; 64 | # }; 65 | # music streaming server 66 | navidrome = { 67 | enable = true; 68 | musicFolder = "/data/music"; 69 | }; 70 | # self-hosted cloud 71 | nextcloud = { 72 | enable = true; 73 | passwordFile = secrets."nextcloud/password".path; 74 | exporterPasswordFile = secrets."nextcloud-exporter/password".path; 75 | }; 76 | # document management system 77 | paperless = { 78 | enable = true; 79 | passwordFile = secrets."paperless/password".path; 80 | settings.PAPERLESS_ADMIN_USER = "felix"; 81 | mediaDir = "/data/docs"; 82 | }; 83 | # RSS aggregator and reader 84 | freshrss = { 85 | enable = true; 86 | defaultUser = "felix"; 87 | passwordFile = secrets."freshrss/password".path; 88 | }; 89 | # self-hosted git server 90 | git = { 91 | enable = true; 92 | passwordFile = secrets."git/password".path; 93 | }; 94 | # collaborative markdown editor 95 | hedgedoc = { 96 | enable = true; 97 | }; 98 | # a password-generator using the marokov model 99 | passworts = { 100 | enable = true; 101 | }; 102 | # self-hosted photo gallery 103 | photos = { 104 | enable = true; 105 | secretsFile = secrets."photos/secrets".path; 106 | }; 107 | ssh-server = { 108 | enable = true; 109 | }; 110 | initrd-ssh = { 111 | enable = true; 112 | }; 113 | # self-hosted recipe manager 114 | tandoor-recipes = { 115 | enable = true; 116 | }; 117 | 118 | prometheus = { 119 | enable = true; 120 | }; 121 | alertmanager = { 122 | enable = true; 123 | }; 124 | matrix-bot = { 125 | enable = true; 126 | PasswortFile = secrets."matrix-bot/password".path; 127 | }; 128 | grafana = { 129 | enable = true; 130 | passwordFile = secrets."grafana/password".path; 131 | }; 132 | loki = { 133 | enable = true; 134 | }; 135 | promtail = { 136 | enable = true; 137 | }; 138 | blackbox = { 139 | enable = true; 140 | }; 141 | webserver = { 142 | enable = true; 143 | }; 144 | acme = { 145 | enable = true; 146 | credentialsFile = secrets."acme/inwx".path; 147 | }; 148 | vpn = { 149 | enable = true; 150 | isMaster = true; 151 | }; 152 | }; 153 | } 154 | -------------------------------------------------------------------------------- /modules/services/homer/config.nix: -------------------------------------------------------------------------------- 1 | { config, lib, ... }: 2 | 3 | { 4 | options.webapps = { 5 | dashboardCategories = lib.mkOption { 6 | type = lib.types.listOf ( 7 | lib.types.submodule { 8 | options = { 9 | name = lib.mkOption { 10 | type = lib.types.str; 11 | description = '' 12 | Category name. 13 | ''; 14 | example = "Applications"; 15 | }; 16 | tag = lib.mkOption { 17 | type = lib.types.str; 18 | description = '' 19 | Category tag. 20 | ''; 21 | example = "app"; 22 | }; 23 | }; 24 | } 25 | ); 26 | description = '' 27 | App categories to display on the dashboard. 28 | ''; 29 | example = [ 30 | { 31 | name = "Application"; 32 | tag = "app"; 33 | } 34 | ]; 35 | default = [ ]; 36 | }; 37 | 38 | apps = lib.mkOption { 39 | type = lib.types.attrsOf ( 40 | lib.types.submodule { 41 | options = { 42 | dashboard = { 43 | url = lib.mkOption { 44 | type = lib.types.nullOr lib.types.str; 45 | description = '' 46 | Url to webapp 47 | ''; 48 | example = "http://192.168.1.10:1234"; 49 | default = null; 50 | }; 51 | name = lib.mkOption { 52 | type = lib.types.nullOr lib.types.str; 53 | description = '' 54 | Application name. 55 | ''; 56 | example = "App"; 57 | default = null; 58 | }; 59 | category = lib.mkOption { 60 | type = lib.types.nullOr lib.types.str; 61 | description = '' 62 | App category tag. 63 | ''; 64 | example = "app"; 65 | default = null; 66 | }; 67 | icon = lib.mkOption { 68 | type = lib.types.nullOr lib.types.str; 69 | description = '' 70 | Font Awesome application icon. 71 | ''; 72 | example = "rss"; 73 | default = null; 74 | }; 75 | type = lib.mkOption { 76 | type = lib.types.nullOr lib.types.str; 77 | description = '' 78 | application type. 79 | ''; 80 | example = "Ping"; 81 | default = "Ping"; 82 | }; 83 | method = lib.mkOption { 84 | type = lib.types.enum [ 85 | "get" 86 | "head" 87 | ]; 88 | description = '' 89 | method of request used 90 | ''; 91 | example = "get"; 92 | default = "head"; 93 | }; 94 | }; 95 | }; 96 | } 97 | ); 98 | description = '' 99 | Defines a web application. 100 | ''; 101 | default = { }; 102 | }; 103 | }; 104 | 105 | config = 106 | let 107 | cfg = config.webapps; 108 | appsWithName = builtins.filter (app: app.dashboard.name != null) (lib.attrValues cfg.apps); 109 | in 110 | { 111 | lib.webapps.homerServices = lib.forEach cfg.dashboardCategories ( 112 | category: 113 | let 114 | catTag = category.tag; 115 | catApps = lib.sort (a: b: a.dashboard.name < b.dashboard.name) ( 116 | builtins.filter ( 117 | app: 118 | let 119 | cat = app.dashboard.category; 120 | in 121 | (cat != null && cat == catTag) || (cat == null && catTag == "misc") 122 | ) appsWithName 123 | ); 124 | in 125 | { 126 | inherit (category) name; 127 | items = lib.forEach catApps ( 128 | app: 129 | let 130 | dash = app.dashboard; 131 | in 132 | { 133 | inherit (dash) 134 | method 135 | name 136 | type 137 | url 138 | ; 139 | icon = lib.optionalString (dash.icon != null) "fas fa-${dash.icon}"; 140 | target = "_blank"; 141 | } 142 | ); 143 | } 144 | ); 145 | 146 | my.services.blackbox.http_endpoints = lib.mapAttrsToList (_: app: app.dashboard.url) cfg.apps ++ [ 147 | "https://${config.networking.domain}/" 148 | ]; 149 | }; 150 | } 151 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # nixos-config [![built with nix](https://builtwithnix.org/badge.svg)](https://builtwithnix.org)![CI](https://github.com/Stunkymonkey/nixos/actions/workflows/nix.yml/badge.svg) 2 | 3 | This repository holds my NixOS configuration. 4 | It is fully reproducible, flakes based, and position-independent, ... 5 | 6 | used flakes: 7 | 8 | - image generation: [nixos-generators](https://github.com/nix-community/nixos-generators) 9 | - disk partitioning: [disko](https://github.com/nix-community/disko) 10 | - secrets: [sops-nix](https://github.com/Mic92/sops-nix) 11 | - deployment: [nixinate](https://github.com/MatthewCroughan/nixinate), see [usage](#usage) 12 | - formatting: [git-hooks](https://github.com/cachix/git-hooks.nix) 13 | - install: [nixos-anywhere](https://github.com/nix-community/nixos-anywhere/) 14 | 15 | ## Structure 16 | 17 | ```text 18 | . 19 | ├── images # custom image generations 20 | ├── machines # machine definitions 21 | ├── modules # own nix-options, to modularize services/hardware/... 22 | ├── overlays # overlays 23 | ├── pkgs # own packages, which are not available in nixpkgs 24 | └── profiles # summarize module collections into single options 25 | ``` 26 | 27 | ## Usage 28 | 29 | - updating: 30 | 31 | ```bash 32 | nix flake update 33 | ``` 34 | 35 | - deployment/update: 36 | 37 | ```bash 38 | nix run .# 39 | ``` 40 | 41 | - secrets: 42 | 43 | ```bash 44 | sops ./machines//secrets.yaml 45 | ``` 46 | 47 | - images: 48 | 49 | ```bash 50 | nix build .#install-iso 51 | nix build .#aarch64-install --system aarch64-linux 52 | ``` 53 | 54 | - vms: 55 | 56 | ```bash 57 | nixos-rebuild build-vm --flake .# 58 | ``` 59 | 60 | - (re-)install: 61 | 62 | make sure you have ssh-root access to the machine and the ssh-key is used properly. 63 | (It does not matter what system is installed before.) 64 | 65 | 1. generate config (only needed for new host) 66 | 67 | get `nixos-generate-config` to run via nix and execute 68 | 69 | ```bash 70 | nixos-generate-config --no-filesystems --root $(mktemp -d) 71 | ``` 72 | 73 | reuse the `hardware-configuration.nix` to create a new machine with its flake. 74 | 75 | 1. setup secrets 76 | 77 | 1. new host 78 | 79 | then prepare the secrets in the following layout: 80 | 81 | ```bash 82 | # enter disk encryption key 83 | (umask 077; echo "my-super-safe-password" > /tmp/disk.key) 84 | 85 | temp=$(mktemp -d) 86 | # ssh-host keys 87 | install -d -m755 "$temp/etc/ssh" 88 | install -d -m755 "$temp/etc/secrets/initrd" 89 | ssh-keygen -o -a 100 -N "" -t rsa -b 4096 -f "$temp/etc/ssh/ssh_host_rsa_key" 90 | ssh-keygen -o -a 100 -N "" -t ed25519 -f "$temp/etc/ssh/ssh_host_ed25519_key" 91 | ssh-keygen -o -a 100 -N "" -t ed25519 -f "$temp/etc/secrets/initrd/ssh_host_ed25519_key" 92 | ``` 93 | 94 | 1. existing host 95 | 96 | ```bash 97 | (umask 077; echo "my-super-safe-password" > /tmp/disk.key) 98 | temp=$(mktemp -d) 99 | find $temp -printf '%M %p\n' 100 | ``` 101 | 102 | should result in something looking like this 103 | 104 | ```text 105 | drwx------ $temp 106 | drwxr-xr-x $temp/etc 107 | drwxr-xr-x $temp/etc/ssh 108 | -rw------- $temp/etc/ssh/ssh_host_rsa_key 109 | -rw------- $temp/etc/ssh/ssh_host_ed25519_key 110 | -rw-r--r-- $temp/etc/ssh/ssh_host_rsa_key.pub 111 | -rw-r--r-- $temp/etc/ssh/ssh_host_ed25519_key.pub 112 | drwxr-xr-x $temp/etc/secrets 113 | drwxr-xr-x $temp/etc/secrets/initrd 114 | -rw------- $temp/etc/secrets/initrd/ssh_host_ed25519_key 115 | -rw-r--r-- $temp/etc/secrets/initrd/ssh_host_ed25519_key.pub 116 | ``` 117 | 118 | 1. execute install 119 | 120 | now simply install by executing (this will delete all data!): 121 | 122 | ```bash 123 | nix run github:nix-community/nixos-anywhere -- \ 124 | --disko-mode disko \ 125 | --disk-encryption-keys /tmp/disk.key /tmp/disk.key \ 126 | --extra-files "$temp" \ 127 | --flake .# \ 128 | root@ 129 | ``` 130 | 131 | ## Inspired by 132 | 133 | - [Nix config by Mic92](https://github.com/Mic92/dotfiles) 134 | - [Nix config by ambroisie](https://github.com/ambroisie/nix-config) 135 | - [Nix config by pborzenkov](https://github.com/pborzenkov/nix-config) 136 | - [Nix config by nyanloutre](https://gitea.nyanlout.re/nyanloutre/nixos-config) 137 | - [Nix config by disassembler](https://github.com/disassembler/network) 138 | - [git-hook config](https://github.com/cachix/git-hooks.nix/blob/master/template/flake.nix) 139 | -------------------------------------------------------------------------------- /modules/services/home-automation/default.nix: -------------------------------------------------------------------------------- 1 | # home automation 2 | { 3 | config, 4 | lib, 5 | pkgs, 6 | ... 7 | }: 8 | let 9 | cfg = config.my.services.home-automation; 10 | inherit (config.networking) domain; 11 | in 12 | { 13 | options.my.services.home-automation = { 14 | enable = lib.mkEnableOption "home-assistant server"; 15 | 16 | package = lib.mkPackageOption pkgs "home-assistant" { }; 17 | 18 | extraComponents = lib.mkOption { 19 | type = lib.types.listOf (lib.types.enum cfg.package.availableComponents); 20 | example = lib.literalExpression '' 21 | [ 22 | "analytics" 23 | "default_config" 24 | "esphome" 25 | "my" 26 | "wled" 27 | ] 28 | ''; 29 | default = [ ]; 30 | description = '' 31 | List 32 | of [ components ] 33 | (https://www.home-assistant.io/integrations/) 34 | that 35 | have 36 | their 37 | dependencies 38 | included in the package. 39 | 40 | The component name can be found in the URL, for example `https://www.home-assistant.io/integrations/ffmpeg/` would map to `ffmpeg`. 41 | ''; 42 | }; 43 | 44 | latitude = lib.mkOption { 45 | type = lib.types.nullOr (lib.types.either lib.types.float lib.types.str); 46 | default = null; 47 | example = 52.3; 48 | description = '' 49 | your location latitude. Impacts sunrise data. 50 | ''; 51 | }; 52 | 53 | longitude = lib.mkOption { 54 | type = lib.types.nullOr (lib.types.either lib.types.float lib.types.str); 55 | default = null; 56 | example = 4.9; 57 | description = '' 58 | your location longitude. Impacts sunrise data. 59 | ''; 60 | }; 61 | 62 | elevation = lib.mkOption { 63 | type = lib.types.nullOr (lib.types.either lib.types.float lib.types.str); 64 | default = null; 65 | description = '' 66 | your location elevation. Impacts sunrise data. 67 | ''; 68 | }; 69 | 70 | timezone = lib.mkOption { 71 | type = lib.types.str; 72 | default = "GMT"; 73 | description = '' 74 | your timezone. 75 | ''; 76 | }; 77 | }; 78 | 79 | config = lib.mkIf cfg.enable { 80 | services = { 81 | home-assistant = { 82 | enable = true; 83 | 84 | inherit (cfg) package; 85 | 86 | config = { 87 | default_config = { }; 88 | homeassistant = { 89 | name = "Home"; 90 | inherit (cfg) latitude longitude elevation; 91 | unit_system = "metric"; 92 | time_zone = cfg.timezone; 93 | external_url = "https://automation.${domain}"; 94 | internal_url = "http://localhost:${toString config.services.home-assistant.config.http.server_port}"; 95 | }; 96 | http = { 97 | use_x_forwarded_for = true; 98 | trusted_proxies = [ 99 | "127.0.0.1" 100 | "::1" 101 | ]; 102 | }; 103 | prometheus.requires_auth = false; 104 | }; 105 | 106 | extraComponents = [ 107 | "backup" 108 | "esphome" 109 | "shelly" 110 | "prometheus" 111 | ] 112 | ++ cfg.extraComponents; 113 | }; 114 | 115 | prometheus.scrapeConfigs = [ 116 | { 117 | job_name = "home-assistant"; 118 | metrics_path = "/api/prometheus"; 119 | static_configs = [ 120 | { 121 | targets = [ "localhost:${toString config.services.home-assistant.config.http.server_port}" ]; 122 | labels = { 123 | instance = config.networking.hostName; 124 | }; 125 | } 126 | ]; 127 | } 128 | ]; 129 | 130 | esphome.enable = true; 131 | }; 132 | 133 | my.services.prometheus.rules = { 134 | homeassistant = { 135 | condition = ''homeassistant_entity_available{domain="persistent_notification", entity!~"persistent_notification.http_login|persistent_notification.recorder_database_migration"} >= 0''; 136 | description = "homeassistant notification {{$labels.entity}} ({{$labels.friendly_name}}): {{$value}}"; 137 | }; 138 | }; 139 | 140 | my.services.webserver.virtualHosts = [ 141 | { 142 | subdomain = "automation"; 143 | port = config.services.home-assistant.config.http.server_port; 144 | } 145 | { 146 | subdomain = "esphome"; 147 | inherit (config.services.esphome) port; 148 | } 149 | ]; 150 | 151 | webapps.apps = { 152 | home-assistant = { 153 | dashboard = { 154 | name = "Home-Automation"; 155 | category = "infra"; 156 | icon = "house-signal"; 157 | url = "https://automation.${domain}"; 158 | method = "get"; 159 | }; 160 | }; 161 | esphome = { 162 | dashboard = { 163 | name = "Home-Firmware"; 164 | category = "infra"; 165 | icon = "house-laptop"; 166 | url = "https://esphome.${domain}"; 167 | method = "get"; 168 | }; 169 | }; 170 | }; 171 | }; 172 | } 173 | -------------------------------------------------------------------------------- /modules/services/alertmanager/default.nix: -------------------------------------------------------------------------------- 1 | # monitoring system services 2 | { 3 | config, 4 | lib, 5 | pkgs, 6 | ... 7 | }: 8 | let 9 | cfg = config.my.services.alertmanager; 10 | inherit (config.networking) domain; 11 | in 12 | { 13 | options.my.services.alertmanager = { 14 | enable = lib.mkEnableOption "Prometheus alertmanager for monitoring"; 15 | }; 16 | 17 | config = lib.mkIf cfg.enable { 18 | assertions = [ 19 | { 20 | assertion = config.services.prometheus.enable; 21 | message = '' 22 | Enable alertmanager without prometheus does not work. Please enable prometheus as well. 23 | ''; 24 | } 25 | ]; 26 | 27 | services = { 28 | prometheus = { 29 | alertmanager = { 30 | enable = true; 31 | configuration = import ./config.nix; 32 | webExternalUrl = "https://alerts.${domain}"; 33 | # fix issue: https://gitlab.com/gitlab-org/omnibus-gitlab/-/issues/4556 34 | extraFlags = [ 35 | "--cluster.advertise-address 127.0.0.1:${toString config.services.prometheus.alertmanager.port}" 36 | ]; 37 | }; 38 | 39 | alertmanagers = [ 40 | { 41 | static_configs = [ 42 | { targets = [ "localhost:${toString config.services.prometheus.alertmanager.port}" ]; } 43 | ]; 44 | } 45 | ]; 46 | scrapeConfigs = [ 47 | { 48 | job_name = "alertmanager"; 49 | static_configs = [ 50 | { 51 | targets = [ "localhost:${toString config.services.prometheus.alertmanager.port}" ]; 52 | labels = { 53 | instance = config.networking.hostName; 54 | }; 55 | } 56 | ]; 57 | } 58 | ]; 59 | }; 60 | 61 | grafana.provision = { 62 | datasources.settings.datasources = [ 63 | { 64 | name = "Alertmanager"; 65 | type = "alertmanager"; 66 | url = "http://localhost:${toString config.services.prometheus.alertmanager.port}"; 67 | jsonData = { 68 | implementation = "prometheus"; 69 | handleGrafanaManagedAlerts = config.services.prometheus.enable; 70 | }; 71 | } 72 | ]; 73 | }; 74 | 75 | grafana.provision = { 76 | dashboards.settings.providers = [ 77 | { 78 | name = "Alertmanager"; 79 | options.path = pkgs.grafana-dashboards.alertmanager; 80 | disableDeletion = true; 81 | } 82 | ]; 83 | }; 84 | 85 | go-neb.config.services = [ 86 | { 87 | ID = "alertmanager_service"; 88 | Type = "alertmanager"; 89 | UserId = config.my.services.matrix-bot.Username; 90 | Config = { 91 | # url contains "alertmanager_service" encoded as base64 92 | webhook_url = "http://localhost:4050/services/hooks/YWxlcnRtYW5hZ2VyX3NlcnZpY2U"; 93 | rooms = { 94 | "${config.my.services.matrix-bot.RoomID}" = { 95 | #bots:nixos.org 96 | text_template = '' 97 | {{range .Alerts -}} [{{ .Status }}] {{index .Labels "alertname" }}: {{index .Annotations "description"}} {{ end -}} 98 | ''; 99 | # $$severity otherwise envsubst replaces $severity with an empty string 100 | html_template = '' 101 | {{range .Alerts -}} 102 | {{ $$severity := index .Labels "severity" }} 103 | {{ if eq .Status "firing" }} 104 | {{ if eq $$severity "critical"}} 105 | [FIRING - CRITICAL] 106 | {{ else if eq $$severity "warning"}} 107 | [FIRING - WARNING] 108 | {{ else }} 109 | [FIRING - {{ $$severity }}] 110 | {{ end }} 111 | {{ else }} 112 | [RESOLVED] 113 | {{ end }} 114 | {{ index .Labels "alertname"}}: {{ index .Annotations "summary"}} 115 | ( 116 | 📈 Grafana, 117 | 🔥 Prometheus, 118 | 🔕 Silence 119 | )
120 | {{end -}}''; 121 | msg_type = "m.text"; # Must be either `m.text` or `m.notice` 122 | }; 123 | }; 124 | }; 125 | } 126 | ]; 127 | }; 128 | 129 | my.services.prometheus.rules = { 130 | alerts_silences_changed = { 131 | condition = ''abs(delta(alertmanager_silences{state="active"}[1h])) >= 1''; 132 | description = "alertmanager: number of active silences has changed: {{$value}}"; 133 | }; 134 | }; 135 | 136 | my.services.webserver.virtualHosts = [ 137 | { 138 | subdomain = "alerts"; 139 | inherit (config.services.prometheus.alertmanager) port; 140 | } 141 | ]; 142 | 143 | webapps.apps = { 144 | alertmanager.dashboard = { 145 | name = "Alerting"; 146 | category = "infra"; 147 | icon = "bell"; 148 | url = "https://alerts.${domain}"; 149 | method = "get"; 150 | }; 151 | }; 152 | }; 153 | } 154 | -------------------------------------------------------------------------------- /modules/services/blackbox/default.nix: -------------------------------------------------------------------------------- 1 | # monitor urls 2 | { 3 | config, 4 | lib, 5 | pkgs, 6 | ... 7 | }: 8 | let 9 | cfg = config.my.services.blackbox; 10 | blackBoxConfig = { 11 | modules = { 12 | http_2xx = { 13 | prober = "http"; 14 | http.preferred_ip_protocol = "ip4"; 15 | }; 16 | ssh_banner = { 17 | prober = "tcp"; 18 | tcp.query_response = [ 19 | { send = "SSH-2.0-blackbox-ssh-check"; } 20 | { expect = "^SSH-2.0-"; } 21 | ]; 22 | }; 23 | }; 24 | }; 25 | in 26 | { 27 | options.my.services.blackbox = { 28 | enable = lib.mkEnableOption "Blackbox prometheus exporter"; 29 | 30 | http_endpoints = lib.mkOption { 31 | type = lib.types.listOf lib.types.str; 32 | default = [ ]; 33 | example = lib.literalExpression '' 34 | [ 35 | "https://domain.com" 36 | "https://another-domain.com" 37 | ] 38 | ''; 39 | description = '' 40 | List of domains to test. 41 | ''; 42 | }; 43 | }; 44 | 45 | config = lib.mkIf cfg.enable { 46 | services = { 47 | prometheus.exporters.blackbox = { 48 | enable = true; 49 | configFile = pkgs.writeText "blackbox-config.yml" (builtins.toJSON blackBoxConfig); 50 | }; 51 | 52 | # relabels as in https://github.com/prometheus/blackbox_exporter#prometheus-configuration 53 | prometheus = { 54 | scrapeConfigs = [ 55 | { 56 | job_name = "blackbox"; 57 | metrics_path = "/probe"; 58 | params.module = [ "http_2xx" ]; 59 | static_configs = [ 60 | { 61 | targets = cfg.http_endpoints; 62 | labels = { 63 | instance = config.networking.hostName; 64 | }; 65 | } 66 | ]; 67 | relabel_configs = [ 68 | { 69 | source_labels = [ "__address__" ]; 70 | target_label = "__param_target"; 71 | } 72 | { 73 | source_labels = [ "__param_target" ]; 74 | target_label = "instance"; 75 | } 76 | { 77 | target_label = "__address__"; 78 | replacement = "localhost:${toString config.services.prometheus.exporters.blackbox.port}"; 79 | } 80 | ]; 81 | } 82 | ]; 83 | }; 84 | grafana.provision.dashboards.settings.providers = [ 85 | { 86 | name = "Blackbox"; 87 | options.path = pkgs.grafana-dashboards.blackbox; 88 | disableDeletion = true; 89 | } 90 | ]; 91 | }; 92 | 93 | my.services.prometheus.rules = { 94 | BlackboxProbeFailed = { 95 | condition = ''probe_success == 0''; 96 | description = "Blackbox probe failed (instance {{ $labels.instance }}): {{$value}}"; 97 | time = "1m"; 98 | labels = { 99 | severity = "critical"; 100 | }; 101 | }; 102 | BlackboxConfigurationReloadFailure = { 103 | condition = ''blackbox_exporter_config_last_reload_successful != 1''; 104 | description = "Blackbox configuration reload failure\n VALUE = {{ $value }}\n LABELS = {{ $labels }}"; 105 | time = "0m"; 106 | labels = { 107 | severity = "warning"; 108 | }; 109 | }; 110 | BlackboxSlowProbe = { 111 | condition = ''avg_over_time(probe_duration_seconds[1m]) > 2''; 112 | description = "Blackbox probe took more than 2s to complete\n VALUE = {{ $value }}\n LABELS = {{ $labels }}"; 113 | time = "1m"; 114 | labels = { 115 | severity = "warning"; 116 | }; 117 | }; 118 | BlackboxProbeHttpFailure = { 119 | condition = ''probe_http_status_code <= 199 OR probe_http_status_code >= 400''; 120 | description = "HTTP status code is not 200-399\n VALUE = {{ $value }}\n LABELS = {{ $labels }}"; 121 | time = "1m"; 122 | }; 123 | BlackboxSslCertificateWillExpireSoon = { 124 | condition = ''3 <= round((last_over_time(probe_ssl_earliest_cert_expiry[10m]) - time()) / 86400, 0.1) < 20''; 125 | description = "SSL certificate expires in less than 20 days\n VALUE = {{ $value }}\n LABELS = {{ $labels }}"; 126 | time = "0m"; 127 | labels = { 128 | severity = "warning"; 129 | }; 130 | }; 131 | BlackboxSslCertificateWillExpireShortly = { 132 | condition = ''0 <= round((last_over_time(probe_ssl_earliest_cert_expiry[10m]) - time()) / 86400, 0.1) < 3''; 133 | description = "SSL certificate expires in less than 3 days\n VALUE = {{ $value }}\n LABELS = {{ $labels }}"; 134 | time = "0m"; 135 | labels = { 136 | severity = "critical"; 137 | }; 138 | }; 139 | BlackboxProbeSlowHttp = { 140 | condition = ''avg_over_time(probe_http_duration_seconds[1m]) > 2''; 141 | description = "HTTP request took more than 2s\n VALUE = {{ $value }}\n LABELS = {{ $labels }}"; 142 | time = "1m"; 143 | labels = { 144 | severity = "warning"; 145 | }; 146 | }; 147 | BlackboxProbeSlowPing = { 148 | condition = ''avg_over_time(probe_icmp_duration_seconds[1m]) > 1''; 149 | description = "Blackbox ping took more than 1s\n VALUE = {{ $value }}\n LABELS = {{ $labels }}"; 150 | time = "1m"; 151 | labels = { 152 | severity = "warning"; 153 | }; 154 | }; 155 | }; 156 | }; 157 | } 158 | -------------------------------------------------------------------------------- /pkgs/grafana-dashboards/default.nix: -------------------------------------------------------------------------------- 1 | { pkgs }: 2 | 3 | with pkgs; 4 | 5 | let 6 | inherit (pkgs) stdenv fetchurl; 7 | in 8 | 9 | lib.makeScope pkgs.newScope ( 10 | _self: 11 | let 12 | buildGrafanaDashboard = 13 | args: 14 | stdenv.mkDerivation ( 15 | args 16 | // { 17 | pname = "grafana-dashboard-${args.pname}-${toString args.id}"; 18 | inherit (args) version; 19 | src = fetchurl { 20 | url = "https://grafana.com/api/dashboards/${toString args.id}/revisions/${args.version}/download"; 21 | inherit (args) hash; 22 | }; 23 | dontUnpack = true; 24 | installPhase = '' 25 | runHook preInstall 26 | mkdir -p $out 27 | cp $src $out/${args.pname}-${toString args.id}.json 28 | runHook postInstall 29 | ''; 30 | } 31 | ); 32 | in 33 | { 34 | inherit buildGrafanaDashboard; 35 | 36 | node-exporter = buildGrafanaDashboard { 37 | id = 1860; 38 | pname = "node-exporter-full"; 39 | version = "37"; 40 | hash = "sha256-1DE1aaanRHHeCOMWDGdOS1wBXxOF84UXAjJzT5Ek6mM="; 41 | }; 42 | node-systemd = 43 | (buildGrafanaDashboard { 44 | id = 1617; 45 | pname = "node-systemd"; 46 | version = "1"; 47 | hash = "sha256-MEWU5rIqlbaGu3elqdSoMZfbk67WDnH0VWuC8FqZ8v8="; 48 | }).overrideAttrs 49 | (_: { 50 | src = ./node-systemd.json; # sadly only imported dashboards work 51 | }); 52 | 53 | nextcloud = 54 | (buildGrafanaDashboard { 55 | id = 9632; 56 | pname = "nextcloud"; 57 | version = "1"; 58 | hash = "sha256-Z28Q/sMg3jxglkszAs83IpL8f4p9loNnTQzjc3S/SAQ="; 59 | }).overrideAttrs 60 | (_: { 61 | src = ./nextcloud.json; # sadly only imported dashboards work 62 | }); 63 | 64 | blocky = buildGrafanaDashboard { 65 | id = 13768; 66 | pname = "blocky"; 67 | version = "3"; 68 | hash = "sha256-T1HqWbwt+i/Wa+Y2B7hcl3CijGxZF5aI38aPcXjk9y0="; 69 | }; 70 | 71 | navidrome = 72 | (buildGrafanaDashboard { 73 | id = 18038; 74 | pname = "navidrome"; 75 | version = "1"; 76 | hash = "sha256-MU890UAEI9wrnVIC/R0HkYwFa6mJ8Y7ESAWuaSQ8FQ8="; 77 | }).overrideAttrs 78 | (_: { 79 | src = ./navidrome.json; # sadly data source is not detected 80 | }); 81 | 82 | # taken from https://gitlab.archlinux.org/archlinux/infrastructure/-/blob/master/roles/grafana/files/dashboards/Hedgedoc.json?ref_type=heads 83 | hedgedoc = 84 | (buildGrafanaDashboard { 85 | id = -1; 86 | pname = "hedgedoc"; 87 | version = "1"; 88 | hash = lib.fakeSha256; 89 | }).overrideAttrs 90 | (_: { 91 | src = ./hedgedoc.json; # sadly data source is not detected 92 | }); 93 | 94 | cadvisor = buildGrafanaDashboard { 95 | id = 10619; 96 | pname = "cadvisor"; 97 | version = "1"; 98 | hash = "sha256-T1HqWbwt+i/Wa+Y2B7hclaCijGxZF5QI38aPcXjk9y0="; 99 | }; 100 | 101 | loki = 102 | (buildGrafanaDashboard { 103 | id = 13407; 104 | pname = "loki"; 105 | version = "1"; 106 | hash = "sha256-1sxTDSEwi2O/Ce+rWqqhMvsYEJeELBfkb9W2R6cDjcU="; 107 | }).overrideAttrs 108 | (_: { 109 | src = ./loki.json; # sadly not yet updated to latest grafana 110 | }); 111 | 112 | alertmanager = buildGrafanaDashboard { 113 | id = 9578; 114 | pname = "alertmanager"; 115 | version = "4"; 116 | hash = "sha256-/scCKBKqTjRKKImIrEYLBKGweOUnkx+QsD5yLfdXW5o="; 117 | }; 118 | 119 | forgejo = 120 | (buildGrafanaDashboard { 121 | id = 13192; 122 | pname = "forgejo"; 123 | version = "1"; 124 | hash = "sha256-IAaI/HvMxcWE3PGQFK8avNjgj88DgcDvkWRcDAWSejM="; 125 | }).overrideAttrs 126 | (_: { 127 | src = ./forgejo.json; # sadly not yet updated to latest grafana 128 | }); 129 | 130 | prometheus = 131 | (buildGrafanaDashboard { 132 | id = 3662; 133 | pname = "prometheus"; 134 | version = "2"; 135 | hash = "sha256-+nsi8/dYNvGVGV+ftfO1gSAQbO5GpZwW480T5mHMM4Q="; 136 | }).overrideAttrs 137 | (_: { 138 | src = ./prometheus.json; # sadly only imported dashboards work 139 | }); 140 | 141 | grafana = 142 | (buildGrafanaDashboard { 143 | id = 20138; 144 | pname = "grafana"; 145 | version = "1"; 146 | }).overrideAttrs 147 | (_: { 148 | src = ./grafana.json; # sadly only imported dashboards work 149 | }); 150 | 151 | blackbox = 152 | (buildGrafanaDashboard { 153 | id = 13659; 154 | pname = "blackbox"; 155 | version = "1"; 156 | hash = "sha256-nnBFWFDAqKUqTOYxOrkRPlVla4ioQZ6rqEqakdzUj1Q="; 157 | }).overrideAttrs 158 | (_: { 159 | src = ./blackbox.json; # sadly only imported dashboards work 160 | }); 161 | 162 | fritzbox = buildGrafanaDashboard { 163 | id = 17751; 164 | pname = "fritzbox"; 165 | version = "1"; 166 | hash = "sha256-foc00LtcBTXOzvwyddJO0QJkCP82bUXa5iMuQmHqKcg="; 167 | }; 168 | 169 | caddy = 170 | (buildGrafanaDashboard { 171 | id = 20802; 172 | pname = "caddy-monitoring"; 173 | version = "1"; 174 | hash = "sha256-vSt63PakGp5NzKFjbU5Yh0nDbKET5QRWp5nusM76/O4="; 175 | }).overrideAttrs 176 | (_: { 177 | src = ./caddy.json; # sadly only imported dashboards work 178 | }); 179 | } 180 | ) 181 | -------------------------------------------------------------------------------- /modules/services/nextcloud/default.nix: -------------------------------------------------------------------------------- 1 | # self-hosted cloud 2 | { 3 | config, 4 | lib, 5 | pkgs, 6 | ... 7 | }: 8 | let 9 | cfg = config.my.services.nextcloud; 10 | inherit (config.networking) domain; 11 | in 12 | { 13 | options.my.services.nextcloud = { 14 | enable = lib.mkEnableOption "Nextcloud"; 15 | maxSize = lib.mkOption { 16 | type = lib.types.str; 17 | default = "10G"; 18 | example = "512M"; 19 | description = "Maximum file upload size"; 20 | }; 21 | admin = lib.mkOption { 22 | type = lib.types.str; 23 | default = "felix"; 24 | example = "admin"; 25 | description = "Name of the admin user"; 26 | }; 27 | default_phone_region = lib.mkOption { 28 | type = lib.types.str; 29 | default = "DE"; 30 | example = "US"; 31 | description = "country codes for automatic phone-number "; 32 | }; 33 | passwordFile = lib.mkOption { 34 | type = lib.types.path; 35 | example = "/var/lib/nextcloud/password.txt"; 36 | description = '' 37 | Path to a file containing the admin's password, must be readable by 38 | 'nextcloud' user. 39 | ''; 40 | }; 41 | 42 | exporterPasswordFile = lib.mkOption { 43 | type = lib.types.path; 44 | example = "/var/lib/nextcloud/password.txt"; 45 | description = '' 46 | Path to a file containing the admin's password, must be readable by 47 | 'nextcloud' user. 48 | ''; 49 | }; 50 | }; 51 | 52 | config = lib.mkIf cfg.enable { 53 | services = { 54 | nextcloud = { 55 | enable = true; 56 | package = pkgs.nextcloud32; 57 | hostName = "cloud.${domain}"; 58 | maxUploadSize = cfg.maxSize; 59 | autoUpdateApps.enable = true; 60 | settings = { 61 | inherit (cfg) default_phone_region; 62 | overwriteprotocol = "https"; # nginx only allows SSL 63 | }; 64 | config = { 65 | adminuser = cfg.admin; 66 | adminpassFile = cfg.passwordFile; 67 | 68 | dbtype = "sqlite"; 69 | #dbtype = "pgsql"; 70 | #dbhost = "/run/postgresql"; 71 | }; 72 | 73 | extraApps = with config.services.nextcloud.package.packages.apps; { 74 | inherit 75 | calendar 76 | contacts 77 | tasks 78 | deck 79 | ; 80 | }; 81 | extraAppsEnable = true; 82 | }; 83 | 84 | #postgresql = { 85 | # enable = true; 86 | # ensureDatabases = [ "nextcloud" ]; 87 | # ensureUsers = [ 88 | # { 89 | # name = "nextcloud"; 90 | # ensurePermissions."DATABASE nextcloud" = "ALL PRIVILEGES"; 91 | # } 92 | # ]; 93 | #}; 94 | 95 | prometheus.exporters.nextcloud = { 96 | enable = true; 97 | url = "https://cloud.${domain}"; 98 | username = cfg.admin; 99 | passwordFile = cfg.exporterPasswordFile; 100 | }; 101 | 102 | prometheus.scrapeConfigs = [ 103 | { 104 | job_name = "nextcloud"; 105 | static_configs = [ 106 | { 107 | targets = [ "localhost:${toString config.services.prometheus.exporters.nextcloud.port}" ]; 108 | labels = { 109 | instance = config.networking.hostName; 110 | }; 111 | } 112 | ]; 113 | } 114 | ]; 115 | grafana.provision = { 116 | dashboards.settings.providers = [ 117 | { 118 | name = "Nextcloud"; 119 | options.path = pkgs.grafana-dashboards.nextcloud; 120 | disableDeletion = true; 121 | } 122 | ]; 123 | }; 124 | }; 125 | 126 | #systemd.services."nextcloud-setup" = { 127 | # requires = [ "postgresql.service" ]; 128 | # after = [ "postgresql.service" ]; 129 | #}; 130 | services.phpfpm.pools.nextcloud.settings = { 131 | "listen.owner" = config.services.caddy.user; 132 | "listen.group" = config.services.caddy.group; 133 | }; 134 | 135 | users.groups.nextcloud.members = [ 136 | "nextcloud" 137 | config.services.caddy.user 138 | ]; 139 | 140 | my.services.webserver.virtualHosts = [ 141 | { 142 | subdomain = "cloud"; 143 | extraConfig = '' 144 | redir /.well-known/carddav /remote.php/dav/ 301 145 | redir /.well-known/caldav /remote.php/dav/ 301 146 | 147 | @forbidden { 148 | path /.htaccess 149 | path /data/* 150 | path /config/* 151 | path /db_structure 152 | path /.xml 153 | path /README 154 | path /3rdparty/* 155 | path /lib/* 156 | path /templates/* 157 | path /occ 158 | path /console.php 159 | } 160 | respond @forbidden 403 161 | 162 | header { 163 | X-Frame-Options "sameorigin" 164 | X-Permitted-Cross-Domain-Policies "none" 165 | } 166 | 167 | root * ${config.services.nextcloud.finalPackage} 168 | file_server 169 | php_fastcgi unix/${config.services.phpfpm.pools."nextcloud".socket} { 170 | root ${config.services.nextcloud.finalPackage} 171 | env front_controller_active true 172 | env modHeadersAvailable true 173 | } 174 | ''; 175 | } 176 | ]; 177 | 178 | my.services.backup = { 179 | exclude = [ 180 | # image previews can take up a lot of space 181 | "${config.services.nextcloud.home}/data/appdata_*/preview" 182 | ]; 183 | }; 184 | 185 | webapps.apps.nextcloud = { 186 | dashboard = { 187 | name = "Cloud"; 188 | category = "media"; 189 | icon = "cloud"; 190 | url = "https://cloud.${domain}/login"; 191 | }; 192 | }; 193 | }; 194 | } 195 | -------------------------------------------------------------------------------- /modules/services/loki/default.nix: -------------------------------------------------------------------------------- 1 | # log monitoring 2 | { 3 | config, 4 | lib, 5 | pkgs, 6 | ... 7 | }: 8 | let 9 | cfg = config.my.services.loki; 10 | # no default port defined in nixpkgs 11 | port = 3101; 12 | in 13 | { 14 | options.my.services.loki = { 15 | enable = lib.mkEnableOption "loki log monitoring"; 16 | 17 | rules = lib.mkOption { 18 | type = lib.types.attrsOf ( 19 | lib.types.submodule { 20 | options = { 21 | condition = lib.mkOption { 22 | type = lib.types.str; 23 | description = '' 24 | Loki alert expression. 25 | ''; 26 | example = ''count_over_time({job=~"secure"} |="sshd[" |~": Failed|: Invalid|: Connection closed by authenticating user" | __error__="" [15m]) > 15''; 27 | default = null; 28 | }; 29 | description = lib.mkOption { 30 | type = lib.types.str; 31 | description = '' 32 | Loki alert message. 33 | ''; 34 | example = "Prometheus encountered value {{ $value }} with {{ $labels }}"; 35 | default = null; 36 | }; 37 | labels = lib.mkOption { 38 | type = lib.types.nullOr (lib.types.attrsOf lib.types.str); 39 | description = '' 40 | Additional alert labels. 41 | ''; 42 | example = lib.literalExpression '' 43 | { severity = "page" }; 44 | ''; 45 | default = { }; 46 | }; 47 | time = lib.mkOption { 48 | type = lib.types.str; 49 | description = '' 50 | Time until the alert is fired. 51 | ''; 52 | example = "5m"; 53 | default = "2m"; 54 | }; 55 | }; 56 | } 57 | ); 58 | description = '' 59 | Defines the loki rules. 60 | ''; 61 | default = { }; 62 | }; 63 | }; 64 | 65 | config = 66 | let 67 | rulerConfig = { 68 | groups = [ 69 | { 70 | name = "alerting-rules"; 71 | rules = lib.mapAttrsToList (name: opts: { 72 | alert = name; 73 | inherit (opts) condition labels; 74 | for = opts.time; 75 | annotations.description = opts.description; 76 | }) cfg.rules; 77 | } 78 | ]; 79 | }; 80 | rulerFile = pkgs.writeText "ruler.yml" (builtins.toJSON rulerConfig); 81 | in 82 | lib.mkIf cfg.enable { 83 | services = { 84 | loki = { 85 | enable = true; 86 | configuration = { 87 | server = { 88 | http_listen_address = "localhost"; 89 | http_listen_port = port; 90 | }; 91 | auth_enabled = false; 92 | 93 | common = { 94 | instance_addr = "localhost"; 95 | ring.kvstore.store = "inmemory"; 96 | replication_factor = 1; 97 | 98 | path_prefix = config.services.loki.dataDir; 99 | storage.filesystem = { 100 | chunks_directory = "${config.services.loki.dataDir}/chunks"; 101 | rules_directory = "${config.services.loki.dataDir}/rules"; 102 | }; 103 | }; 104 | 105 | ruler = lib.mkIf config.my.services.alertmanager.enable { 106 | storage = { 107 | type = "local"; 108 | local.directory = "${config.services.loki.dataDir}/ruler"; 109 | }; 110 | rule_path = "${config.services.loki.dataDir}/rules"; 111 | alertmanager_url = "http://localhost:${toString config.services.prometheus.alertmanager.port}"; 112 | enable_alertmanager_v2 = true; 113 | }; 114 | 115 | schema_config = { 116 | configs = [ 117 | { 118 | from = "2020-11-08"; 119 | store = "tsdb"; 120 | object_store = "filesystem"; 121 | schema = "v13"; 122 | index = { 123 | prefix = "index_"; 124 | period = "24h"; 125 | }; 126 | } 127 | ]; 128 | }; 129 | 130 | limits_config = { 131 | max_query_lookback = "672h"; # 28 days 132 | retention_period = "672h"; # 28 days 133 | }; 134 | 135 | compactor = { 136 | working_directory = "${config.services.loki.dataDir}/compactor"; 137 | retention_enabled = true; 138 | delete_request_store = "filesystem"; 139 | }; 140 | }; 141 | }; 142 | 143 | grafana.provision = { 144 | datasources.settings.datasources = [ 145 | { 146 | name = "Loki"; 147 | type = "loki"; 148 | access = "proxy"; 149 | url = "http://localhost:${toString port}"; 150 | } 151 | ]; 152 | dashboards.settings.providers = [ 153 | { 154 | name = "Loki"; 155 | options.path = pkgs.grafana-dashboards.loki; 156 | disableDeletion = true; 157 | } 158 | ]; 159 | }; 160 | 161 | prometheus = { 162 | scrapeConfigs = [ 163 | { 164 | job_name = "loki"; 165 | static_configs = [ 166 | { 167 | targets = [ "localhost:${toString port}" ]; 168 | labels = { 169 | instance = config.networking.hostName; 170 | }; 171 | } 172 | ]; 173 | } 174 | ]; 175 | }; 176 | }; 177 | 178 | systemd.tmpfiles.rules = [ 179 | "d /var/lib/loki 0700 loki loki - -" 180 | "d /var/lib/loki/ruler 0700 loki loki - -" 181 | "d /var/lib/loki/rules 0700 loki loki - -" 182 | "L /var/lib/loki/ruler/ruler.yml - - - - ${rulerFile}" 183 | ]; 184 | systemd.services.loki.reloadTriggers = [ rulerFile ]; 185 | 186 | my.services.loki.rules = { 187 | loki_highLogRate = { 188 | condition = ''sum by (host) (rate({unit="loki.service"}[1m])) > 60''; 189 | description = "Loki has a high logging rate"; 190 | }; 191 | }; 192 | }; 193 | } 194 | -------------------------------------------------------------------------------- /machines/newton/secrets.yaml: -------------------------------------------------------------------------------- 1 | users: 2 | felix: 3 | password: ENC[AES256_GCM,data:3c0MIwAJsWO3NdZPyByN9YYvR25BDaTFmeVX0gSgBjEsRbE2Qzy+sw/0UJ1UIm8wMszVOiK3eTwGnx7gzVZH6oTbprP4nHJnmQ==,iv:eOW99ZmSDrwZUHeF7PlM6+bV/LcHctw1QWcKBU1Bhz0=,tag:hDwITip+mRIroHUeqmUinQ==,type:str] 4 | borgbackup: 5 | password: ENC[AES256_GCM,data:MCzHfmd5uSIO6NqOLljVEce6O2btQoc=,iv:CGGvivLFcsUSQKT1EkY72uDVM9+No25WlXjL/WryuEU=,tag:wZOV8Clot5Df+mMf71thZA==,type:str] 6 | ssh_key: ENC[AES256_GCM,data:Tznd7jifHBRcJY1kISOVGnmXYuHM8GBEaz4XdPfjf/uNFdloNy8TkBSwKoAmtHn26z0UI+bjtZLvRkSqdyZSD37uILLNxQg3pLO68i1CPduOD2enDWri0pIL3bLM7YA6Mh36cW1jnz1v+7oz6sQLZesX4RmtMgBsomBjWH3OAXdERHRKCSKtCIdvLwORJY1K2C+WcizjYdQIEqDP3V4h+nPkj0Ifc3c3TWCCk7jAHH7mjcOyGsdfls3SGErMTFTCyOumi9TPcABfcXTgy6VGr5NQSjO7NOAMshgZsOs6ePSDQCicPRsYYxuHhPID83AXnx/T414xLrdXAa2ms+9LSYxe+2TUpNMEUbTm+j7fpgv24KN/qbAEKT51Jk5MBVyGu+T95l5F+c2063wyqhHt0bOc3OvZw7TNI55JmrFteqDQ791+KByRXK6U4D1tmnCV5mtEAGM74GNg28RRJPxWWia2oB9q1XuCFrROdHHrsmek0IDzHJoUFzyzFefFlqF7Mkz8XRG21GWXGUxZWxqg,iv:W/J5jkwuOqS5TO4E3O5FXjG4zzJ9v1ulv5WLsfOPyek=,tag:nohAtERvEMxhzQs7NNcLTg==,type:str] 7 | syncthing: 8 | key: ENC[AES256_GCM,data:r4kihzg6FcZYHqchcu+8k5N27ZwdrXkQXyJ1QM2p0kVSiwTPq5n1jQCI8i7bCXckBkEv5IQmSOc3kf9383EM3XH3Q9zDqw2z4TEHWo6EBI9JalPK/4UC6gRpN5bTJRpwUlZ5gnhM6ifcl+OX6bdU6zSS2MtFqL1ne/CVJdUoGLHV4BKNCF43CBgIcJG+o/iOt0P292/PozrRRL3nCHxB8TCIpfUDjyeQYGAvBuQy1/WDUJoBLFIOKVYmzo5jbGwScVrUIg2hT+KIuv4P4ZVkBBgx0avOi9JlTglOh2kS//+F0+nT7+XQV/Fo/AwSE4Y1MBZCaxtlb+C89DRcrR5oqFbPLA11oynPHrMryPgyikrtRKDnN52/8UNVMaJgK04E,iv:w3ItAGO/LZxqtLNLu9R0nrcUezIBwe9aFqY2kY9gY0s=,tag:shC2Qo9vF4o3I/RXjbTXoA==,type:str] 9 | cert: ENC[AES256_GCM,data:k3LEq2gEcoM3x1xEtFzbvbd04LRYvm15z5Cm/t07IG2fVvdtPWy9Dsha5Crx2CEnjB4Xtf7xdCOTaKr/OeZnUf+iK67e/xj6St1Q2n9ua20/+82nUxFtG2ItXH+iu5rkF7IIsXuVxE0WNAXxC8M+xdDt4KUOJP6e/GSv+/8KA1N4fOiW1N5EUpLwuN7sAwaj7YkzUJwHiGrUw3qTYRwXU6j/hWA3vtw8Z8latWW3+goaD9/aDSMHzSE5Pdi2/GQ1Ll/UVc43nRUYgXL7H9grOrXiI3sdPpc42MeFUpNM0AGJp3M6tepUsLIuA9L54wgqaQVrGO0RilBMtMrAb1tUoqLqEB0CM0VqNDBEWyIzDKIfHtQWta/1fIxzo5bKoJcJ6UG2qrZ5ru6sX387sGi8QcC9uN5ko6oZesdOacw9z5tvFDQrk7KvfiCtvHj+AtPxiLrkFj3EPoQK1vXzy4IyanIDJx46WtuAURTHmBZms2KBLPKLMYzWm9qdq8dW4sadbaSofzwquj4HaWuOIugSdjVlSdzBj+KI/nIRde5uWunpnr9AYHrf3lPgl1tkduQpL5RRu3je4RQ5dNa+TNkcqacMTF/3Q34uJXBkp99LpK+8fXx70PKm/PmnHOQgeK7WJnz2kAeSc6kBhX+sh2w1KWBG01ptc0Ixi7xrC/6fgB4pmkKdryMFsZ/PML/VOwLxbkbrzsHyK1Zpw8jV03oGTwz76IaDpELqpfX2rD3GztS58mYXxJn5sqyTIFKjLVX05jB77bjYk5pWLQZasHpdU5wLYg6AHCe2wEIfbu3y3M4XVEfCiB/d+P370T1qLMbxeTYMd1MtD4oq87Bg7brlCy64LWhOwW/Elu0oeBqHmpoXwBc6aoIBH8a+uK4ZWm87N/oJha1zzsh0+0Z3NdinAL4ufjrlKQBGJRZj7NBKdQfnKZXM/ELLEQD2R6UxZUsFnmrXEN2AuAv+KZtoImf1oOOGXiaIDPEJNvGlIT8fZ8cQViYRxzGjtkbO5p3FHpn+/DdcEke+/4ABqNZ0hHw+98U6qRlvcQ==,iv:80pejP5/F/uLoFuAwg1tKjwq5hQjowhpBT6/+ipPW0c=,tag:zccXZgI/KT7+DPbqyZWjOA==,type:str] 10 | paperless: 11 | password: ENC[AES256_GCM,data:GrH2MEFUGSoJEUnFUb5nTxHHnnSIohwEUVU+2Xpa,iv:U9tDsq5PsqFzzl1e1sYUL5XxUqGEmdiZoJtCh96+yEA=,tag:qVu2bulQ9wz+K0lmbMULzQ==,type:str] 12 | freshrss: 13 | password: ENC[AES256_GCM,data:dUOKeRxovwIHIchkwMFxsQYEKrU2muY=,iv:OA1zbIiV3NBWIoJLpxpLBEjR/I6m5vzVKvzMEZYYE7Q=,tag:r4PbEbEkSH3bsJMamDuuFw==,type:str] 14 | nextcloud: 15 | password: ENC[AES256_GCM,data:uE507Ij34zJVYnd2YkNCGj8hpFpEM5w=,iv:x8BNCUaAas0poQ/Lo0izZApF6l52xal8DDrClIzWjvk=,tag:sA08dmcVQbKswX9hF/txag==,type:str] 16 | nextcloud-exporter: 17 | password: ENC[AES256_GCM,data:ziQ/rVJx9NELSQA6/BeniH7joJPnc6Q=,iv:LtZ2Inm0V4ZRv0blRUNfXJOyFY85NkZo+5r31fs0oXM=,tag:5maJ5fkJU+JnbpLGf/MhOg==,type:str] 18 | git: 19 | password: ENC[AES256_GCM,data:uUN/BJ1JMwahgq6sqb+bWaIgolfu6pHq+pnt/2zl,iv:x8KjU4tNfK2xkxcIk9dkIs3j1V+CGY/rc4DbhEdoH+U=,tag:9lRP2CQHbAtrxvMAjqMJKg==,type:str] 20 | photos: 21 | secrets: ENC[AES256_GCM,data:eUlyoAx9odHPjuSSL63WFJJntRm77FvcxSzf8+6r6qNGtlnx,iv:IEq+NpZx4JIQ/hDCcNEHh+I2B7tD9BbbB7Y1XyW4yW0=,tag:+Ar83rwchWdaMvTYGHAoDQ==,type:str] 22 | grafana: 23 | password: ENC[AES256_GCM,data:OaAJs/asyZWIdKLaY7OlXPzoDeL4jaA=,iv:J7NwPmqjDj3d4LEB7DGEGDsPML+EXhvwWy3j6MyueNA=,tag:mPGOT7pi1lH+5Q6Mx+yAGQ==,type:str] 24 | sso: 25 | auth-key: ENC[AES256_GCM,data:jFDeymziDiJMnoIGjYPMmnxTzKer1bFffGDaoHnbKlpMPslP/Bmtsc5kio2tbDBlxG0TCdf+ePirPPw2,iv:8wGHEp1gB/qgkSvqkqjb9zBnqkkl1+Ezm9tCFS8tL3w=,tag:tHIT9Iw29TUXJm2e7z3Z/A==,type:str] 26 | felix: 27 | password-hash: ENC[AES256_GCM,data:4yOMOg3/RsijtXC7h73sIk3HDPq83xmSY7AaLeUbtPVZzjjZ/Qp+YiXpgL0fVsio1Pic3HyAzeVlWg0E,iv:23CdJQAhtpgrUZ1jVKiKYfgw5jQNVN6413Ew8ryJH6w=,tag:bds2Yd18gFtVDtgLkxMGkQ==,type:str] 28 | totp-secret: ENC[AES256_GCM,data:1fh22SICF19Svg6rLwUYs8oIBr0FV9dG30WWpPN751VbL104nQZcKnszbDox0hwuuotY7G8twYI=,iv:RrkNlJ9us8LDeQQUuexB1yvvYIbSElsB3VOm/4fBHzY=,tag:uv6Fwtk42atINufook7aAw==,type:str] 29 | matrix-bot: 30 | password: ENC[AES256_GCM,data:RmEcNdQXEQoFYRdDC4fP7+b59aqrWh39YPVI3Ej7MRlMVdUGjmLW4pjhsq98TTqaLefwX0RYW7AD6wo0ua1wLA==,iv:wZF3xTdM4XOIwaweOoHW9d52atjI3zFiQ19dVDj9BLw=,tag:m4KGfgQMKtRi266fovXQSA==,type:str] 31 | acme: 32 | inwx: ENC[AES256_GCM,data:tu3pdI2fAQPAwQNdIIk10Kkl0xBPBLx592UlYYEXxJX0YGddAhGMwDUqKTh/B5k9WW11cyoLoeTMnZPMzD15V1Qj,iv:UbbauJBjr3O4XnKRL9Pc3PdRGJqAhqO0PDNkvpyXH2o=,tag:YRVtRo8D0KLVCtWukW7GWw==,type:str] 33 | sops: 34 | age: 35 | - recipient: age1hf8m9upp00dr7qv2kmqdr50fpvd9ejzkfu8yknqnuda2aas2tvrs4l3u7m 36 | enc: | 37 | -----BEGIN AGE ENCRYPTED FILE----- 38 | YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBHOHlyV1B4SE9maDhmTVhB 39 | RWIzbkdhZzhwVjVuUEcxN3pQL2VNTU9Oa0VZCnN5YUhSZUVFTUVMS3dLRDExSFBW 40 | SHNNM0pKeDRITG5heXhrNVRFbFJ4WkEKLS0tIHR2a3RXcUNzbjZQNythTnk1T0d1 41 | bnlMMmY2NHRncEEvTlNob0JJU3dLdE0Kjo2Ge3OVnClKgAyUHvi//Qx74fqhtxjw 42 | 7IlsnpkmHbPPbmTWtcXwzS8S8/2tcurMj9mG1wrDou3POr/aHMPs/Q== 43 | -----END AGE ENCRYPTED FILE----- 44 | - recipient: age1s9spl75rwhgm3cvvqsr9rze5m0kuxqes2tsxjmq07xg5ycn5j47s2m0dlu 45 | enc: | 46 | -----BEGIN AGE ENCRYPTED FILE----- 47 | YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBKWmp0dGtsU3ZiMjN3SjU0 48 | OFpGdkJ1SitjMlJkSFVhZm5lYjZHUFRjYWh3ClFKM1d1RG9GbFg5V0dWOGs2WmQv 49 | OFZpTlU2V0xITUNmSXlyWlkwdHFmbHMKLS0tIHNKb1lSaTB6cnE3RytaRklpbzVx 50 | NmNwT3N5UEVabFdLTDhseFRjeVZaWFkKL3HGFqfttU1tXY4OhnIr1ABFsHB0R0CX 51 | s6wxb0ilut32ijjtnGXMIIa9y6XsMTpYskTb9FdRP9VnQQGVrMfdew== 52 | -----END AGE ENCRYPTED FILE----- 53 | lastmodified: "2025-06-06T18:36:39Z" 54 | mac: ENC[AES256_GCM,data:cXcCbvl+KgXniJUF70yH2PcQanptrBoY36FdtJjWz5TiNlPNbJCXprmrnetb3Cjwn9LzT+yxF29ZqLP4U5Z4dqfyFHUVHKugiAtvQFYPWiux8Tx9x582RS+R6LXtpZaxWY2Ho30kO/INI3n9FRkdwcCWvgbS5G0y4a8v/tbEhTU=,iv:ACa221uyygkE1yWgA1Eps0oTjd67z9nO12X5x3q1Vyo=,tag:YtydRTcualGTTX920iZVtA==,type:str] 55 | unencrypted_suffix: _unencrypted 56 | version: 3.10.2 57 | -------------------------------------------------------------------------------- /machines/serverle/secrets.yaml: -------------------------------------------------------------------------------- 1 | users: 2 | felix: 3 | password: ENC[AES256_GCM,data:nmGfgkhZrR+fUlsAJ55rxoRQbMbcGW31phFnCjvC5iI7bgkgH+ppo1ZFBsneCQoOp6TwScxDqyUIjUPY0bz5/iwIQy7+7Y/g+A==,iv:i7r7BVDV3MuIxndUrduRLZvSqvS50wdzSYLPWRZWRL0=,tag:nhWPG8TSvKntLdtrex1aZQ==,type:str] 4 | wifi: 5 | bismarck: ENC[AES256_GCM,data:6mcU+o6GiBelKq5Av2hJdHigf+yyQRO/qXV0DbGeri00CaO1cjjGka86C/t0NAoh8EaJeLPd6lZQspeUTIdqJMleQLhmq1zk1qGw65fvEphCsWkKUsUd6flWODdmll80mmYLpllX6hmZ8I/Of7EVZPn4WEiw4M2HB1UCw5+M0W2H0MJrjlskafbL9+uYtMeoUikXVMTYNAkX3+9+MZi3KzDMEc6JVt1fQZHjXkdeKx+2iVsaXEtXL72Dbj3ihlpA+WRhgZOmrrbablYde71mpzSuQ/yqeTZT+2NCMUmkYaj0dGz4FW0gJMPzjHjji8CopeOK7/CQM/MMWGFJXR2Xdea8X/YdDoVAz34WraGylYzK9e7a1C+s37mekt6c/bvuiQ==,iv:DH0LDw1pZ5m9RScJ3+IhjUb5JroLLOJ3jru7DwCKXv0=,tag:r57fz+W2nEybNqOc8eEDFA==,type:str] 6 | borgbackup: 7 | password: ENC[AES256_GCM,data:vXa1LO8Akwume5PVsxny59H5FDI+HTo=,iv:RKiriS4VHP3HDMRPS5e3Fz5rsDMpqQpZgC7XFI3hAWU=,tag:XHZNH4cLJzmSuF6k+gJ3cg==,type:str] 8 | ssh_key: ENC[AES256_GCM,data:HbGwAzKl4/+ubkwELA4wyC3c65gi7UzRZh5SAwa/H/Y8Yf2BM3JOL7dd/X1RanPrqV6SejQDgNpCc7MKzr+ptg8kITLZiHKESZjW+NEraV9v7JDEaxfPlL8KFzniG5dqaLHOKuBZ6CMmXnVOcsYg1ANQdLOFUCvxRpmi1HsV5x1EwkmZV/J0Za6C10sgqdGX1y5mYF3SP0WHdqSShFkS8oZaSOfA50pVL+BVlaxnrAUQaPC2QuRsv5kuV7QnwI0YQ4znNRcRI5AeUuv8dTt8NFoTeeytcM/22zSUwskTs/ity0V/o9+Z1/4RUnGcXQW7yI7Scf2Wcg/BwY0TpQhxYUEx0TM5mdLEgjTV66DSqbYpJu8NBCxIP8MMj97vwf6v1Y+p2/SdkkLrBop1vdb4eMtH38wCQCTrIhYx2T0M44X7Jq+l3FOng0+Sm30GXK4B5o97YCxEEZlrLSDquo5QP20EqrWWwdRIJboh6H6YDal3T5ptlZymAfzOIbT689SE8ZznT4cdcj6YZJB/2BQV,iv:kDNaLE93XCHQ7Hf6UqzTlSz0GxIhN/HN57iJ8Wnn3GQ=,tag:jA180VNe5l7OqLB4BPZ39w==,type:str] 9 | syncthing: 10 | key: ENC[AES256_GCM,data:SilpQP29PbI6JNGX5/Ij9YDdwN5a0rDRKicduLZizoIkwPM4zhs4PHlQDhf20Q9IE9OLxI+6Ik57e28iPY0KDL5M/Qg6sPvUaOZBriM24dCYnq8MQMncfI2AnmUSLbelSkoumxlkhOdMjPWNhh1+qoNRQadLC7RTOcrr6rv7POEoHRMJye9fyMMgmoZqWNw9bGJOV5o2O5GGx/mSJDcaxnmNNXmhJ+HaCYOU6dJ0CD4ZiAGIchOwt5H281r9Fy4qQNoQh9F00adv4X9/NN2FsbN4z9HSqikYLDtpRC14Osw/qIJ1tHK4r1KL5azmsJTG7lSd0V57LA5PwWz1WX5gMzWd6+K85mjw6YvvteektFI/PlYDzQml5R8gn7CAsTIU,iv:cZnh6Ql0n4ruhow3XwESnrwGI0L/3R7KmaqG+WjwVcY=,tag:KH0aODHRPbIM2qUse3CGuA==,type:str] 11 | cert: ENC[AES256_GCM,data:vuNG3zFkAFY37xXCAjHya+Y5p/gWbEslUEPMyKDpm3j4xJD7rWIpxySj6LsDIW7iC4L/5JsLlTnTsNZWrhpmTQmKKVw0likKIhD7cFsS/21c7DkfxHphHW5wfYIPUPtLXfUwmrlZ47WWYprOuXNX2CMMVqS1WIV9YHc3Vn+p3qZbcPPHz8P+294CC3p6HHhtiAaKtYGFWY5gLVVosTz+H68TqnWRkCgc1kXN3nv6JDltsQiNvocLg6PZxJT3jTFB5+Aki1G1mT66wqWB9FBylSxAJhuE7Ty/9h5YfLRcrCu2RvlRW26s4Lbnu6UDmdNyp2aU5AZJp4T5+JLm7fpUdF0mLweHYk+wDEM1SQo7Mn5GLMABty5v9HzPxXfAjYlfIUjl1J5EhIlvKhF5y5L9Q5hX1yH5fqW7TCBJsjTLku4cz7zS3VutwzOTkBqlx8MRzUMSOL+3AMzuYaNY21/Ou5RC4nNDM4610kx1BTd08ves/4Mi4nqm9uFROLQ/0K2REIA1LgHWY+gtpAPQbiGEYe9N99bRtlN1kurknzL0U4MoNPpkjuzFEZIgDfhyNNWvUJW/HEYKOtDBhyJcxZFNhy0oZZVW9MBhVwqTKuxsiQvL1QDxIBTEsacnW8b1/GS/Q0nFSXs6fRw1B4Zs4a8BSFzv8m2WFNbJmXos5xV2d+tnkKz26YMGNZT6peIVk4bTIs6/cAafrg5dJrWDshAYONk0Xwqx3oaG4hmG7aD0EMnlynHpCz/RpaKziLkdC1FIUYpiH0YuVbQK8NEUIunrmU0q9ERCx+0ycnebXXtjKi0U2YTv/5I6,iv:NZr0LXtO0zNOv0LmD8wLZ829kgwWtPCo637UyzqSD0s=,tag:3mQckI1jSCMzsHQWFPoMgg==,type:str] 12 | sso: 13 | auth-key: ENC[AES256_GCM,data:vedIcTCaLbKewqi30tglraSuoozOtPhE5fUbBCv55So/RvQ1/MO9aWtte/yVSjFkbv34ryVin3P7J1SK,iv:PDM5LZwI3nTYhz+CHF6mUuUVr20hIRnzgjXgfCymdg4=,tag:dt4Ff37fV+5t0Msrbivnig==,type:str] 14 | felix: 15 | password-hash: ENC[AES256_GCM,data:aa3VXLavQEnym5VtqqToQ30AnKzhYXwa73KdUHHssOja+rLKy9iShUUGjkR4+Csmpr+TRrLxARjWJW0d,iv:zyX2SdqPrbwl5W75xWu3idRUmVdh64x1T8lH2TVhwxk=,tag:xsKImab3e01sCNfZGzwcrg==,type:str] 16 | totp-secret: ENC[AES256_GCM,data:J60zceMAWdUuUIHyV57mcEatt5zhbMatvL+Z0NBoEJyDG2u9MornLJju/+AoBUq/erbY4Z/ymlI=,iv:kUBIGMy1yDQ8Vc6GvxmGGqgQ341Lfvmq+Q/DCTHiT0k=,tag:fzCvUmnKuioArq7ObFwDUA==,type:str] 17 | acme: 18 | inwx: ENC[AES256_GCM,data:yf1Xi0YOSUMfTd91NCjMUD7uMPzFjK7wfsxnDeIXBfd1U0hopemF6KBDYVLIP7NrTDz+wGWCqvgO3w8FjfWjCgtcqw==,iv:4HOFyvuhCDTSFGgEqbdVmC8pDpf/CpapDaSUwwRZOGI=,tag:ynQxQS1VzpqCJZOfUP0Bwg==,type:str] 19 | dyndns: 20 | password: ENC[AES256_GCM,data:NMEyYAGcPpUCcfr7nETG3hwKlBwqCSnr41+uOeCn9Jctn3Qx,iv:fcR0kjFOZ+nRiNF1xb8BYwuVhiZn+NMxY7grbvb5nac=,tag:A3BZhMVXE2hYdqEwREsRug==,type:str] 21 | radarr: 22 | apikey: ENC[AES256_GCM,data:vyv5PRvNhJ0MxszGa0dQPIhlPdDqEV1Vm0QRCPKmY6k=,iv:tY159K+FqR6eqHYPR6uEveD7V8QZDJidxT0ms81kEew=,tag:tX1fA6BLHq3b4AwfD8L1Ag==,type:str] 23 | sonarr: 24 | apikey: ENC[AES256_GCM,data:gnqWrd3FAqR49Q3LJfVk0DtSa9Oo5qR8BguI7qhF62Q=,iv:mmSHSV74EHtsor3xBU5HL7DtVXk3SKu5KjV2DglWgNI=,tag:1jlDMaYp2zu4B1EpfeL9nQ==,type:str] 25 | prowlarr: 26 | apikey: ENC[AES256_GCM,data:5H3aZEaa7trPgwo7CKnryhMUdZ+UP+a9UBNbeLNHqxk=,iv:OE5/q4HRIhoB/ZK4ic2nC63t5nNyJi2AZsP4JYuxaW0=,tag:l5IFkGCmam+f3dFgKM1Rwg==,type:str] 27 | bazarr: 28 | apikey: ENC[AES256_GCM,data:242MsUAen1TWp967NoWifjuof41umtrpTKDRe2eeLyU=,iv:uvWaZmatQk71Q1Q7TQ4PLmjn1jYwKrrWLZ+xOjgl6eo=,tag:BFYP4uozPyAX9I8djfBESQ==,type:str] 29 | fritzbox: 30 | password: ENC[AES256_GCM,data:JGWuhP87ghn249eeaDBJNuuqOvl5AYTlfINxtbe5TSc=,iv:SQWvylQPZcnMXj/3/E3u8B3zwucr//kv7kPf1ACouaw=,tag:1BA4mvj+HemxJdQAQ0xBUQ==,type:str] 31 | sops: 32 | kms: [] 33 | gcp_kms: [] 34 | azure_kv: [] 35 | hc_vault: [] 36 | age: 37 | - recipient: age1hf8m9upp00dr7qv2kmqdr50fpvd9ejzkfu8yknqnuda2aas2tvrs4l3u7m 38 | enc: | 39 | -----BEGIN AGE ENCRYPTED FILE----- 40 | YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB1dVYyRDRWUkNKQXN2c2N2 41 | MDVGajJzWVl4S2FkVkNHc1FlcEtYdmc1aVV3CmRFckplMWVyNVhJWDdNOFlXK2xK 42 | VW9MdmdvbTkrQiswdlU3SkZhUU9TK28KLS0tIHdxY05qaHlHbGVMejBzYk5MUSty 43 | cTF3ZTZKdm9WK29Da21oRU9ERVJVS00Kp/VflSZZB0evGinqjFBnqR1zI0CIwF5s 44 | jqQhA0OQV5tHcP/SBoLRJeEn5iH7aAcUzXseV1DZ2kwkZ8eKUUWmdA== 45 | -----END AGE ENCRYPTED FILE----- 46 | - recipient: age14nt7qcsrye0vrpk0xcgcfmhkxwwumna39fpn83g3x0zml62skatqpnmhk4 47 | enc: | 48 | -----BEGIN AGE ENCRYPTED FILE----- 49 | YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBOd2ozNTBXMTB0UlBnTWZs 50 | VVJqdC9nUXFHUjBBdGk2RjFaRVhTeS96WENRCjJUNGJNTmJ6Nmd0ME5XTjNyaGsw 51 | SFp2Z1F5djJETnpsa0tNOFJpY0F3ZmcKLS0tIEsrQ0tvZ09QeVdxTmhxSXdBUjAw 52 | a1BjcjVaM01UV0o5bTFTVTE5QUZKMmsKPAGYDf1FVtp4+Z/KUrI6z0aZYEwN1DYa 53 | jKtA1IUXrmdaRllN0SfC+YjMXTk7IoJvrjagCv9Zo0zEKasfO8PL4g== 54 | -----END AGE ENCRYPTED FILE----- 55 | lastmodified: "2024-11-07T21:01:40Z" 56 | mac: ENC[AES256_GCM,data:EmiltUNVqdNaabw60HiSM+bxHZd+ni8h4V6e8/z19LMn1FYgRsZ5tXx+9SmceP77c9Ofp5CO+NsCg67MWtO8typo/WrPTfrpCa1sPThaRY5CzMaGizjx68X2H9/Iz7TClHe+ZbkD5fix1sgkAKdRTpF3KGPN42dJbHnEvPOR4ok=,iv:4lvS+vQFfn2dIqVhH9YgWRBkPzujgBOgSJXp/D/dZk8=,tag:cXU+Dy69VVztQuJGhotyAg==,type:str] 57 | pgp: [] 58 | unencrypted_suffix: _unencrypted 59 | version: 3.8.1 60 | --------------------------------------------------------------------------------