Last active
June 13, 2026 21:06
-
-
Save ibizaman/83d13092e56e75f5c4abdc39f4ad4236 to your computer and use it in GitHub Desktop.
SelfHostBlocks example config
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| { | |
| lib, | |
| config, | |
| utils, | |
| pkgs, | |
| ... | |
| }: | |
| let | |
| domain = "example.com"; | |
| dcdomain = "dc=example,dc=com"; | |
| friendlyName = "Example"; | |
| email = "me@example.com"; | |
| hostname = "mycomputer"; | |
| username = "myself"; | |
| firstName = "Me"; | |
| backupCfg = | |
| { | |
| enable ? true, | |
| shbOpt ? null, | |
| timerConfig ? { | |
| OnBootSec = "15min"; | |
| OnUnitActiveSec = "1h"; | |
| RandomizedDelaySec = "7min"; | |
| }, | |
| s3TimerConfig ? { | |
| OnCalendar = "*-*-* 03:00:00"; | |
| RandomizedDelaySec = "1h"; | |
| }, | |
| }: | |
| name: | |
| let | |
| bck = (if shbOpt != null then shbOpt else config.shb.${name}).backup.request; | |
| in | |
| { | |
| shb.restic.instances."${name}-local" = { | |
| request = bck; | |
| settings = { | |
| enable = if enable then true else shbOpt.enable; | |
| passphrase.result = config.shb.sops.secret."restic/passphrases/${name}".result; | |
| repository = { | |
| path = "/srv/backup/restic/${hostname}/${name}"; | |
| timerConfig = timerConfig // { | |
| FixedRandomDelay = true; | |
| }; | |
| }; | |
| retention = { | |
| keep_within = "1d"; | |
| keep_hourly = 24; | |
| keep_daily = 7; | |
| keep_weekly = 4; | |
| keep_monthly = 6; | |
| }; | |
| limitUploadKiBs = 8000; | |
| limitDownloadKiBs = 8000; | |
| }; | |
| }; | |
| shb.restic.instances."${name}-s3" = { | |
| request = bck; | |
| settings = { | |
| enable = if enable then true else shbOpt.enable; | |
| passphrase.result = config.shb.sops.secret."restic/passphrases/${name}".result; | |
| repository = { | |
| path = "s3:s3.us-west-000.backblazeb2.com/${hostname}-backup/${name}"; | |
| timerConfig = s3TimerConfig // { | |
| FixedRandomDelay = true; | |
| }; | |
| secrets = { | |
| AWS_ACCESS_KEY_ID.source = config.sops.secrets."backup/b2/access_key_id/${name}".path; | |
| AWS_SECRET_ACCESS_KEY.source = config.sops.secrets."backup/b2/secret_access_key/${name}".path; | |
| }; | |
| }; | |
| retention = { | |
| keep_within = "1d"; | |
| keep_hourly = 24; | |
| keep_daily = 7; | |
| keep_weekly = 4; | |
| keep_monthly = 6; | |
| }; | |
| limitUploadKiBs = 3000; | |
| limitDownloadKiBs = 3000; | |
| }; | |
| }; | |
| shb.sops.secret."restic/passphrases/${name}" = { | |
| request = config.shb.restic.instances."${name}-local".settings.passphrase.request; | |
| }; | |
| shb.sops.secret."backup/b2/access_key_id/${name}" = { | |
| request = config.shb.restic.instances."${name}-local".settings.passphrase.request; | |
| settings.key = "backup/b2/access_key_id"; | |
| }; | |
| shb.sops.secret."backup/b2/secret_access_key/${name}" = { | |
| request = config.shb.restic.instances."${name}-local".settings.passphrase.request; | |
| settings.key = "backup/b2/secret_access_key"; | |
| }; | |
| }; | |
| in | |
| { | |
| config = lib.mkMerge [ | |
| { | |
| networking.firewall.enable = false; | |
| # DNS and DHCP | |
| networking.firewall.allowedUDPPorts = [ | |
| 53 | |
| 67 | |
| ]; | |
| } | |
| { | |
| powerManagement.cpuFreqGovernor = "performance"; | |
| hardware.cpu.intel.updateMicrocode = config.hardware.enableRedistributableFirmware; | |
| environment.systemPackages = with pkgs; [ | |
| curl | |
| git | |
| htop | |
| jq | |
| lsof | |
| ncdu | |
| sqlite | |
| sysz | |
| usbutils | |
| vim | |
| ]; | |
| sops.defaultSopsFile = ../../secrets/secrets.yaml; | |
| sops.age = { | |
| sshKeyPaths = [ "/boot/host_key" ]; | |
| }; | |
| services.openssh.listenAddresses = [ | |
| { | |
| addr = "0.0.0.0"; # Needs to be 0.0.0.0 otherwise it cannot bind on boot. | |
| port = 22; | |
| } | |
| { | |
| addr = "0.0.0.0"; | |
| port = 22345; | |
| } | |
| ]; | |
| networking.firewall.allowedTCPPorts = [ | |
| 22 | |
| 22345 | |
| ]; | |
| shb.nginx.accessLog = true; | |
| shb.nginx.debugLog = false; | |
| shb.postgresql.debug = false; | |
| hardware.bluetooth.enable = true; | |
| } | |
| { | |
| nix.settings.trusted-public-keys = [ | |
| "selfhostblocks.cachix.org-1:H5h6Uj188DObUJDbEbSAwc377uvcjSFOfpxyCFP7cVs=" | |
| ]; | |
| nix.settings.substituters = [ | |
| "https://selfhostblocks.cachix.org" | |
| ]; | |
| } | |
| { | |
| skarabox = { | |
| inherit hostname username; | |
| hashedPasswordFile = config.sops.secrets."${hostname}/user/hashedPassword".path; | |
| facter-config = ./facter.json; | |
| sshAuthorizedKey = [ | |
| ./ssh-key.pub | |
| "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIcenez8sAc8eXutU8vji378LNMbgdQrZYVy3s0spEvR" | |
| ]; | |
| sshPort = 22345; | |
| boot.sshPort = 22346; | |
| disks = { | |
| rootPool = { | |
| disk1 = "/dev/nvme0n1"; | |
| reservation = "100G"; | |
| }; | |
| dataPool = { | |
| enable = false; | |
| name = "data"; | |
| reservation = "1T"; | |
| }; | |
| initialBackupDataset = false; | |
| }; | |
| staticNetwork = { | |
| ip = "192.168.1.30"; | |
| gateway = "192.168.1.1"; | |
| }; | |
| hostId = "2f44b30a"; | |
| machineId = "2f44b30a07b946e3a142fe2e44a9c6ea"; | |
| }; | |
| sops.secrets."${hostname}/user/hashedPassword" = { | |
| neededForUsers = true; | |
| }; | |
| users.users.root = { | |
| hashedPasswordFile = config.sops.secrets."${hostname}/root/hashedPassword".path; | |
| }; | |
| sops.secrets."${hostname}/root/hashedPassword" = { | |
| neededForUsers = true; | |
| }; | |
| } | |
| (lib.optionalAttrs true { | |
| networking.firewall.allowedUDPPorts = [ 53 ]; | |
| # Need to wait on auth endpoint to be available otherwise nginx can fail to start. | |
| systemd.services.nginx = { | |
| wants = [ "dnsmasq.service" ]; | |
| after = [ "dnsmasq.service" ]; | |
| }; | |
| services.resolved.enable = false; | |
| services.dnsmasq = { | |
| enable = true; | |
| settings = { | |
| cache-size = 1000; | |
| # When switching DNS server, accept old leases from previous server. | |
| dhcp-authoritative = true; | |
| dhcp-range = "192.168.1.101,192.168.1.150,255.255.255.0,6h"; | |
| dhcp-host = [ | |
| "74:3a:f4:73:46:50,laspin,192.168.1.50,infinite" | |
| "ca:6f:2a:43:12:50,timios,192.168.1.51,infinite" | |
| "6c:a6:04:df:67:46,TVBox,192.168.1.70,infinite" | |
| ]; | |
| dhcp-option = [ | |
| "3,192.168.1.1" | |
| ]; | |
| server = [ | |
| # NextDNS | |
| # "2a07:a8c1::" | |
| # "45.90.30.0" | |
| # "2a07:a8c0::" | |
| # "45.90.28.0" | |
| # Quad9 | |
| # https://quad9.net/service/service-addresses-and-features/#ip | |
| # "9.9.9.9" | |
| # "149.112.112.112" | |
| # "2620:fe::fe" | |
| # "2620:fe::9" | |
| # Stubby | |
| # Also https://wiki.archlinux.org/title/Stubby#Change_port | |
| "127.0.0.1#53000" | |
| "::1#53000" | |
| ]; | |
| log-queries = true; | |
| # For stubby | |
| proxy-dnssec = true; | |
| inherit domain; | |
| no-resolv = true; | |
| bogus-priv = true; | |
| # Got issues with bind-interface on startup, needing to restart dnsmasq for it to listen correctly. | |
| # bind-interfaces = true; | |
| # add-cpe-id = 858972; | |
| address = [ | |
| "/.${domain}/${config.skarabox.staticNetwork.ip}" | |
| ]; | |
| cname = [ | |
| "blog.${domain},ibizaman.github.io" | |
| ]; | |
| }; | |
| }; | |
| services.stubby = { | |
| enable = true; | |
| # https://github.com/getdnsapi/stubby/blob/develop/stubby.yml.example | |
| settings = pkgs.stubby.passthru.settingsExample // { | |
| listen_addresses = [ | |
| "127.0.0.1@53000" | |
| "0::1@53000" | |
| ]; | |
| # https://dnsprivacy.org/public_resolvers/ | |
| # digest from https://nixos.wiki/wiki/Encrypted_DNS#Stubby | |
| upstream_recursive_servers = [ | |
| { | |
| address_data = "9.9.9.9"; | |
| tls_auth_name = "dns.quad9.net"; | |
| tls_pubkey_pinset = [ | |
| { | |
| digest = "sha256"; | |
| value = "i2kObfz0qIKCGNWt7MjBUeSrh0Dyjb0/zWINImZES+I="; | |
| } | |
| ]; | |
| } | |
| { | |
| address_data = "149.112.112.112"; | |
| tls_auth_name = "dns.quad9.net"; | |
| tls_pubkey_pinset = [ | |
| { | |
| digest = "sha256"; | |
| value = "i2kObfz0qIKCGNWt7MjBUeSrh0Dyjb0/zWINImZES+I="; | |
| } | |
| ]; | |
| } | |
| ]; | |
| }; | |
| }; | |
| }) | |
| { | |
| services.sanoid = { | |
| enable = true; | |
| templates.main = { | |
| autosnap = true; | |
| autoprune = true; | |
| hourly = 5; | |
| daily = 10; | |
| monthly = 15; | |
| yearly = 20; | |
| }; | |
| }; | |
| shb.zfs.snapshotBeforeActivation = { | |
| enable = true; | |
| recursive = true; | |
| }; | |
| shb.zfs.pools.backup.datasets."data" = { | |
| path = "/srv/backup"; | |
| }; | |
| shb.zfs.pools.backup.datasets."syncoid" = { | |
| path = "none"; | |
| }; | |
| shb.zfs.pools.backup.datasets."syncoid/root" = { | |
| path = "none"; | |
| }; | |
| shb.zfs.pools.backup.datasets."syncoid/data" = { | |
| path = "none"; | |
| }; | |
| services.syncoid = { | |
| enable = true; | |
| commands."root" = { | |
| recursive = true; | |
| target = "backup/syncoid/root"; | |
| }; | |
| commands."data/games" = { | |
| recursive = true; | |
| target = "backup/syncoid/data/games"; | |
| }; | |
| commands."data/music" = { | |
| recursive = true; | |
| target = "backup/syncoid/data/music"; | |
| }; | |
| commands."data/nextcloud" = { | |
| recursive = true; | |
| target = "backup/syncoid/data/nextcloud"; | |
| }; | |
| commands."data/pinchflat" = { | |
| recursive = true; | |
| target = "backup/syncoid/data/pinchflat"; | |
| }; | |
| commands."data/projects" = { | |
| recursive = true; | |
| target = "backup/syncoid/data/projects"; | |
| }; | |
| }; | |
| } | |
| { | |
| boot.swraid.enable = true; | |
| environment.systemPackages = [ pkgs.borgbackup ]; | |
| users.users.vorta = { | |
| isSystemUser = true; | |
| useDefaultShell = true; | |
| home = "/srv/backup/${username}"; | |
| homeMode = "770"; | |
| createHome = false; | |
| group = "backup"; | |
| openssh.authorizedKeys.keys = [ | |
| "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDXGZ+Sqlz8HmS3HDEIX0FXyDECmHMw9C6DaU84S2w8c" | |
| "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEDKB9Jjd+S+wPfWDZYbN+f6xkf/HnpuU1JgplLCwFev" | |
| ]; | |
| }; | |
| users.groups.backup = { }; | |
| users.groups.media.members = [ "backup" ]; | |
| } | |
| ( | |
| let | |
| name = "postgres"; | |
| in | |
| { | |
| services.postgresql.package = pkgs.postgresql_15; | |
| shb.zfs.pools.root.datasets."safe/postgresql".path = "/var/lib/postgresql"; | |
| shb.sanoid.backup."root/safe/postgresql" = { | |
| request = config.shb.zfs.pools.root.datasets."safe/postgresql".datasetBackup.request; | |
| settings.useTemplate = [ "main" ]; | |
| }; | |
| shb.restic.databases."postgres" = { | |
| request = config.shb.postgresql.databasebackup.request; | |
| settings = { | |
| enable = true; | |
| passphrase.result = config.shb.sops.secret."restic/passphrases/${name}".result; | |
| repository = { | |
| path = "/srv/backup/restic/${hostname}/${name}"; | |
| timerConfig = { | |
| OnBootSec = "15min"; | |
| OnUnitActiveSec = "1h"; | |
| RandomizedDelaySec = "7min"; | |
| }; | |
| }; | |
| retention = { | |
| keep_within = "1d"; | |
| keep_hourly = 24; | |
| keep_daily = 7; | |
| keep_weekly = 4; | |
| keep_monthly = 6; | |
| }; | |
| limitUploadKiBs = 8000; | |
| limitDownloadKiBs = 8000; | |
| }; | |
| }; | |
| shb.sops.secret."restic/passphrases/${name}".request = | |
| config.shb.restic.databases.${name}.settings.passphrase.request; | |
| shb.sops.secret."backup/b2/access_key_id/${name}" = { | |
| request = config.shb.restic.databases.${name}.settings.passphrase.request; | |
| settings.key = "backup/b2/access_key_id"; | |
| }; | |
| shb.sops.secret."backup/b2/secret_access_key/${name}" = { | |
| request = config.shb.restic.databases.${name}.settings.passphrase.request; | |
| settings.key = "backup/b2/secret_access_key"; | |
| }; | |
| shb.sops.secret."backup/skaraboxJean/access_key_id/${name}" = { | |
| request = config.shb.restic.databases.${name}.settings.passphrase.request; | |
| settings.key = "backup/skaraboxJean/access_key_id"; | |
| }; | |
| shb.sops.secret."backup/skaraboxJean/secret_access_key/${name}" = { | |
| request = config.shb.restic.databases.${name}.settings.passphrase.request; | |
| settings.key = "backup/skaraboxJean/secret_access_key"; | |
| }; | |
| } | |
| ) | |
| (lib.optionalAttrs true { | |
| shb.certs.certs.letsencrypt.${domain} = { | |
| inherit domain; | |
| group = "nginx"; | |
| reloadServices = [ "nginx.service" ]; | |
| adminEmail = "shb@${domain}"; | |
| afterAndWants = lib.optionals config.services.dnsmasq.enable [ "dnsmasq.service" ]; | |
| }; | |
| shb.zfs.pools.root.datasets."safe/acme".path = "/var/lib/acme"; | |
| shb.sanoid.backup."root/safe/acme" = { | |
| request = config.shb.zfs.pools.root.datasets."safe/acme".datasetBackup.request; | |
| settings.useTemplate = [ "main" ]; | |
| }; | |
| }) | |
| (lib.optionalAttrs true { | |
| shb.certs.certs.letsencrypt.${domain}.extraDomains = [ "ldap.${domain}" ]; | |
| shb.lldap = { | |
| enable = true; | |
| inherit domain; | |
| subdomain = "ldap"; | |
| ssl = config.shb.certs.certs.letsencrypt.${domain}; | |
| ldapPort = 3890; | |
| webUIListenPort = 17170; | |
| inherit dcdomain; | |
| ldapUserPassword.result = config.shb.sops.secret."lldap/user_password".result; | |
| jwtSecret.result = config.shb.sops.secret."lldap/jwt_secret".result; | |
| # restrictAccessIPRange = "192.168.50.0/24"; | |
| debug = false; | |
| ensureUsers = { | |
| ${username} = { | |
| inherit email; | |
| password.result = config.shb.sops.secret."users/${username}/password".result; | |
| displayName = firstName; | |
| groups = [ | |
| "arr_admin" | |
| "arr_user" | |
| "deluge_admin" | |
| "deluge_user" | |
| "forgejo_admin" | |
| "forgejo_user" | |
| "hledger_user" | |
| "homeassistant_admin" | |
| "homeassistant_user" | |
| "jellyfin_admin" | |
| "jellyfin_user" | |
| "pinchflat_user" | |
| "music-assistant_user" | |
| "nextcloud_admin" | |
| "nextcloud_user" | |
| "vaultwarden_admin" | |
| "vaultwarden_user" | |
| ]; | |
| }; | |
| }; | |
| }; | |
| shb.sops.secret."lldap/user_password".request = config.shb.lldap.ldapUserPassword.request; | |
| shb.sops.secret."lldap/jwt_secret".request = config.shb.lldap.jwtSecret.request; | |
| shb.sops.secret."users/${username}/password".request = config.shb.lldap.ensureUsers.${username}.password.request; | |
| shb.zfs.pools.root.datasets."safe/lldap".path = "/var/lib/private/lldap"; | |
| shb.sanoid.backup."root/safe/lldap" = { | |
| request = config.shb.zfs.pools.root.datasets."safe/lldap".datasetBackup.request; | |
| settings.useTemplate = [ "main" ]; | |
| }; | |
| shb.homepage.servicesGroups.Admin.services.LLDAP = { | |
| sortOrder = 2; | |
| dashboard.request = config.shb.lldap.dashboard.request; | |
| }; | |
| }) | |
| (backupCfg { | |
| shbOpt.backup.request = { | |
| user = "root"; | |
| sourceDirectories = [ | |
| "/var/lib/private/lldap" | |
| ]; | |
| }; | |
| } "lldap") | |
| (lib.optionalAttrs true { | |
| shb.certs.certs.letsencrypt.${domain}.extraDomains = [ "authelia.${domain}" ]; | |
| shb.authelia = { | |
| enable = true; | |
| inherit domain; | |
| subdomain = "authelia"; | |
| ssl = config.shb.certs.certs.letsencrypt.${domain}; | |
| debug = false; | |
| ldapHostname = "127.0.0.1"; | |
| ldapPort = config.shb.lldap.ldapPort; | |
| dcdomain = config.shb.lldap.dcdomain; | |
| smtp = { | |
| host = "smtp.mailgun.org"; | |
| port = 587; | |
| username = "postmaster@mg.${domain}"; | |
| from_address = "authelia@${domain}"; | |
| password.result = config.shb.sops.secret."authelia/smtp_password".result; | |
| }; | |
| secrets = { | |
| jwtSecret.result = config.shb.sops.secret."authelia/jwt_secret".result; | |
| ldapAdminPassword.result = config.shb.sops.secret."authelia/ldap_admin_password".result; | |
| sessionSecret.result = config.shb.sops.secret."authelia/session_secret".result; | |
| storageEncryptionKey.result = config.shb.sops.secret."authelia/storage_encryption_key".result; | |
| identityProvidersOIDCHMACSecret.result = config.shb.sops.secret."authelia/hmac_secret".result; | |
| identityProvidersOIDCIssuerPrivateKey.result = config.shb.sops.secret."authelia/private_key".result; | |
| }; | |
| }; | |
| shb.sops.secret."authelia/jwt_secret".request = config.shb.authelia.secrets.jwtSecret.request; | |
| shb.sops.secret."authelia/ldap_admin_password".request = | |
| config.shb.authelia.secrets.ldapAdminPassword.request; | |
| shb.sops.secret."authelia/session_secret".request = | |
| config.shb.authelia.secrets.sessionSecret.request; | |
| shb.sops.secret."authelia/storage_encryption_key".request = | |
| config.shb.authelia.secrets.storageEncryptionKey.request; | |
| shb.sops.secret."authelia/hmac_secret".request = | |
| config.shb.authelia.secrets.identityProvidersOIDCHMACSecret.request; | |
| shb.sops.secret."authelia/private_key".request = | |
| config.shb.authelia.secrets.identityProvidersOIDCIssuerPrivateKey.request; | |
| shb.sops.secret."authelia/smtp_password".request = config.shb.authelia.smtp.password.request; | |
| shb.zfs.pools.root.datasets."safe/redis-authelia".path = "/var/lib/redis-authelia"; | |
| shb.zfs.pools.root.datasets."safe/authelia-authelia.${domain}" = config.shb.authelia.mount; | |
| shb.sanoid.backup."root/safe/redis-authelia" = { | |
| request = config.shb.zfs.pools.root.datasets."safe/redis-authelia".datasetBackup.request; | |
| settings.useTemplate = [ "main" ]; | |
| }; | |
| shb.sanoid.backup."root/safe/authelia-authelia.${domain}" = { | |
| request = | |
| config.shb.zfs.pools.root.datasets."safe/authelia-authelia.${domain}".datasetBackup.request; | |
| settings.useTemplate = [ "main" ]; | |
| }; | |
| shb.homepage.servicesGroups.Admin.services.Authelia = { | |
| sortOrder = 2; | |
| dashboard.request = config.shb.authelia.dashboard.request; | |
| }; | |
| }) | |
| (lib.optionalAttrs true { | |
| shb.certs.certs.letsencrypt.${domain}.extraDomains = [ "vaultwarden.${domain}" ]; | |
| shb.vaultwarden = { | |
| enable = true; | |
| inherit domain; | |
| subdomain = "vaultwarden"; | |
| ssl = config.shb.certs.certs.letsencrypt.${domain}; | |
| port = 8222; | |
| authEndpoint = "https://${config.shb.authelia.subdomain}.${config.shb.authelia.domain}"; | |
| databasePassword.result = config.shb.sops.secret."vaultwarden/db".result; | |
| smtp = { | |
| from_address = "vaultwarden@${domain}"; | |
| host = "smtp.mailgun.org"; | |
| port = 587; | |
| username = "postmaster@mg.${domain}"; | |
| password.result = config.shb.sops.secret."vaultwarden/smtp".result; | |
| }; | |
| }; | |
| shb.sops.secret."vaultwarden/db".request = config.shb.vaultwarden.databasePassword.request; | |
| shb.sops.secret."vaultwarden/smtp".request = config.shb.vaultwarden.smtp.password.request; | |
| shb.zfs.pools.root.datasets."safe/postgresql".path = "/var/lib/postgresql"; | |
| shb.zfs.pools.root.datasets."safe/bitwarden_rs" = config.shb.vaultwarden.mount; | |
| shb.sanoid.backup."root/safe/postgresql" = { | |
| request = config.shb.zfs.pools.root.datasets."safe/postgresql".datasetBackup.request; | |
| settings.useTemplate = [ "main" ]; | |
| }; | |
| shb.sanoid.backup."root/safe/bitwarden_rs" = { | |
| request = config.shb.zfs.pools.root.datasets."safe/bitwarden_rs".datasetBackup.request; | |
| settings.useTemplate = [ "main" ]; | |
| }; | |
| shb.lldap.ensureGroups = { | |
| vaultwarden_user = { }; | |
| vaultwarden_admin = { }; | |
| }; | |
| shb.homepage.servicesGroups.Documents.services.Vaultwarden = { | |
| sortOrder = 10; | |
| dashboard.request = config.shb.vaultwarden.dashboard.request; | |
| }; | |
| }) | |
| (backupCfg { } "vaultwarden") | |
| (lib.optionalAttrs true { | |
| shb.certs.certs.letsencrypt.${domain}.extraDomains = [ | |
| "ha.${domain}" | |
| "ma.${domain}" | |
| ]; | |
| shb.home-assistant = { | |
| enable = true; | |
| inherit domain; | |
| subdomain = "ha"; | |
| ssl = config.shb.certs.certs.letsencrypt.${domain}; | |
| config = { | |
| name = friendlyName; | |
| country.source = config.shb.sops.secret."home-assistant/country".result.path; | |
| latitude.source = config.shb.sops.secret."home-assistant/latitude_home".result.path; | |
| longitude.source = config.shb.sops.secret."home-assistant/longitude_home".result.path; | |
| time_zone.source = config.shb.sops.secret."home-assistant/time_zone".result.path; | |
| unit_system = "metric"; | |
| }; | |
| ldap = { | |
| enable = true; | |
| host = "127.0.0.1"; | |
| port = config.shb.lldap.webUIListenPort; | |
| userGroup = "homeassistant_user"; | |
| }; | |
| }; | |
| shb.sops.secret."home-assistant/country".request = { | |
| mode = "0440"; | |
| owner = "hass"; | |
| group = "hass"; | |
| restartUnits = [ "home-assistant.service" ]; | |
| }; | |
| shb.sops.secret."home-assistant/latitude_home".request = { | |
| mode = "0440"; | |
| owner = "hass"; | |
| group = "hass"; | |
| restartUnits = [ "home-assistant.service" ]; | |
| }; | |
| shb.sops.secret."home-assistant/longitude_home".request = { | |
| mode = "0440"; | |
| owner = "hass"; | |
| group = "hass"; | |
| restartUnits = [ "home-assistant.service" ]; | |
| }; | |
| shb.sops.secret."home-assistant/time_zone".request = { | |
| mode = "0440"; | |
| owner = "hass"; | |
| group = "hass"; | |
| restartUnits = [ "home-assistant.service" ]; | |
| }; | |
| services.home-assistant = { | |
| # https://github.com/NixOS/nixpkgs/blob/master/pkgs/servers/home-assistant/component-packages.nix | |
| extraComponents = [ | |
| "accuweather" | |
| "apple_tv" | |
| "asuswrt" | |
| "backup" | |
| "bluetooth" | |
| "cast" | |
| "co2signal" | |
| "deluge" | |
| "esphome" | |
| "ibeacon" | |
| "icloud" | |
| "ipp" | |
| "jellyfin" | |
| "kegtron" | |
| "kodi" | |
| "matter" | |
| "mqtt" | |
| "music_assistant" | |
| "nmap_tracker" | |
| "ollama" | |
| "openweathermap" | |
| "oralb" | |
| "overkiz" | |
| "philips_js" | |
| "radarr" | |
| "simplisafe" | |
| "sonarr" | |
| "sonos" | |
| "subaru" | |
| "tradfri" | |
| "wled" | |
| "zha" | |
| "assist_pipeline" | |
| "conversation" | |
| "piper" | |
| "wake_word" | |
| "whisper" | |
| "wyoming" | |
| ]; | |
| customComponents = with pkgs.home-assistant-custom-components; [ | |
| adaptive_lighting | |
| # auth-header | |
| (pkgs.buildHomeAssistantComponent rec { | |
| owner = "rstrouse"; | |
| domain = "espsomfy_rts"; | |
| version = "v2.4.7"; | |
| src = pkgs.fetchFromGitHub { | |
| owner = "ibizaman"; | |
| repo = "ESPSomfy-RTS-HA"; | |
| # rev = "refs/tags/${version}"; | |
| rev = "861a607fec5d7b72d093e83de0c34a6ca5a31b93"; | |
| hash = "sha256-12TF73Xr7gunfsDOmzH+VpfXjyhCA6RirPck8kF7tKM="; | |
| }; | |
| buildInputs = [ | |
| pkgs.python314Packages.aiofiles | |
| pkgs.python314Packages.websocket-client | |
| ]; | |
| meta = with lib; { | |
| changelog = "https://github.com/rstrouse/ESPSomfy-RTS-HA/releases/tag/${version}"; | |
| description = "Home Assistant Somfy integration"; | |
| homepage = "https://github.com/rstrouse/ESPSomfy-RTS-HA"; | |
| maintainers = with maintainers; [ ibizaman ]; | |
| # license = licenses.unlicense; | |
| }; | |
| }) | |
| (pkgs.buildHomeAssistantComponent rec { | |
| owner = "JaccoR"; | |
| domain = "entsoe"; | |
| version = "v0.6.3"; | |
| src = pkgs.fetchFromGitHub { | |
| owner = "JaccoR"; | |
| repo = "hass-entso-e"; | |
| rev = "refs/tags/${version}"; | |
| hash = "sha256-BOFXy2KlpMBLGHjq9oSAW34408tlS68Zl6L9c8+mXjI="; | |
| }; | |
| buildInputs = [ | |
| pkgs.python314Packages.requests | |
| # pkgs.python312Packages.websocket-client | |
| ]; | |
| meta = with lib; { | |
| changelog = "https://github.com/JaccoR/hass-entso-e/releases/tag/${version}"; | |
| description = "Home Assistant ENTSO-e Transparency Platform Energy Prices"; | |
| homepage = "https://github.com/JaccoR/hass-entso-e"; | |
| maintainers = with maintainers; [ ibizaman ]; | |
| # license = licenses.unlicense; | |
| }; | |
| }) | |
| ]; | |
| # Need to add them manually by enabling advanced mode in user profile | |
| # then adding in Settings > Dashboards > Resources: | |
| # - /local/nixos-lovelace-modules/mini-graph-card-bundle.js | |
| # - /local/nixos-lovelace-modules/mini-media-player-bundle.js | |
| # - /local/nixos-lovelace-modules/hourly-weather.js | |
| # - /local/nixos-lovelace-modules/weather-card.js | |
| customLovelaceModules = with pkgs.home-assistant-custom-lovelace-modules; [ | |
| clock-weather-card | |
| mini-graph-card | |
| mini-media-player | |
| horizon-card | |
| hourly-weather | |
| weather-card | |
| mushroom | |
| scheduler-card | |
| ]; | |
| extraPackages = python3Packages: [ | |
| ]; | |
| }; | |
| # nixpkgs.config.permittedInsecurePackages = [ | |
| # "openssl-1.1.1w" | |
| # ]; | |
| users.users.hass.extraGroups = [ "dialout" ]; | |
| shb.home-assistant.voice.text-to-speech = { | |
| "fr" = { | |
| enable = true; | |
| voice = "fr-siwis-medium"; | |
| uri = "tcp://0.0.0.0:10200"; | |
| speaker = 0; | |
| }; | |
| # "en" = { | |
| # enable = true; | |
| # voice = "en_GB-alba-medium"; | |
| # uri = "tcp://0.0.0.0:10201"; | |
| # speaker = 0; | |
| # }; | |
| }; | |
| shb.home-assistant.voice.speech-to-text = { | |
| "tiny-fr" = { | |
| enable = true; | |
| model = "base-int8"; | |
| language = "fr"; | |
| uri = "tcp://0.0.0.0:10300"; | |
| device = "cpu"; | |
| }; | |
| # "tiny-en" = { | |
| # enable = true; | |
| # model = "base-int8"; | |
| # language = "en"; | |
| # uri = "tcp://0.0.0.0:10301"; | |
| # device = "cpu"; | |
| # }; | |
| }; | |
| systemd.services.wyoming-faster-whisper-tiny-en.environment."HF_HUB_CACHE" = "/tmp"; | |
| systemd.services.wyoming-faster-whisper-tiny-fr.environment."HF_HUB_CACHE" = "/tmp"; | |
| shb.home-assistant.voice.wakeword = { | |
| enable = true; | |
| uri = "tcp://127.0.0.1:10400"; | |
| }; | |
| # nixpkgs.config.allowUnfreePredicate = pkg: builtins.elem (pkgs.lib.getName pkg) [ | |
| # "python-nest" | |
| # ]; | |
| services.matter-server = { | |
| enable = true; | |
| openFirewall = true; | |
| }; | |
| services.music-assistant = { | |
| enable = true; | |
| providers = [ | |
| # https://github.com/NixOS/nixpkgs/commit/a9691ce732e26df12f0f2cbe4a91dc9f3d375527 | |
| # "airplay" | |
| "hass" | |
| "hass_players" | |
| "jellyfin" | |
| "radiobrowser" | |
| "sonos" | |
| "spotify" | |
| ]; | |
| }; | |
| shb.nginx.vhosts = [ | |
| { | |
| subdomain = "ma"; | |
| inherit domain; | |
| ssl = config.shb.certs.certs.letsencrypt.${domain}; | |
| authEndpoint = "https://${config.shb.authelia.subdomain}.${config.shb.authelia.domain}"; | |
| upstream = "http://127.0.0.1:8095"; | |
| autheliaRules = [ | |
| { | |
| domain = "ma.${domain}"; | |
| policy = "two_factor"; | |
| subject = [ "group:music-assistant_user" ]; | |
| } | |
| ]; | |
| } | |
| ]; | |
| shb.zfs.pools.root.datasets."safe/music-assistant".path = "/var/lib/private/music-assistant"; | |
| shb.sanoid.backup."root/safe/music-assistant" = { | |
| request = config.shb.zfs.pools.root.datasets."safe/music-assistant".datasetBackup.request; | |
| settings.useTemplate = [ "main" ]; | |
| }; | |
| shb.lldap.ensureGroups = { | |
| homeassistant_admin = { }; | |
| homeassistant_user = { }; | |
| music-assistant_user = { }; | |
| }; | |
| shb.homepage.servicesGroups.Home.services.HomeAssistant = { | |
| sortOrder = 1; | |
| dashboard.request = config.shb.home-assistant.dashboard.request; | |
| apiKey.result = config.shb.sops.secret."home-assistant/homepageApiKey".result; | |
| settings.icon = "si-homeassistant"; | |
| settings.widget.custom = [ | |
| { | |
| template = "{{ states('sensor.power_consumption_power_consumption', with_unit=True, rounded=True) }}"; | |
| label = "energy now"; | |
| } | |
| { | |
| state = "sensor.power_consumption_daily_power_consumption"; | |
| label = "energy today"; | |
| } | |
| ]; | |
| }; | |
| shb.sops.secret."home-assistant/homepageApiKey".request = | |
| config.shb.homepage.servicesGroups.Home.services.HomeAssistant.apiKey.request; | |
| }) | |
| (backupCfg { shbOpt = config.shb.home-assistant; } "homeassistant") | |
| (lib.optionalAttrs true { | |
| users.groups.media.gid = 980; | |
| }) | |
| (lib.optionalAttrs true { | |
| shb.certs.certs.letsencrypt.${domain}.extraDomains = [ "jellyfin.${domain}" ]; | |
| shb.jellyfin = { | |
| enable = true; | |
| inherit domain; | |
| subdomain = "jellyfin"; | |
| ssl = config.shb.certs.certs.letsencrypt.${domain}; | |
| admin = { | |
| username = "jellyfin"; | |
| password.result = config.shb.sops.secret."jellyfin/admin/password".result; | |
| }; | |
| ldap = { | |
| enable = true; | |
| host = "127.0.0.1"; | |
| port = config.shb.lldap.ldapPort; | |
| dcdomain = config.shb.lldap.dcdomain; | |
| adminPassword.result = config.shb.sops.secret."jellyfin/ldap_password".result; | |
| userGroup = "jellyfin_user"; | |
| adminGroup = "jellyfin_admin"; | |
| }; | |
| sso = { | |
| enable = true; | |
| endpoint = "https://${config.shb.authelia.subdomain}.${config.shb.authelia.domain}"; | |
| clientID = "jellyfin"; | |
| sharedSecret.result = config.shb.sops.secret."jellyfin/sso_secret".result; | |
| sharedSecretForAuthelia.result = config.shb.sops.secret."jellyfin/authelia/sso_secret".result; | |
| }; | |
| }; | |
| shb.sops.secret."jellyfin/admin/password".request = config.shb.jellyfin.admin.password.request; | |
| shb.sops.secret."jellyfin/ldap_password" = { | |
| request = config.shb.jellyfin.ldap.adminPassword.request; | |
| settings.key = "lldap/user_password"; | |
| }; | |
| shb.sops.secret."jellyfin/sso_secret".request = config.shb.jellyfin.sso.sharedSecret.request; | |
| shb.sops.secret."jellyfin/authelia/sso_secret" = { | |
| request = config.shb.jellyfin.sso.sharedSecretForAuthelia.request; | |
| settings.key = "jellyfin/sso_secret"; | |
| }; | |
| shb.zfs.pools.root.datasets."safe/jellyfin".path = "/var/lib/jellyfin"; | |
| shb.sanoid.backup."root/safe/jellyfin" = { | |
| request = config.shb.zfs.pools.root.datasets."safe/jellyfin".datasetBackup.request; | |
| settings.useTemplate = [ "main" ]; | |
| }; | |
| shb.lldap.ensureGroups = { | |
| jellyfin_admin = { }; | |
| jellyfin_user = { }; | |
| }; | |
| shb.homepage.servicesGroups.Media.services.Jellyfin = { | |
| sortOrder = 1; | |
| dashboard.request = config.shb.jellyfin.dashboard.request; | |
| apiKey.result = config.shb.sops.secret."jellyfin/homepageApiKey".result; | |
| }; | |
| shb.sops.secret."jellyfin/homepageApiKey".request = | |
| config.shb.homepage.servicesGroups.Media.services.Jellyfin.apiKey.request; | |
| }) | |
| (backupCfg { } "jellyfin") | |
| (lib.optionalAttrs true { | |
| shb.zfs.pools.data.datasets."pinchflat-data" = { | |
| path = config.shb.pinchflat.mediaDir; | |
| }; | |
| shb.zfs.pools.data.datasets."pinchflat" = { | |
| path = "/var/lib/pinchflat"; | |
| }; | |
| shb.sanoid.backup."root/safe/pinchflat-data" = { | |
| request = config.shb.zfs.pools.root.datasets."safe/pinchflat-data".datasetBackup.request; | |
| settings.useTemplate = [ "main" ]; | |
| }; | |
| shb.sanoid.backup."root/safe/pinchflat" = { | |
| request = config.shb.zfs.pools.root.datasets."safe/pinchflat".datasetBackup.request; | |
| settings.useTemplate = [ "main" ]; | |
| }; | |
| shb.certs.certs.letsencrypt.${domain}.extraDomains = [ "pinchflat.${domain}" ]; | |
| shb.pinchflat = { | |
| enable = true; | |
| inherit domain; | |
| subdomain = "pinchflat"; | |
| ssl = config.shb.certs.certs.letsencrypt.${domain}; | |
| secretKeyBase.result = config.shb.sops.secret."pinchflat/secretKeyBase".result; | |
| timeZone = "Europe/Zurich"; | |
| mediaDir = "/srv/pinchflat"; | |
| ldap = { | |
| enable = true; | |
| userGroup = "pinchflat_user"; | |
| }; | |
| sso = { | |
| enable = true; | |
| authEndpoint = "https://${config.shb.authelia.subdomain}.${config.shb.authelia.domain}"; | |
| }; | |
| }; | |
| shb.sops.secret."pinchflat/secretKeyBase".request = config.shb.pinchflat.secretKeyBase.request; | |
| shb.homepage.servicesGroups.Media.services.Pinchflat = { | |
| sortOrder = 2; | |
| dashboard.request = config.shb.pinchflat.dashboard.request; | |
| }; | |
| }) | |
| # For Intel | |
| (lib.optionalAttrs false { | |
| # Verify configuration with `nix shell nixpkgs#libva-utils --command vainfo` | |
| # see https://nixos.wiki/wiki/Accelerated_Video_Playback | |
| nixpkgs.config.packageOverrides = pkgs: { | |
| vaapiIntel = pkgs.vaapiIntel.override { enableHybridCodec = true; }; | |
| }; | |
| hardware.graphics = { | |
| enable = true; | |
| extraPackages = with pkgs; [ | |
| intel-media-driver | |
| vaapiIntel | |
| vaapiVdpau | |
| libvdpau-va-gl | |
| intel-compute-runtime # OpenCL filter support (hardware tonemapping and subtitle burn-in) | |
| ]; | |
| }; | |
| }) | |
| # For AMD Ryzen 5 | |
| (lib.optionalAttrs true { | |
| # Verify configuration with `nix shell nixpkgs#libva-utils --command vainfo` | |
| # see https://nixos.wiki/wiki/Accelerated_Video_Playback | |
| hardware.firmware = [ pkgs.linux-firmware ]; | |
| hardware.graphics.enable = true; | |
| hardware.graphics.enable32Bit = true; | |
| hardware.amdgpu.initrd.enable = true; | |
| }) | |
| (lib.optionalAttrs true { | |
| shb.zfs.pools.root.datasets."safe/nextcloud".path = "/var/lib/nextcloud"; | |
| shb.zfs.pools.root.datasets."safe/redis-nextcloud".path = "/var/lib/redis-nextcloud"; | |
| shb.sanoid.backup."root/safe/nextcloud" = { | |
| request = config.shb.zfs.pools.root.datasets."safe/nextcloud".datasetBackup.request; | |
| settings.useTemplate = [ "main" ]; | |
| }; | |
| shb.sanoid.backup."root/safe/redis-nextcloud" = { | |
| request = config.shb.zfs.pools.root.datasets."safe/redis-nextcloud".datasetBackup.request; | |
| settings.useTemplate = [ "main" ]; | |
| }; | |
| shb.certs.certs.letsencrypt.${domain}.extraDomains = [ "n.${domain}" ]; | |
| shb.nextcloud = { | |
| enable = true; | |
| debug = false; | |
| inherit domain; | |
| subdomain = "n"; | |
| ssl = config.shb.certs.certs.letsencrypt.${domain}; | |
| defaultPhoneRegion = "US"; | |
| version = 32; | |
| dataDir = "/var/lib/nextcloud"; | |
| # mountPointServices = [ "srv-nextcloud.mount" ]; | |
| adminPass.result = config.shb.sops.secret."nextcloud/adminpass".result; | |
| apps = { | |
| previewgenerator.enable = true; | |
| externalStorage = { | |
| enable = true; | |
| userLocalMount.directory = "/srv/nextcloud/data/$user/files"; | |
| userLocalMount.mountName = "/"; | |
| }; | |
| ldap = { | |
| enable = true; | |
| host = "127.0.0.1"; | |
| port = config.shb.lldap.ldapPort; | |
| dcdomain = config.shb.lldap.dcdomain; | |
| adminName = "admin"; | |
| adminPassword.result = config.shb.sops.secret."nextcloud/ldap_admin_password".result; | |
| userGroup = "nextcloud_user"; | |
| }; | |
| sso = { | |
| enable = true; | |
| endpoint = "https://${config.shb.authelia.subdomain}.${config.shb.authelia.domain}"; | |
| clientID = "nextcloud"; | |
| secret.result = config.shb.sops.secret."nextcloud/sso/secret".result; | |
| secretForAuthelia.result = config.shb.sops.secret."authelia/nextcloud_sso_secret".result; | |
| fallbackDefaultAuth = true; | |
| }; | |
| memories = { | |
| enable = false; | |
| vaapi = true; | |
| }; | |
| recognize = { | |
| enable = false; | |
| }; | |
| # mediadc.enable = true; | |
| # mediadc.debug = true; | |
| }; | |
| extraApps = apps: { | |
| inherit (apps) | |
| bookmarks | |
| calendar | |
| contacts | |
| deck | |
| groupfolders | |
| notes | |
| tasks | |
| ; | |
| }; | |
| # tracing = "debug_me"; | |
| postgresSettings = { | |
| # From https://pgtune.leopard.in.ua/ with: | |
| # DB Version: 14 | |
| # OS Type: linux | |
| # DB Type: dw | |
| # Total Memory (RAM): 12 GB | |
| # CPUs num: 4 | |
| # Connections num: <none> | |
| # Data Storage: ssd | |
| max_connections = "400"; | |
| shared_buffers = "3GB"; | |
| effective_cache_size = "9GB"; | |
| maintenance_work_mem = "768MB"; | |
| checkpoint_completion_target = "0.9"; | |
| wal_buffers = "16MB"; | |
| default_statistics_target = "100"; | |
| random_page_cost = "1.1"; | |
| effective_io_concurrency = "200"; | |
| work_mem = "7864kB"; | |
| huge_pages = "off"; | |
| min_wal_size = "1GB"; | |
| max_wal_size = "4GB"; | |
| max_worker_processes = "4"; | |
| max_parallel_workers_per_gather = "2"; | |
| max_parallel_workers = "4"; | |
| max_parallel_maintenance_workers = "2"; | |
| }; | |
| # Chose static and small number of children to avoid too much I/O strain on hard drives. | |
| phpFpmPoolSettings = { | |
| "pm" = "static"; | |
| "pm.max_children" = 150; | |
| # "pm.start_servers" = 50; | |
| # "pm.min_spare_servers" = 30; | |
| # "pm.max_spare_servers" = 70; | |
| # "pm.max_spawn_rate" = 10; | |
| # "pm.max_requests" = 100; | |
| # "pm.process_idle_timeout" = "20s"; | |
| }; | |
| }; | |
| systemd.services.postgresql.serviceConfig.Restart = "always"; | |
| # Secret needed for services.nextcloud.config.adminpassFile. | |
| shb.sops.secret."nextcloud/adminpass".request = config.shb.nextcloud.adminPass.request; | |
| shb.sops.secret."nextcloud/ldap_admin_password" = { | |
| request = config.shb.nextcloud.apps.ldap.adminPassword.request; | |
| settings.key = "lldap/user_password"; | |
| }; | |
| shb.sops.secret."nextcloud/sso/secret".request = config.shb.nextcloud.apps.sso.secret.request; | |
| shb.sops.secret."authelia/nextcloud_sso_secret" = { | |
| request = config.shb.nextcloud.apps.sso.secretForAuthelia.request; | |
| settings.key = "nextcloud/sso/secret"; | |
| }; | |
| # nixpkgs.config.allowUnfreePredicate = pkg: builtins.elem (pkgs.lib.getName pkg) [ | |
| # "corefonts" # For onlyoffice | |
| # ]; | |
| shb.lldap.ensureGroups = { | |
| nextcloud_admin = { }; | |
| nextcloud_user = { }; | |
| }; | |
| shb.homepage.servicesGroups.Documents.services.Nextcloud = { | |
| sortOrder = 1; | |
| dashboard.request = config.shb.nextcloud.dashboard.request; | |
| # apiKey.result = config.shb.sops.secret."nextcloud/homepageApiKey".result; | |
| settings.username = username; | |
| # settings.widget.type = "firefly"; | |
| }; | |
| # shb.sops.secret."nextcloud/homepageApiKey".request = | |
| # config.shb.homepage.servicesGroups.Documents.services.Nextcloud.apiKey.request; | |
| }) | |
| # (backupCfg { enable = false; } "nextcloud") | |
| (lib.optionalAttrs false ( | |
| backupCfg { | |
| shbOpt.backup.request = { | |
| user = "nextcloud"; | |
| sourceDirectories = [ | |
| "/srv/nextcloud/data" | |
| ]; | |
| }; | |
| timerConfig = { | |
| OnBootSec = "1h"; | |
| OnUnitActiveSec = "8h"; | |
| RandomizedDelaySec = "2h"; | |
| }; | |
| } "nextcloud-data" | |
| )) | |
| (lib.optionalAttrs true { | |
| shb.lldap.ensureUsers.${username}.groups = [ | |
| "monitoring_admin" | |
| "monitoring_user" | |
| ]; | |
| shb.certs.certs.letsencrypt.${domain}.extraDomains = [ | |
| "grafana.${domain}" | |
| "scrutiny.${domain}" | |
| ]; | |
| shb.monitoring = { | |
| enable = true; | |
| inherit domain; | |
| subdomain = "grafana"; | |
| ssl = config.shb.certs.certs.letsencrypt.${domain}; | |
| contactPoints = [ email ]; | |
| adminPassword.result = config.shb.sops.secret."monitoring/admin_password".result; | |
| secretKey.result = config.shb.sops.secret."monitoring/secret_key".result; | |
| smtp = { | |
| from_address = "grafana@${domain}"; | |
| from_name = "Grafana"; | |
| host = "smtp.mailgun.org"; | |
| port = 587; | |
| username = "postmaster@mg.${domain}"; | |
| passwordFile = config.shb.sops.secret."monitoring/smtp".result.path; | |
| }; | |
| sso = { | |
| enable = true; | |
| authEndpoint = "https://${config.shb.authelia.subdomain}.${config.shb.authelia.domain}"; | |
| sharedSecret.result = config.shb.sops.secret."monitoring/ssoSecret".result; | |
| sharedSecretForAuthelia.result = config.shb.sops.secret."monitoring/authelia/ssoSecret".result; | |
| }; | |
| }; | |
| shb.sops.secret."monitoring/smtp".request = { | |
| mode = "0400"; | |
| owner = "grafana"; | |
| group = "grafana"; | |
| restartUnits = [ "grafana.service" ]; | |
| }; | |
| shb.sops.secret."monitoring/admin_password".request = config.shb.monitoring.adminPassword.request; | |
| shb.sops.secret."monitoring/secret_key".request = config.shb.monitoring.secretKey.request; | |
| shb.sops.secret."monitoring/ssoSecret".request = config.shb.monitoring.sso.sharedSecret.request; | |
| shb.sops.secret."monitoring/authelia/ssoSecret" = { | |
| request = config.shb.monitoring.sso.sharedSecretForAuthelia.request; | |
| settings.key = "monitoring/ssoSecret"; | |
| }; | |
| services.prometheus.retentionTime = "180d"; | |
| shb.zfs.pools.root.datasets."safe/grafana".path = "/var/lib/grafana"; | |
| shb.zfs.pools.root.datasets."safe/loki".path = "/var/lib/loki"; | |
| shb.zfs.pools.root.datasets."safe/netdata".path = "/var/lib/netdata"; | |
| shb.sanoid.backup."root/safe/grafana" = { | |
| request = config.shb.zfs.pools.root.datasets."safe/grafana".datasetBackup.request; | |
| settings.useTemplate = [ "main" ]; | |
| }; | |
| shb.sanoid.backup."root/safe/loki" = { | |
| request = config.shb.zfs.pools.root.datasets."safe/loki".datasetBackup.request; | |
| settings.useTemplate = [ "main" ]; | |
| }; | |
| shb.sanoid.backup."root/safe/netdata" = { | |
| request = config.shb.zfs.pools.root.datasets."safe/netdata".datasetBackup.request; | |
| settings.useTemplate = [ "main" ]; | |
| }; | |
| shb.homepage.servicesGroups.Admin.services.Grafana = { | |
| sortOrder = 10; | |
| dashboard.request = config.shb.monitoring.dashboard.request; | |
| # apiKey.result = config.shb.sops.secret."homepage/grafanaApiKey".result; | |
| }; | |
| shb.homepage.servicesGroups.Admin.services.Scrutiny = { | |
| sortOrder = 11; | |
| dashboard.request = config.shb.monitoring.scrutiny.dashboard.request; | |
| }; | |
| # shb.sops.secret."homepage/grafanaApiKey" = { | |
| # settings.key = "monitoring/homepageApiKey"; | |
| # request = config.shb.homepage.servicesGroups.Admin.services.Grafana.apiKey.request; | |
| # }; | |
| }) | |
| { | |
| users.users.hledger.openssh.authorizedKeys.keys = [ | |
| "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPghxSnq3K7t2Wjwdg7v9MGKM8BnuY28IVpo4yLYqv3W" | |
| ]; | |
| shb.certs.certs.letsencrypt.${domain}.extraDomains = [ "hledger.${domain}" ]; | |
| shb.hledger = { | |
| enable = true; | |
| inherit domain; | |
| subdomain = "hledger"; | |
| ssl = config.shb.certs.certs.letsencrypt.${domain}; | |
| authEndpoint = "https://${config.shb.authelia.subdomain}.${config.shb.authelia.domain}"; | |
| localNetworkIPRange = "192.168.1.0/24"; | |
| extraArguments = [ | |
| "--forecast" | |
| "--auto" | |
| "-X" | |
| "CHF" | |
| ]; | |
| }; | |
| shb.zfs.pools.root.datasets."safe/hledger".path = "/var/lib/hledger"; | |
| shb.sanoid.backup."root/safe/hledger" = { | |
| request = config.shb.zfs.pools.root.datasets."safe/hledger".datasetBackup.request; | |
| settings.useTemplate = [ "main" ]; | |
| }; | |
| shb.lldap.ensureGroups = { | |
| hledger_user = { }; | |
| }; | |
| shb.homepage.servicesGroups.Finance.services.Hledger = { | |
| sortOrder = 1; | |
| dashboard.request = config.shb.hledger.dashboard.request; | |
| # settings.widget.type = "hledger"; | |
| }; | |
| } | |
| (backupCfg { } "hledger") | |
| (lib.optionalAttrs true { | |
| # Find best server: https://nordvpn.com/fr/servers/tools/ | |
| # Then ~/org/bin/nordvpnremoteip.sh y us8575 | |
| # shb.vpn.nordvpnus = { | |
| # enable = true; | |
| # provider = "nordvpn"; | |
| # dev = "tun1"; | |
| # routingNumber = 10; | |
| # remoteServerIP = "91.196.220.28"; | |
| # authFile = config.sops.secrets."nordvpnus/auth".path; | |
| # proxyPort = 12000; | |
| # }; | |
| # sops.secrets."nordvpnus/auth" = { | |
| # mode = "0440"; | |
| # restartUnits = [ "openvpn-nordvpnus.service" ]; | |
| # }; | |
| shb.vpn.nordvpnfr = { | |
| enable = true; | |
| provider = "nordvpn"; | |
| dev = "tun2"; | |
| routingNumber = 11; | |
| remoteServerIP = "37.120.204.147"; | |
| authFile = config.shb.sops.secret."nordvpnfr/auth".result.path; | |
| proxyPort = 12001; | |
| }; | |
| shb.sops.secret."nordvpnfr/auth".request = { | |
| mode = "0440"; | |
| restartUnits = [ "openvpn-nordvpnfr.service" ]; | |
| }; | |
| }) | |
| (lib.optionalAttrs true { | |
| shb.certs.certs.letsencrypt.${domain}.extraDomains = [ "forgejo.${domain}" ]; | |
| shb.forgejo = { | |
| enable = true; | |
| subdomain = "forgejo"; | |
| inherit domain; | |
| ssl = config.shb.certs.certs.letsencrypt."${domain}"; | |
| databasePassword.result = config.shb.sops.secret."forgejo/databasePassword".result; | |
| repositoryRoot = "/srv/projects"; | |
| users = { | |
| "forgejoadmin" = { | |
| isAdmin = true; | |
| email = "forgejoadmin@${domain}"; | |
| password.result = config.shb.sops.secret."forgejo/adminPassword".result; | |
| }; | |
| }; | |
| smtp = { | |
| host = "smtp.mailgun.org"; | |
| port = 587; | |
| username = "postmaster@mg.${domain}"; | |
| from_address = "authelia@${domain}"; | |
| password.result = config.shb.sops.secret."forgejo/smtpPassword".result; | |
| }; | |
| ldap = { | |
| enable = true; | |
| host = "127.0.0.1"; | |
| port = config.shb.lldap.ldapPort; | |
| dcdomain = config.shb.lldap.dcdomain; | |
| adminPassword.result = config.shb.sops.secret."forgejo/ldap_admin_password".result; | |
| }; | |
| sso = { | |
| enable = true; | |
| endpoint = "https://${config.shb.authelia.subdomain}.${config.shb.authelia.domain}"; | |
| clientID = "forgejo"; | |
| sharedSecret.result = config.shb.sops.secret."forgejo/ssoSecret".result; | |
| sharedSecretForAuthelia.result = config.shb.sops.secret."forgejo/authelia/ssoSecret".result; | |
| }; | |
| hostPackages = with pkgs; [ | |
| bash | |
| coreutils | |
| curl | |
| gawk | |
| gitMinimal | |
| gnused | |
| nix | |
| nodejs | |
| openssh | |
| wget | |
| ]; | |
| }; | |
| services.forgejo.settings.repository.ENABLE_PUSH_CREATE_USER = true; | |
| nix.settings.trusted-users = [ "forgejo" ]; | |
| users.users.forgejo.openssh.authorizedKeys.keys = [ | |
| "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICIPM0UrnUQqX7sScEtGj27uxS6FRhvpNTh1YF0E4NzM fogejo@${hostname}" | |
| "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIgmFPtRC0soKC7hxCIYutHSkLIyN/d6F9d1woUivHz4" | |
| ]; | |
| shb.sops.secret."forgejo/adminPassword".request = | |
| config.shb.forgejo.users."forgejoadmin".password.request; | |
| shb.sops.secret."forgejo/databasePassword".request = config.shb.forgejo.databasePassword.request; | |
| shb.sops.secret."forgejo/smtpPassword" = { | |
| request = config.shb.forgejo.smtp.password.request; | |
| settings.key = "authelia/smtp_password"; | |
| }; | |
| shb.sops.secret."forgejo/ldap_admin_password" = { | |
| request = config.shb.forgejo.ldap.adminPassword.request; | |
| settings.key = "lldap/user_password"; | |
| }; | |
| shb.sops.secret."forgejo/ssoSecret".request = config.shb.forgejo.sso.sharedSecret.request; | |
| shb.sops.secret."forgejo/authelia/ssoSecret" = { | |
| request = config.shb.forgejo.sso.sharedSecretForAuthelia.request; | |
| settings.key = "forgejo/ssoSecret"; | |
| }; | |
| shb.zfs.pools.root.datasets."safe/forgejo" = config.shb.forgejo.mount; | |
| shb.sanoid.backup."root/safe/forgejo" = { | |
| request = config.shb.zfs.pools.root.datasets."safe/forgejo".datasetBackup.request; | |
| settings.useTemplate = [ "main" ]; | |
| }; | |
| shb.lldap.ensureGroups = { | |
| forgejo_admin = { }; | |
| forgejo_user = { }; | |
| }; | |
| shb.homepage.servicesGroups.Admin.services.Forgejo = { | |
| sortOrder = 1; | |
| dashboard.request = config.shb.forgejo.dashboard.request; | |
| }; | |
| }) | |
| (backupCfg { } "forgejo") | |
| (lib.optionalAttrs true { | |
| shb.davfs.mounts = [ | |
| { | |
| remoteUrl = "https://n.${domain}/remote.php/dav/files/124eb1fb-49f6-3511-b4e4-c6626c749940"; | |
| mountPoint = "/srv/syncthing/photoframe"; | |
| # Must match the username in Nextcloud when creating a token | |
| username = "photoframe"; | |
| passwordFile = config.shb.sops.secret."photoframe".result.path; | |
| uid = 10001; | |
| gid = 10002; | |
| } | |
| ]; | |
| # Secret must be in the format: | |
| # <mountPoint> <user> <secret> | |
| # /srv/syncthing/photoframe photoframe xyz... | |
| shb.sops.secret."webdav/nextcloud" = { | |
| request.mode = "0600"; | |
| settings.path = "/etc/davfs2/secrets"; | |
| }; | |
| }) | |
| (lib.optionalAttrs true ( | |
| let | |
| vhost = | |
| { folder, certs }: | |
| { | |
| forceSSL = true; | |
| sslCertificate = certs.paths.cert; | |
| sslCertificateKey = certs.paths.key; | |
| locations."/" = { | |
| root = "/srv/${folder}/"; | |
| extraConfig = '' | |
| add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload"; | |
| add_header Cache-Control "max-age=604800, stale-while-revalidate=86400, stale-if-error=86400, must-revalidate, public"; | |
| ''; | |
| }; | |
| }; | |
| in | |
| { | |
| shb.certs.certs.letsencrypt.${domain}.extraDomains = [ | |
| "skarabox.${domain}" | |
| ]; | |
| shb.certs.certs.letsencrypt."skarabox.com" = { | |
| inherit domain; | |
| group = "nginx"; | |
| reloadServices = [ "nginx.service" ]; | |
| adminEmail = "shb@${domain}"; | |
| afterAndWants = lib.optionals config.services.dnsmasq.enable [ "dnsmasq.service" ]; | |
| }; | |
| services.dnsmasq.settings.address = [ | |
| "/skarabox.${domain}/${config.skarabox.staticNetwork.ip}" | |
| "/skarabox.com/${config.skarabox.staticNetwork.ip}" | |
| ]; | |
| services.nginx = { | |
| enable = true; | |
| virtualHosts."skarabox.${domain}" = vhost { | |
| folder = "skarabox-website-staging"; | |
| certs = config.shb.certs.certs.letsencrypt."${domain}"; | |
| }; | |
| virtualHosts."skarabox.com" = vhost { | |
| folder = "skarabox-website"; | |
| certs = config.shb.certs.certs.letsencrypt."skarabox.com"; | |
| }; | |
| }; | |
| systemd.tmpfiles.rules = lib.mkBefore [ | |
| "d '/srv/skarabox-website-staging' 0755 ${username} nginx - -" | |
| "d '/srv/skarabox-website' 0755 ${username} nginx - -" | |
| ]; | |
| shb.zfs.pools.root.datasets."safe/skarabox-website".path = "/srv/skarabox-website"; | |
| shb.zfs.pools.root.datasets."safe/skarabox-website-staging".path = "/srv/skarabox-website-staging"; | |
| shb.sanoid.backup."root/safe/skarabox-website" = { | |
| request = config.shb.zfs.pools.root.datasets."safe/skarabox-website".datasetBackup.request; | |
| settings.useTemplate = [ "main" ]; | |
| }; | |
| shb.sanoid.backup."root/safe/skarabox-website-staging" = { | |
| request = config.shb.zfs.pools.root.datasets."safe/skarabox-website-staging".datasetBackup.request; | |
| settings.useTemplate = [ "main" ]; | |
| }; | |
| } | |
| )) | |
| { | |
| services.thermald.enable = true; | |
| services.fstrim.enable = true; | |
| # It monitors the acceleration values through the various motion interfaces and automatically | |
| # initiates disk head parking if a fall or sliding of the laptop is detected. | |
| services.hdapsd.enable = false; | |
| nixpkgs.overlays = [ | |
| (final: prev: { | |
| pythonPackagesOverlays = (prev.pythonPackagesOverlays or [ ]) ++ [ | |
| (python-final: python-prev: { | |
| }) | |
| ]; | |
| python3 = | |
| let | |
| self = prev.python3.override { | |
| inherit self; | |
| packageOverrides = prev.lib.composeManyExtensions final.pythonPackagesOverlays; | |
| }; | |
| in | |
| self; | |
| python3Packages = final.python3.pkgs; | |
| }) | |
| ]; | |
| } | |
| (lib.optionalAttrs true { | |
| shb.zfs.pools.root.datasets."safe/minecraft" = { | |
| path = config.services.minecraft-servers.dataDir; | |
| owner = "minecraft"; | |
| group = "minecraft"; | |
| }; | |
| shb.sanoid.backup."root/safe/minecraft" = { | |
| request = config.shb.zfs.pools.root.datasets."safe/minecraft".datasetBackup.request; | |
| settings.useTemplate = [ "main" ]; | |
| }; | |
| services.minecraft-servers = { | |
| enable = true; | |
| eula = true; | |
| openFirewall = true; | |
| servers.main = { | |
| enable = true; | |
| whitelist = { | |
| }; | |
| package = pkgs.neoforgeServers.neoforge-1_21_1; | |
| serverProperties = { | |
| server-port = 43000; | |
| difficulty = "hard"; | |
| gamemode = "survival"; | |
| max-players = 5; | |
| motd = "${friendlyName} Minecraft server!"; | |
| enforce-secure-profile = false; | |
| online-mode = false; | |
| white-list = true; | |
| }; | |
| jvmOpts = "-Xms4092M -Xmx4092M -XX:+UseG1GC -XX:ParallelGCThreads=2 -XX:MinHeapFreeRatio=5 -XX:MaxHeapFreeRatio=10"; | |
| symlinks = { | |
| # nix shell github:Infinidoge/nix-minecraft#nix-modrinth-prefetch | |
| # nix-modrinth-prefetch KSgki4uc | |
| mods = pkgs.linkFarmFromDrvs "mods" ( | |
| builtins.attrValues { | |
| # https://modrinth.com/mod/journeymap | |
| JourneyMap = pkgs.fetchurl { | |
| url = "https://cdn.modrinth.com/data/lfHFW1mp/versions/E44vwUJO/journeymap-neoforge-1.21.1-6.0.0-beta.66.jar"; | |
| sha512 = "a6e6fc436020c4d4d3625147c271b04535765e0ae9c59703aa66cd1d3c986e077ddafe2daf9c00b45fd9f4dfbf82ab0f1ca1ea730253ea9e0b4d954729b2c632"; | |
| # url = "https://cdn.modrinth.com/data/lfHFW1mp/versions/HSajmA5j/journeymap-neoforge-1.21.5-6.0.0-beta.52.jar"; | |
| # sha512 = "sha512-EBX4pZkqYNM2zIs2RRXR0W6bW5wrTlJIqGJ3ScXsI7msAylAdpfjHkgxjINVDspePTGoOnBrlq4gnDPpuLPZww=="; | |
| }; | |
| # https://modrinth.com/mod/sophisticated-backpacks | |
| SophisticatedBackpacks = pkgs.fetchurl { | |
| url = "https://cdn.modrinth.com/data/TyCTlI4b/versions/ZMuJ1TI7/sophisticatedbackpacks-1.21.1-3.25.44.1736.jar"; | |
| sha512 = "0704060b2d0e2e88bc771da1b26fe262563ac6a814f129a1207782d7edbac8bdc1b4057669c5bc94976e6eb74c6386c0735f93628b2d1f23e7eac4ae6562e185"; | |
| # url = "https://cdn.modrinth.com/data/TyCTlI4b/versions/TbHJGS23/sophisticatedbackpacks-1.21.5-3.25.45.1737.jar"; | |
| # sha512 = "sha512-y+zMncOEF2squyfjOZ4PdWWHSOK2Lx0vF20U1krkC0fPoxc5g2Fw+f5M/uPXURVvxBWxEsXTZb/OtBYsJ95z3w=="; | |
| }; | |
| # https://modrinth.com/mod/sophisticated-core | |
| SophisticatedCore = pkgs.fetchurl { | |
| url = "https://cdn.modrinth.com/data/nmoqTijg/versions/ErcuCe4o/sophisticatedcore-1.21.1-1.4.35.1823.jar"; | |
| sha512 = "c2c9c4e9e99a43292297f92b1f4dcfa1c9000d9aa684fed07153a4896ae9791d90f29cf0d8365c61d1915c5a6ee4c5cb881eee6c2385b562770281c3b700c0f0"; | |
| }; | |
| # https://modrinth.com/mod/jei | |
| JustEnoughItems = pkgs.fetchurl { | |
| url = "https://cdn.modrinth.com/data/u6dRKJwZ/versions/YAcQ6elZ/jei-1.21.1-neoforge-19.27.0.340.jar"; | |
| sha512 = "8bad8eb3c8e974f867e23e4d74598f603c5fbf03eb5356a386dd37cb9fa23e08ad1f58be6b7be50d2fbf9d3fbfaeac8584c70ced736df4b8f82c7c75be242998"; | |
| # url = "https://cdn.modrinth.com/data/u6dRKJwZ/versions/Up8I2nu2/jei-1.21.5-neoforge-21.4.0.27.jar"; | |
| # sha512 = "sha512-qVyHvI7XUqD2s1pyFeY2IpOuerSWaL0c0hdrCNoMLXfjQFG7HW+utFB1Ggnssuc1cYKz/857n9M6JmeBOowk3A=="; | |
| }; | |
| # https://modrinth.com/mod/citadel | |
| Citadel = pkgs.fetchurl { | |
| url = "https://cdn.modrinth.com/data/jJfV67b1/versions/uzrkhBpn/citadel-2.7.0-1.21.1.jar"; | |
| sha512 = "d77d8d25279d877ef94f1e48e770547ba2fbee2f01ed151aa932c6e36ed5dd7f846db096f7b1f9c13f49a3e2fd5de434d20a0924a3232e06d20c7ca5f46e47f6"; | |
| }; | |
| # https://modrinth.com/mod/alexs-mobs(1.21.1) | |
| AlexsMobs = pkgs.fetchurl { | |
| url = "https://cdn.modrinth.com/data/EmNhnNnt/versions/KSgki4uc/alexsmobs-1.22.17.jar"; | |
| sha512 = "9f57f20693bb187c54f6d68dc34592b87d0e55a5a4628ad097b0aa39afe165f4a834b83b6590d803e64bac0b1230a8f46369bfe45851a3cc964cf3d88f235277"; | |
| } ; | |
| # https://modrinth.com/mod/farmers-delight | |
| FarmersDelight = pkgs.fetchurl { | |
| url = "https://cdn.modrinth.com/data/R2OftAxM/versions/9gp7w8NC/FarmersDelight-1.21.1-1.3.1.jar"; | |
| sha512 = "596340db019049e8da066df13cedb76eb06a8e877c86fd10b38332317b5fd5001cad485a6de87e3a8bf0423d87a1ffbdd51a86f6c06560f40995d68fe05edf95"; | |
| }; | |
| } | |
| ); | |
| }; | |
| }; | |
| }; | |
| allowedUnfreePackagesRegexs = [ | |
| "neoforge" | |
| ]; | |
| }) | |
| (lib.optionalAttrs false ( | |
| backupCfg { | |
| shbOpt.backup.request = { | |
| user = "minecraft"; | |
| sourceDirectories = [ | |
| config.services.minecraft-server.dataDir | |
| ]; | |
| }; | |
| } "minecraft" | |
| )) | |
| { | |
| hardware.amdgpu.opencl.enable = true; | |
| environment.systemPackages = [ | |
| pkgs.clinfo | |
| pkgs.radeontop | |
| pkgs.rocmPackages.rocminfo | |
| ]; | |
| } | |
| # Using ollama to download models | |
| ( | |
| let | |
| defaultModel = "phi3:3.8b"; | |
| in | |
| lib.optionalAttrs true { | |
| shb.zfs.pools.root.datasets."safe/ollama".path = "/var/lib/private/ollama"; | |
| shb.sanoid.backup."root/safe/ollama" = { | |
| request = config.shb.zfs.pools.root.datasets."safe/ollama".datasetBackup.request; | |
| settings.useTemplate = [ "main" ]; | |
| }; | |
| services.ollama = { | |
| enable = true; | |
| # https://wiki.nixos.org/wiki/Ollama#AMD_GPU_with_open_source_driver | |
| package = pkgs.ollama-rocm; | |
| # The logs show: | |
| # compute=gfx900 | |
| # gfx900:xnack+ | |
| environmentVariables = { | |
| OLLAMA_DEBUG = "2"; | |
| # This is maybe not needed anymore | |
| HCC_AMDGPU_TARGET = "gfx900"; | |
| OLLAMA_KEEP_ALIVE = "24h"; | |
| OLLAMA_LOW_VRAM = "1"; # We have < 20Gb VRAM | |
| OLLAMA_OUTPUT_ON_CPU = "1"; | |
| GPU_MAX_HW_QUEUES = "1"; # prevents GPU hang at 100% | |
| }; | |
| # https://www.phoronix.com/forums/forum/software/programming-compilers/1459983-gcc-14-adds-gfx90c-for-openmp-offloading-to-apus-with-gfx9-vega-graphics?p=1460199#post1460199 | |
| rocmOverrideGfx = "9.0.0"; | |
| # https://ollama.com/library | |
| loadModels = [ | |
| defaultModel | |
| "llama3.1:8b" | |
| "llama3.2:3b" | |
| "mistral:7b-instruct" | |
| "nomic-embed-text:v1.5" | |
| "phi3:3.8b" | |
| "qwen2.5:7b-instruct" | |
| ]; | |
| }; | |
| # systemd.services.ollama.serviceConfig.TimeoutStartSec = "5min"; | |
| # systemd.services.ollama.postStart = '' | |
| # ${pkgs.ollama}/bin/ollama show ${defaultModel} && ${pkgs.ollama}/bin/ollama run ${defaultModel} "" || echo "Model is maybe still downloading" | |
| # ''; | |
| } | |
| ) | |
| (lib.optionalAttrs true { | |
| shb.certs.certs.letsencrypt.${domain}.extraDomains = [ "openwebui.${domain}" ]; | |
| shb.zfs.pools.root.datasets."safe/open-webui".path = "/var/lib/private/open-webui"; | |
| shb.sanoid.backup."root/safe/open-webui" = { | |
| request = config.shb.zfs.pools.root.datasets."safe/open-webui".datasetBackup.request; | |
| settings.useTemplate = [ "main" ]; | |
| }; | |
| shb.lldap.ensureUsers.${username}.groups = [ | |
| "open-webui_admin" | |
| "open-webui_user" | |
| ]; | |
| shb.open-webui = { | |
| enable = true; | |
| inherit domain; | |
| subdomain = "openwebui"; | |
| ssl = config.shb.certs.certs.letsencrypt.${domain}; | |
| # https://docs.openwebui.com/getting-started/env-configuration | |
| environment = { | |
| WEBUI_NAME = friendlyName; | |
| CORS_ALLOW_ORIGIN = "https://${config.shb.open-webui.subdomain}.${config.shb.open-webui.domain}"; | |
| # ENABLE_OPENAI_API = "True"; | |
| # OPENAI_API_BASE_URL = "http://127.0.0.1:${toString config.services.llama-cpp.port}"; | |
| OLLAMA_BASE_URL = "http://127.0.0.1:${toString config.services.ollama.port}"; | |
| OFFLINE_MODE = "True"; | |
| # https://github.com/open-webui/open-webui/discussions/8530#discussioncomment-12691642 | |
| # RAG_EMBEDDING_ENGINE = "openai"; | |
| RAG_OLLAMA_BASE_URL = "http://127.0.0.1:${toString config.services.ollama.port}"; | |
| RAG_EMBEDDING_MODEL = "nomic-embed-text:v1.5"; | |
| ENABLE_ASYNC_EMBEDDING = "true"; | |
| # ENABLE_WEB_SEARCH = "True"; | |
| # TODO: use https://github.com/searxng/searxng | |
| # TODO: setup audio | |
| }; | |
| sso = { | |
| enable = true; | |
| authEndpoint = "https://authelia.${domain}"; | |
| sharedSecret.result = config.shb.sops.secret."open-webui/sso_secret".result; | |
| sharedSecretForAuthelia.result = config.shb.sops.secret."open-webui/authelia/sso_secret".result; | |
| }; | |
| }; | |
| shb.sops.secret."open-webui/sso_secret".request = config.shb.open-webui.sso.sharedSecret.request; | |
| shb.sops.secret."open-webui/authelia/sso_secret" = { | |
| request = config.shb.open-webui.sso.sharedSecretForAuthelia.request; | |
| settings.key = "open-webui/sso_secret"; | |
| }; | |
| allowedUnfreePackagesRegexs = [ | |
| "open-webui" | |
| ]; | |
| shb.homepage.servicesGroups.Documents.services.OpenWebUI = { | |
| sortOrder = 4; | |
| dashboard.request = config.shb.open-webui.dashboard.request; | |
| settings.icon = "sh-open-webui"; | |
| }; | |
| }) | |
| (backupCfg { } "open-webui") | |
| (lib.optionalAttrs true { | |
| shb.certs.certs.letsencrypt.${domain}.extraDomains = [ "karakeep.${domain}" ]; | |
| shb.zfs.pools.root.datasets."safe/karakeep".path = "/var/lib/karakeep"; | |
| shb.zfs.pools.root.datasets."safe/meilisearch".path = "/var/lib/private/meilisearch"; | |
| shb.sanoid.backup."root/safe/karakeep" = { | |
| request = config.shb.zfs.pools.root.datasets."safe/karakeep".datasetBackup.request; | |
| settings.useTemplate = [ "main" ]; | |
| }; | |
| shb.sanoid.backup."root/safe/meilisearch" = { | |
| request = config.shb.zfs.pools.root.datasets."safe/meilisearch".datasetBackup.request; | |
| settings.useTemplate = [ "main" ]; | |
| }; | |
| shb.lldap.ensureUsers.${username}.groups = [ | |
| "karakeep_user" | |
| ]; | |
| shb.karakeep = { | |
| enable = true; | |
| port = 12445; | |
| inherit domain; | |
| subdomain = "karakeep"; | |
| ssl = config.shb.certs.certs.letsencrypt.${domain}; | |
| nextauthSecret.result = config.shb.sops.secret."karakeep/nextauth_secret".result; | |
| meilisearchMasterKey.result = config.shb.sops.secret."karakeep/meilisearch_master_key".result; | |
| # https://docs.karakeep.app/configuration/ | |
| environment = { | |
| OLLAMA_BASE_URL = "http://127.0.0.1:${toString config.services.ollama.port}"; | |
| INFERENCE_TEXT_MODEL = "deepseek-r1:1.5b"; | |
| INFERENCE_IMAGE_MODEL = "llava"; | |
| EMBEDDING_TEXT_MODEL = "nomic-embed-text:v1.5"; | |
| INFERENCE_ENABLE_AUTO_SUMMARIZATION = "true"; | |
| INFERENCE_JOB_TIMEOUT_SEC = "200"; | |
| }; | |
| sso = { | |
| enable = true; | |
| authEndpoint = "https://authelia.${domain}"; | |
| sharedSecret.result = config.shb.sops.secret."karakeep/sso_secret".result; | |
| sharedSecretForAuthelia.result = config.shb.sops.secret."karakeep/authelia/sso_secret".result; | |
| }; | |
| }; | |
| shb.sops.secret."karakeep/meilisearch_master_key".request = | |
| config.shb.karakeep.meilisearchMasterKey.request; | |
| shb.sops.secret."karakeep/nextauth_secret".request = config.shb.karakeep.nextauthSecret.request; | |
| shb.sops.secret."karakeep/sso_secret".request = config.shb.karakeep.sso.sharedSecret.request; | |
| shb.sops.secret."karakeep/authelia/sso_secret" = { | |
| request = config.shb.karakeep.sso.sharedSecretForAuthelia.request; | |
| settings.key = "karakeep/sso_secret"; | |
| }; | |
| services.meilisearch.package = pkgs.meilisearch; | |
| systemd.services.meilisearch.environment.RUST_BACKTRACE = "1"; | |
| systemd.services.meilisearch.serviceConfig.ProcSubset = lib.mkForce "all"; | |
| shb.homepage.servicesGroups.Documents.services.Karakeep = { | |
| sortOrder = 3; | |
| dashboard.request = config.shb.karakeep.dashboard.request; | |
| apiKey.result = config.shb.sops.secret."karakeep/homepageApiKey".result; | |
| }; | |
| shb.sops.secret."karakeep/homepageApiKey".request = | |
| config.shb.homepage.servicesGroups.Documents.services.Karakeep.apiKey.request; | |
| }) | |
| (backupCfg { } "karakeep") | |
| (lib.optionalAttrs false { | |
| shb.authelia.oidcClients = [ | |
| { | |
| client_id = "kbyuFDidLLm280LIwVFiazOqjO3ty8KH"; | |
| client_secret.source = pkgs.writeText "karakeep_client_secret" "60Op4HFM0I8ajz0WdiStAbziZ-VFQttXuxixHHs2R7r7-CW8GR79l-mmLqMhc-Sa"; | |
| scopes = [ | |
| "openid" | |
| "email" | |
| "profile" | |
| "groups" | |
| ]; | |
| token_endpoint_auth_method = "client_secret_post"; | |
| redirect_uris = [ | |
| "https://openidconnect.net/callback" | |
| ]; | |
| } | |
| ]; | |
| }) | |
| { | |
| shb.certs.certs.letsencrypt.${domain}.extraDomains = [ | |
| "${config.shb.firefly-iii.subdomain}.${config.shb.firefly-iii.domain}" | |
| "${config.shb.firefly-iii.importer.subdomain}.${config.shb.firefly-iii.domain}" | |
| ]; | |
| shb.zfs.pools.root.datasets."safe/firefly-iii".path = config.shb.firefly-iii.impermanence; | |
| shb.sanoid.backup."root/safe/firefly-iii" = { | |
| request = config.shb.zfs.pools.root.datasets."safe/firefly-iii".datasetBackup.request; | |
| settings.useTemplate = [ "main" ]; | |
| }; | |
| shb.lldap.ensureUsers.${username}.groups = [ | |
| config.shb.firefly-iii.ldap.userGroup | |
| config.shb.firefly-iii.ldap.adminGroup | |
| ]; | |
| shb.firefly-iii = { | |
| enable = true; | |
| debug = false; | |
| appKey.result = config.shb.sops.secret."firefly-iii/appKey".result; | |
| dbPassword.result = config.shb.sops.secret."firefly-iii/dbPassword".result; | |
| inherit domain; | |
| subdomain = "firefly-iii"; | |
| siteOwnerEmail = email; | |
| ssl = config.shb.certs.certs.letsencrypt.${domain}; | |
| smtp = { | |
| host = "smtp.mailgun.org"; | |
| port = 587; | |
| username = "postmaster@mg.${domain}"; | |
| from_address = "firefly-iii@${domain}"; | |
| password.result = config.shb.sops.secret."firefly-iii/smtpPassword".result; | |
| }; | |
| sso = { | |
| enable = true; | |
| authEndpoint = "https://${config.shb.authelia.subdomain}.${config.shb.authelia.domain}"; | |
| }; | |
| importer = { | |
| firefly-iii-accessToken.result = config.shb.sops.secret."firefly-iii/importerAccessToken".result; | |
| }; | |
| }; | |
| shb.sops.secret."firefly-iii/appKey".request = config.shb.firefly-iii.appKey.request; | |
| shb.sops.secret."firefly-iii/dbPassword".request = config.shb.firefly-iii.dbPassword.request; | |
| shb.sops.secret."firefly-iii/smtpPassword" = { | |
| request = config.shb.firefly-iii.smtp.password.request; | |
| settings.key = "authelia/smtp_password"; | |
| }; | |
| shb.sops.secret."firefly-iii/importerAccessToken".request = | |
| config.shb.firefly-iii.importer.firefly-iii-accessToken.request; | |
| shb.homepage.servicesGroups.Finance.services.Firefly-iii = { | |
| sortOrder = 2; | |
| dashboard.request = config.shb.firefly-iii.dashboard.request; | |
| apiKey.result = config.shb.sops.secret."firefly-iii/homepageApiKey".result; | |
| settings.widget.type = "firefly"; | |
| }; | |
| shb.sops.secret."firefly-iii/homepageApiKey".request = | |
| config.shb.homepage.servicesGroups.Finance.services.Firefly-iii.apiKey.request; | |
| } | |
| (backupCfg { } "firefly-iii") | |
| { | |
| shb.certs.certs.letsencrypt.${domain}.extraDomains = [ | |
| "${config.shb.mailserver.subdomain}.${config.shb.mailserver.domain}" | |
| ]; | |
| shb.lldap.ensureUsers.${username}.groups = [ | |
| config.shb.mailserver.ldap.userGroup | |
| ]; | |
| shb.zfs.pools.root.datasets."safe/mailserver".path = config.shb.mailserver.impermanence.mail; | |
| shb.zfs.pools.root.datasets."safe/mailserver/sieve".path = config.shb.mailserver.impermanence.sieve; | |
| shb.zfs.pools.root.datasets."safe/mailserver/dkim".path = config.shb.mailserver.impermanence.dkim; | |
| shb.sanoid.backup."root/safe/mailserver" = { | |
| request = config.shb.zfs.pools.root.datasets."safe/mailserver".datasetBackup.request; | |
| settings.useTemplate = [ "main" ]; | |
| }; | |
| shb.sanoid.backup."root/safe/mailserver/sieve" = { | |
| request = config.shb.zfs.pools.root.datasets."safe/mailserver/sieve".datasetBackup.request; | |
| settings.useTemplate = [ "main" ]; | |
| }; | |
| shb.sanoid.backup."root/safe/mailserver/dkim" = { | |
| request = config.shb.zfs.pools.root.datasets."safe/mailserver/dkim".datasetBackup.request; | |
| settings.useTemplate = [ "main" ]; | |
| }; | |
| shb.mailserver = { | |
| enable = true; | |
| inherit domain; | |
| subdomain = "imap"; | |
| ssl = config.shb.certs.certs.letsencrypt.${domain}; | |
| imapSync = { | |
| syncTimer = "10s"; | |
| debug = false; | |
| accounts.fastmail = { | |
| host = "imap.fastmail.com"; | |
| port = 993; | |
| username = email; | |
| password.result = config.shb.sops.secret."mailserver/imap/fastmail/password".result; | |
| mapSpecialJunk = "Spam"; | |
| }; | |
| }; | |
| smtpRelay = { | |
| host = "smtp.fastmail.com"; | |
| port = 587; | |
| username = email; | |
| password.result = config.shb.sops.secret."mailserver/smtp/fastmail/password".result; | |
| }; | |
| ldap = { | |
| enable = true; | |
| host = "127.0.0.1"; | |
| port = config.shb.lldap.ldapPort; | |
| dcdomain = config.shb.lldap.dcdomain; | |
| adminName = "admin"; | |
| adminPassword.result = config.shb.sops.secret."mailserver/ldap_admin_password".result; | |
| account = "fastmail"; | |
| }; | |
| }; | |
| mailserver.mailboxes = { | |
| Drafts = { | |
| auto = "subscribe"; | |
| special_use = "\\Drafts"; | |
| }; | |
| Junk = { | |
| auto = "subscribe"; | |
| special_use = "\\Junk"; | |
| }; | |
| Sent = { | |
| auto = "subscribe"; | |
| special_use = "\\Sent"; | |
| }; | |
| Trash = { | |
| auto = "subscribe"; | |
| special_use = "\\Trash"; | |
| }; | |
| Archive = { | |
| auto = "subscribe"; | |
| special_use = "\\Archive"; | |
| }; | |
| }; | |
| shb.sops.secret."mailserver/smtp/fastmail/password".request = | |
| config.shb.mailserver.smtpRelay.password.request; | |
| shb.sops.secret."mailserver/imap/fastmail/password".request = | |
| config.shb.mailserver.imapSync.accounts.fastmail.password.request; | |
| shb.sops.secret."mailserver/ldap_admin_password" = { | |
| request = config.shb.mailserver.ldap.adminPassword.request; | |
| settings.key = "lldap/user_password"; | |
| }; | |
| shb.homepage.servicesGroups.Admin.services.Email = { | |
| sortOrder = 10; | |
| dashboard.request = config.shb.mailserver.dashboard.request; | |
| }; | |
| } | |
| (backupCfg { } "mailserver") | |
| (backupCfg { | |
| shbOpt.backup.request = config.shb.mailserver.backupDKIM.request; | |
| } "mailserver-dkim") | |
| { | |
| shb.certs.certs.letsencrypt.${domain}.extraDomains = [ | |
| "${config.shb.homepage.subdomain}.${config.shb.homepage.domain}" | |
| ]; | |
| shb.lldap.ensureUsers.${username}.groups = [ | |
| config.shb.homepage.ldap.userGroup | |
| ]; | |
| shb.homepage = { | |
| enable = true; | |
| subdomain = "home"; | |
| inherit domain; | |
| ssl = config.shb.certs.certs.letsencrypt.${domain}; | |
| sso = { | |
| enable = true; | |
| authEndpoint = "https://${config.shb.authelia.subdomain}.${config.shb.authelia.domain}"; | |
| }; | |
| servicesGroups = { | |
| Home.sortOrder = 1; | |
| Documents.sortOrder = 2; | |
| Finance.sortOrder = 3; | |
| Media.sortOrder = 4; | |
| Admin.sortOrder = 5; | |
| }; | |
| }; | |
| services.homepage-dashboard = { | |
| settings = { | |
| statusStyle = "dot"; | |
| disableIndexing = true; | |
| }; | |
| widgets = [ | |
| { | |
| datetime = { | |
| locale = "fr"; | |
| format = { | |
| dateStyle = "long"; | |
| timeStyle = "long"; | |
| }; | |
| }; | |
| } | |
| ]; | |
| }; | |
| } | |
| { | |
| users.users.nextcloud.extraGroups = [ "syncthing" ]; | |
| shb.zfs.pools.root.datasets."syncthing" = { | |
| path = "/srv/syncthing"; | |
| mode = "ug=rwx,g+s,o="; | |
| defaultACLs = "g:syncthing:rwX"; | |
| owner = "syncthing"; | |
| group = "syncthing"; | |
| }; | |
| shb.sanoid.backup."root/syncthing" = { | |
| request = config.shb.zfs.pools.root.datasets."syncthing".datasetBackup.request; | |
| settings.useTemplate = [ "main" ]; | |
| }; | |
| services.syncthing = { | |
| enable = true; | |
| openDefaultPorts = true; | |
| guiAddress = "127.0.0.1:8384"; | |
| settings.guiPasswordFile = config.shb.sops.secret."syncthing/gui/password".result.path; | |
| key = config.shb.sops.secret."syncthing/certs/key".result.path; | |
| cert = config.shb.sops.secret."syncthing/certs/cert".result.path; | |
| settings = { | |
| devices = { | |
| "batocera" = { | |
| id = "..."; | |
| }; | |
| }; | |
| folders = { | |
| "pygame" = { | |
| path = "/srv/syncthing/pygame"; | |
| devices = [ "batocera" ]; | |
| }; | |
| }; | |
| }; | |
| }; | |
| shb.sops.secret."syncthing/gui/password".request = { | |
| owner = "syncthing"; | |
| }; | |
| shb.sops.secret."syncthing/certs/key".request = { | |
| owner = "syncthing"; | |
| }; | |
| shb.sops.secret."syncthing/certs/cert".request = { | |
| owner = "syncthing"; | |
| }; | |
| networking.firewall.allowedTCPPorts = [ 8384 ]; | |
| } | |
| ]; | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| { | |
| description = "ibizaman's NixOS configuration"; | |
| inputs = { | |
| nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; | |
| flake-parts.url = "github:hercules-ci/flake-parts"; | |
| home-manager.url = "github:nix-community/home-manager"; | |
| home-manager.inputs.nixpkgs.follows = "nixpkgs"; | |
| selfhostblocks.url = "github:ibizaman/selfhostblocks"; | |
| skarabox.url = "github:ibizaman/skarabox"; | |
| nixos-generators.url = "github:nix-community/nixos-generators"; | |
| nixos-generators.inputs.nixpkgs.follows = "nixpkgs"; | |
| nixos-anywhere.url = "github:nix-community/nixos-anywhere"; | |
| nixos-anywhere.inputs.nixpkgs.follows = "nixpkgs"; | |
| nixos-facter-modules.url = "github:numtide/nixos-facter-modules"; | |
| deploy-rs.url = "github:serokell/deploy-rs"; | |
| colmena.url = "github:zhaofengli/colmena"; | |
| sops-nix.url = "github:Mic92/sops-nix"; | |
| nix-minecraft.url = "github:Infinidoge/nix-minecraft"; | |
| }; | |
| outputs = | |
| inputs@{ | |
| self, | |
| nixpkgs, | |
| deploy-rs, | |
| selfhostblocks, | |
| flake-parts, | |
| skarabox, | |
| ... | |
| }: | |
| flake-parts.lib.mkFlake { inherit inputs; } ( | |
| let | |
| system = "x86_64-linux"; | |
| in | |
| { | |
| imports = [ | |
| skarabox.flakeModules.default | |
| skarabox.flakeModules.deploy-rs | |
| ]; | |
| systems = [ | |
| "x86_64-linux" | |
| "aarch64-linux" | |
| ]; | |
| skarabox.hosts = { | |
| ${hostname} = { | |
| inherit system; | |
| nixpkgs = inputs.selfhostblocks.lib.${system}.patchedNixpkgs; | |
| hostKeyPath = "./machines/${hostname}_host_key"; | |
| hostKeyPub = ./machines/${hostname}/host_key.pub; | |
| ip = "192.168.1.50"; | |
| sshBootPort = 22346; | |
| sshPort = 22345; | |
| knownHosts = ./machines/${hostname}/known_hosts; | |
| knownHostsPath = "./machines/${hostname}/known_hosts"; | |
| sshPrivateKeyPath = "./machines/${hostname}/ssh-key"; | |
| secretsFilePath = "./secrets/secrets.yaml"; | |
| extraSecretsPassphrasesPath = { | |
| "backup_passphrase" = "['${hostname}']['disks']['backupPassphrase']"; | |
| }; | |
| modules = [ | |
| inputs.selfhostblocks.nixosModules.default | |
| inputs.selfhostblocks.nixosModules.sops | |
| inputs.sops-nix.nixosModules.default | |
| inputs.nix-minecraft.nixosModules.minecraft-servers | |
| { | |
| nixpkgs.overlays = [ inputs.nix-minecraft.overlay ]; | |
| } | |
| self.nixosModules.${hostname} | |
| ]; | |
| }; | |
| }; | |
| perSystem = | |
| { | |
| self', | |
| system, | |
| inputs', | |
| pkgs, | |
| ... | |
| }: | |
| { | |
| formatter = nixpkgs.legacyPackages.${system}.nixfmt-tree; | |
| }; | |
| flake = { | |
| nixosModules.${hostname} = { | |
| imports = [ | |
| ./machines/${hostname}/default.nix | |
| { | |
| nixpkgs.config.permittedInsecurePackages = [ | |
| # https://github.com/NixOS/nixpkgs/issues/360592 | |
| "aspnetcore-runtime-6.0.36" | |
| "aspnetcore-runtime-wrapped-6.0.36" | |
| ]; | |
| } | |
| ]; | |
| }; | |
| colmena = { | |
| meta = | |
| let | |
| system = "x86_64-linux"; | |
| patches = [ | |
| # { | |
| # url = "https://patch-diff.githubusercontent.com/raw/NixOS/nixpkgs/pull/268168.patch"; | |
| # hash = "sha256-WIMDnmZV0eL1eFVD0ldHUBrulZWsjdFOmcN4i8+RgFA="; | |
| # } | |
| # { | |
| # url = "https://patch-diff.githubusercontent.com/raw/NixOS/nixpkgs/pull/365751.patch"; | |
| # hash = "sha256-2egcjmS9pxtVSUe696nq920uLSP6/kuw0KSCLfTneU0="; | |
| # } | |
| ]; | |
| originPkgs = selfhostblocks.inputs.nixpkgs; | |
| nixpkgs' = originPkgs.legacyPackages.${system}.applyPatches { | |
| name = "nixpkgs-patched"; | |
| src = originPkgs; | |
| patches = | |
| selfhostblocks.patches.${system} ++ map originPkgs.legacyPackages.${system}.fetchpatch patches; | |
| }; | |
| in | |
| { | |
| nixpkgs = import nixpkgs' { | |
| inherit system; | |
| config = { | |
| permittedInsecurePackages = [ | |
| # https://github.com/NixOS/nixpkgs/issues/360592 | |
| "aspnetcore-runtime-6.0.36" | |
| "aspnetcore-runtime-wrapped-6.0.36" | |
| ]; | |
| }; | |
| }; | |
| specialArgs = inputs; | |
| }; | |
| }; | |
| }; | |
| } | |
| ); | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment