From 72ce184bd43d09c2b7640ceea24f8c23da88ad05 Mon Sep 17 00:00:00 2001 From: Chris Toph Date: Tue, 29 Apr 2025 11:14:59 -0400 Subject: [PATCH] Refactor and reorganize cloud and proxy configs - Introduces new modules for cloud, backup, and NFS services - Removes deprecated Caddy and cloudflared configs - Migrate /pool from SSHFS to NFS - Migrate filerun and SnapRAID configurations to cloud only for better modularity --- home/toph/cloud/default.nix | 10 ++ hosts/common/optional/caddy/cloud.nix | 16 -- hosts/common/optional/caddy/proxy.nix | 10 -- .../optional/containers/komodo/default.nix | 4 +- hosts/common/optional/system/pool.nix | 30 ++-- hosts/nixos/cloud/config/borg.nix | 140 ++++++++++++++++++ hosts/nixos/cloud/config/caddy.nix | 19 +++ hosts/nixos/cloud/config/default.nix | 4 + hosts/nixos/cloud/config/filerun/compose.yml | 38 +++++ hosts/nixos/cloud/config/filerun/default.nix | 119 +++++++++++++++ hosts/nixos/cloud/config/nfs.nix | 23 +++ hosts/nixos/cloud/config/snapraid.nix | 122 +++++++++++++++ hosts/nixos/cloud/default.nix | 31 ++-- hosts/nixos/cloud/hardware.nix | 35 ++++- hosts/nixos/proxy/config/caddy.nix | 13 ++ .../proxy/config}/cloudflared.nix | 0 hosts/nixos/proxy/config/default.nix | 4 + hosts/nixos/proxy/default.nix | 9 +- secrets.nix | Bin 12315 -> 13184 bytes 19 files changed, 569 insertions(+), 58 deletions(-) create mode 100644 home/toph/cloud/default.nix delete mode 100644 hosts/common/optional/caddy/cloud.nix delete mode 100644 hosts/common/optional/caddy/proxy.nix create mode 100644 hosts/nixos/cloud/config/borg.nix create mode 100644 hosts/nixos/cloud/config/caddy.nix create mode 100644 hosts/nixos/cloud/config/default.nix create mode 100644 hosts/nixos/cloud/config/filerun/compose.yml create mode 100644 hosts/nixos/cloud/config/filerun/default.nix create mode 100644 hosts/nixos/cloud/config/nfs.nix create mode 100644 hosts/nixos/cloud/config/snapraid.nix create mode 100644 hosts/nixos/proxy/config/caddy.nix rename hosts/{common/optional/containers => nixos/proxy/config}/cloudflared.nix (100%) create mode 100644 hosts/nixos/proxy/config/default.nix diff --git a/home/toph/cloud/default.nix b/home/toph/cloud/default.nix new file mode 100644 index 0000000..fef0923 --- /dev/null +++ b/home/toph/cloud/default.nix @@ -0,0 +1,10 @@ +{ + pkgs, + ... +}: +{ + imports = [ + ## Required Configs ## + ../common/core # required + ]; +} diff --git a/hosts/common/optional/caddy/cloud.nix b/hosts/common/optional/caddy/cloud.nix deleted file mode 100644 index 810c7a3..0000000 --- a/hosts/common/optional/caddy/cloud.nix +++ /dev/null @@ -1,16 +0,0 @@ -{ - services.caddy.virtualHosts = { - "drive.ryot.foo" = { - useACMEHost = "ryot.foo"; - extraConfig = '' - reverse_proxy http://localhost:8181 { - header_up Host {host} - # header_up X-Forwarded-For {remote} - # header_up X-Forwarded-Proto {scheme} - # header_up X-Forwarded-Protocol {scheme} - # header_up X-Forwarded-Port {server_port} - } - ''; - }; - }; -} diff --git a/hosts/common/optional/caddy/proxy.nix b/hosts/common/optional/caddy/proxy.nix deleted file mode 100644 index 5cf77b4..0000000 --- a/hosts/common/optional/caddy/proxy.nix +++ /dev/null @@ -1,10 +0,0 @@ -{ - services.caddy.virtualHosts = { - "cloudflared.ryot.foo" = { - useACMEHost = "ryot.foo"; - extraConfig = '' - reverse_proxy localhost:14333 - ''; - }; - }; -} diff --git a/hosts/common/optional/containers/komodo/default.nix b/hosts/common/optional/containers/komodo/default.nix index 76981b6..6bbca42 100644 --- a/hosts/common/optional/containers/komodo/default.nix +++ b/hosts/common/optional/containers/komodo/default.nix @@ -1,8 +1,8 @@ # Auto-generated using compose2nix v0.3.1. { - pkgs, + config, lib, - admin, + pkgs, ... }: let diff --git a/hosts/common/optional/system/pool.nix b/hosts/common/optional/system/pool.nix index a13b29b..c4d14e1 100644 --- a/hosts/common/optional/system/pool.nix +++ b/hosts/common/optional/system/pool.nix @@ -2,12 +2,8 @@ let username = config.hostSpec.username; homeDir = config.hostSpec.home; - pve-key = config.secretsSpec.ssh.privateKeys.pve; in { - # For less permission issues with SSHFS - programs.fuse.userAllowOther = true; - # Create the directories if they do not exist systemd.tmpfiles.rules = [ "d /pool 2775 ${username} ryot -" @@ -17,14 +13,16 @@ in # File system configuration fileSystems = { "/pool" = { - device = "${username}@cloud:/pool"; - fsType = "sshfs"; + device = "cloud:/"; + fsType = "nfs"; options = [ - "defaults" - "reconnect" "_netdev" - "allow_other" - "identityfile=${pve-key}" + "defaults" + "nfsvers=4.2" + "noacl" + "noatime" + "nofail" + "sec=sys" ]; }; @@ -37,4 +35,16 @@ in ]; }; }; + + # Ensure NFS client support is complete + boot.supportedFilesystems = [ "nfs" ]; + # services.rpcbind.enable = true; + + # Optional: Configure ID mapping if needed + services.nfs.idmapd.settings = { + General = { + Domain = "local"; # Must match on server and client + Verbosity = 0; + }; + }; } diff --git a/hosts/nixos/cloud/config/borg.nix b/hosts/nixos/cloud/config/borg.nix new file mode 100644 index 0000000..2e12aae --- /dev/null +++ b/hosts/nixos/cloud/config/borg.nix @@ -0,0 +1,140 @@ +{ + config, + lib, + pkgs, + ... +}: + +let + # Borg backup destinations + dockerStorageRepo = "/pool/Backups/DockerStorage"; + forgejoRepo = "/pool/Backups/forgejo"; + + # Common borg backup settings + borgCommonSettings = '' + # Don't use cache to avoid issues with concurrent backups + export BORG_UNKNOWN_UNENCRYPTED_REPO_ACCESS_IS_OK=yes + + # Set this for non-interactive use + export BORG_NON_INTERACTIVE=yes + ''; + + # Initialize a repo if it doesn't exist + initRepo = repo: '' + if [ ! -d "${repo}" ]; then + mkdir -p "${repo}" + ${pkgs.borgbackup}/bin/borg init --encryption=none "${repo}" + fi + ''; + +in +{ + # Make sure borg is installed + environment.systemPackages = [ pkgs.borgbackup ]; + + # Docker Storage Backup Service + systemd.services.backup-docker-storage = { + description = "Backup Docker storage directory with Borg"; + + path = with pkgs; [ + borgbackup + coreutils + ]; + + script = '' + ${borgCommonSettings} + + # Initialize repository if needed + ${initRepo dockerStorageRepo} + + # Create backup + ${pkgs.borgbackup}/bin/borg create \ + --stats \ + --compression zstd,15 \ + --exclude '*.tmp' \ + --exclude '*/tmp/*' \ + ${dockerStorageRepo}::docker-{now:%Y-%m-%d_%H%M%S} \ + /mnt/drive1/DockerStorage + + # Prune old backups + ${pkgs.borgbackup}/bin/borg prune \ + --keep-daily 7 \ + --keep-weekly 4 \ + --keep-monthly 3 \ + ${dockerStorageRepo} + ''; + + serviceConfig = { + Type = "oneshot"; + IOSchedulingClass = "idle"; + CPUSchedulingPolicy = "idle"; + Nice = 19; + }; + }; + + # Docker Storage Backup Timer (Weekly on Monday at 4am) + systemd.timers.backup-docker-storage = { + description = "Timer for Docker Storage Backup"; + + wantedBy = [ "timers.target" ]; + + timerConfig = { + OnCalendar = "Mon *-*-* 04:00:00"; + Persistent = true; # Run backup if system was off during scheduled time + RandomizedDelaySec = "5min"; # Add randomized delay + }; + }; + + # Forgejo Backup Service + systemd.services.backup-forgejo = { + description = "Backup Forgejo directory with Borg"; + + path = with pkgs; [ + borgbackup + coreutils + ]; + + script = '' + ${borgCommonSettings} + + # Initialize repository if needed + ${initRepo forgejoRepo} + + # Create backup + ${pkgs.borgbackup}/bin/borg create \ + --stats \ + --compression zstd,15 \ + --exclude '*.tmp' \ + --exclude '*/tmp/*' \ + ${forgejoRepo}::forgejo-{now:%Y-%m-%d_%H%M%S} \ + /pool/forgejo + + # Prune old backups + ${pkgs.borgbackup}/bin/borg prune \ + --keep-daily 14 \ + --keep-weekly 4 \ + --keep-monthly 3 \ + ${forgejoRepo} + ''; + + serviceConfig = { + Type = "oneshot"; + IOSchedulingClass = "idle"; + CPUSchedulingPolicy = "idle"; + Nice = 19; + }; + }; + + # Forgejo Backup Timer (Every 2 days at 4am) + systemd.timers.backup-forgejo = { + description = "Timer for Forgejo Backup"; + + wantedBy = [ "timers.target" ]; + + timerConfig = { + OnCalendar = "*-*-1/2 04:00:00"; # Every 2 days at 4am + Persistent = true; + RandomizedDelaySec = "5min"; + }; + }; +} diff --git a/hosts/nixos/cloud/config/caddy.nix b/hosts/nixos/cloud/config/caddy.nix new file mode 100644 index 0000000..88ee2da --- /dev/null +++ b/hosts/nixos/cloud/config/caddy.nix @@ -0,0 +1,19 @@ +{ + services.caddy = { + enable = true; + virtualHosts = { + "drive.ryot.foo" = { + useACMEHost = "ryot.foo"; + extraConfig = '' + reverse_proxy http://localhost:8282 { + header_up Host {host} + # header_up X-Forwarded-For {remote} + # header_up X-Forwarded-Proto {scheme} + # header_up X-Forwarded-Protocol {scheme} + # header_up X-Forwarded-Port {server_port} + } + ''; + }; + }; + }; +} diff --git a/hosts/nixos/cloud/config/default.nix b/hosts/nixos/cloud/config/default.nix new file mode 100644 index 0000000..b23287b --- /dev/null +++ b/hosts/nixos/cloud/config/default.nix @@ -0,0 +1,4 @@ +{ lib, ... }: +{ + imports = lib.custom.scanPaths ./.; +} diff --git a/hosts/nixos/cloud/config/filerun/compose.yml b/hosts/nixos/cloud/config/filerun/compose.yml new file mode 100644 index 0000000..97f617a --- /dev/null +++ b/hosts/nixos/cloud/config/filerun/compose.yml @@ -0,0 +1,38 @@ +name: filerun +services: + db: + image: mariadb:10.11 + user: 1001:1004 + environment: + MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASS + MYSQL_USER: ${DB_USER} + MYSQL_PASSWORD: ${DB_PASS} + MYSQL_DATABASE: ${DB_NAME} + restart: unless-stopped + volumes: + - /pool/filerun/db:/var/lib/mysql + + web: + image: filerun/filerun:8.1 + user: root + environment: + FR_DB_HOST: db + FR_DB_PORT: ${DB_PORT} + FR_DB_NAME: ${DB_NAME} + FR_DB_USER: ${DB_USER} + FR_DB_PASS: ${DB_PASS} + APACHE_RUN_USER: + APACHE_RUN_USER_ID: 1001 + APACHE_RUN_GROUP: + APACHE_RUN_GROUP_ID: 1004 + depends_on: + - db + links: + - db:db + ports: + - "8181:80" + restart: unless-stopped + volumes: + - /pool/filerun/html:/var/www/html + - /pool/filerun/user-files:/user-files + - /pool/:/pool diff --git a/hosts/nixos/cloud/config/filerun/default.nix b/hosts/nixos/cloud/config/filerun/default.nix new file mode 100644 index 0000000..f08f199 --- /dev/null +++ b/hosts/nixos/cloud/config/filerun/default.nix @@ -0,0 +1,119 @@ +# Auto-generated using compose2nix v0.3.1. +{ + config, + lib, + pkgs, + ... +}: +let + env = config.secretsSpec.docker.filerun; +in +{ + # Runtime + virtualisation.docker = { + enable = true; + autoPrune.enable = true; + }; + virtualisation.oci-containers.backend = "docker"; + + # Containers + virtualisation.oci-containers.containers."filerun-db" = { + image = "mariadb:10.11"; + environment = env; + volumes = [ + "/pool/filerun/db:/var/lib/mysql:rw" + ]; + user = "1001:1004"; + log-driver = "journald"; + extraOptions = [ + "--network-alias=db" + "--network=filerun_default" + ]; + }; + systemd.services."docker-filerun-db" = { + serviceConfig = { + Restart = lib.mkOverride 90 "always"; + RestartMaxDelaySec = lib.mkOverride 90 "1m"; + RestartSec = lib.mkOverride 90 "100ms"; + RestartSteps = lib.mkOverride 90 9; + }; + after = [ + "docker-network-filerun_default.service" + ]; + requires = [ + "docker-network-filerun_default.service" + ]; + partOf = [ + "docker-compose-filerun-root.target" + ]; + wantedBy = [ + "docker-compose-filerun-root.target" + ]; + }; + virtualisation.oci-containers.containers."filerun-web" = { + image = "filerun/filerun:8.1"; + environment = env; + volumes = [ + "/pool/:/pool:rw" + "/pool/filerun/html:/var/www/html:rw" + "/pool/filerun/user-files:/user-files:rw" + ]; + ports = [ + "8282:80/tcp" + ]; + dependsOn = [ + "filerun-db" + ]; + user = "root"; + log-driver = "journald"; + extraOptions = [ + "--network-alias=web" + "--network=filerun_default" + ]; + }; + systemd.services."docker-filerun-web" = { + serviceConfig = { + Restart = lib.mkOverride 90 "always"; + RestartMaxDelaySec = lib.mkOverride 90 "1m"; + RestartSec = lib.mkOverride 90 "100ms"; + RestartSteps = lib.mkOverride 90 9; + }; + after = [ + "docker-network-filerun_default.service" + ]; + requires = [ + "docker-network-filerun_default.service" + ]; + partOf = [ + "docker-compose-filerun-root.target" + ]; + wantedBy = [ + "docker-compose-filerun-root.target" + ]; + }; + + # Networks + systemd.services."docker-network-filerun_default" = { + path = [ pkgs.docker ]; + serviceConfig = { + Type = "oneshot"; + RemainAfterExit = true; + ExecStop = "docker network rm -f filerun_default"; + }; + script = '' + docker network inspect filerun_default || docker network create filerun_default + ''; + partOf = [ "docker-compose-filerun-root.target" ]; + wantedBy = [ "docker-compose-filerun-root.target" ]; + }; + + # Root service + # When started, this will automatically create all resources and start + # the containers. When stopped, this will teardown all resources. + systemd.targets."docker-compose-filerun-root" = { + unitConfig = { + Description = "Root target generated by compose2nix."; + }; + wantedBy = [ "multi-user.target" ]; + }; +} diff --git a/hosts/nixos/cloud/config/nfs.nix b/hosts/nixos/cloud/config/nfs.nix new file mode 100644 index 0000000..2b4bc8c --- /dev/null +++ b/hosts/nixos/cloud/config/nfs.nix @@ -0,0 +1,23 @@ +{ config, lib, ... }: +{ + # Install and configure NFS server + services.nfs.server = { + enable = true; + + exports = '' + # Pool export - seen as root '/' by the client + /pool *(rw,insecure,no_subtree_check,no_root_squash,fsid=0,anonuid=1000,anongid=1004) + ''; + + extraNfsdConfig = "vers=4,4.1,4.2"; + }; + + # Ensure NFS client support is complete + # services.rpcbind.enable = true; + services.nfs.idmapd.settings = { + General = { + Domain = "local"; + Verbosity = 0; + }; + }; +} diff --git a/hosts/nixos/cloud/config/snapraid.nix b/hosts/nixos/cloud/config/snapraid.nix new file mode 100644 index 0000000..fa2b31e --- /dev/null +++ b/hosts/nixos/cloud/config/snapraid.nix @@ -0,0 +1,122 @@ +{ + pkgs, + inputs, + config, + ... +}: +let + + apprise-url = config.secretsSpec.api.apprise-url; + + snapraid-aio = inputs.snapraid-aio.nixosModules.default; + snapraid-aio-config = pkgs.writeTextFile { + name = "snapraid-aio.config"; + text = '' + CONFIG_VERSION="3.4" + CHECK_UPDATES=1 + + # Notification settings + APPRISE=0 + APPRISE_URL="" + APPRISE_ATTACH=1 + APPRISE_BIN="${pkgs.apprise}/bin/apprise" + APPRISE_EMAIL=1 + APPRISE_EMAIL_URL="${apprise-url}" + TELEGRAM=0 + DISCORD=0 + + # Thresholds for sync operations + DEL_THRESHOLD=500 + UP_THRESHOLD=500 + IGNORE_PATTERN="" + ADD_DEL_THRESHOLD=0 + SYNC_WARN_THRESHOLD=0 + + # Scrub settings + SCRUB_PERCENT=5 + SCRUB_AGE=10 + SCRUB_NEW=1 + SCRUB_DELAYED_RUN=0 + + # Performance and behavior settings + PREHASH=1 + FORCE_ZERO=0 + SPINDOWN=0 + VERBOSITY=1 + RETENTION_DAYS=30 + + # Logging settings + SNAPRAID_LOG_DIR="/var/log/snapraid" + SMART_LOG=1 + SMART_LOG_NOTIFY=0 + SNAP_STATUS=1 + SNAP_STATUS_NOTIFY=1 + + # Critical paths + SNAPRAID_CONF="/etc/snapraid.conf" + SNAPRAID_BIN="${pkgs.snapraid}/bin/snapraid" + + # Email settings (optional - uncomment and configure if needed) + # EMAIL_ADDRESS="your-email@example.com" + # FROM_EMAIL_ADDRESS="snapraid@your-server.com" + + # Advanced settings - typically no need to modify + CHK_FAIL=0 + DO_SYNC=1 + EMAIL_SUBJECT_PREFIX="(SnapRAID on $(hostname))" + SERVICES_STOPPED=0 + SYNC_WARN_FILE="/var/lib/snapraid-aio/snapRAID.warnCount" + SCRUB_COUNT_FILE="/var/lib/snapraid-aio/snapRAID.scrubCount" + TMP_OUTPUT="/var/lib/snapraid-aio/snapRAID.out" + SNAPRAID_LOG="/var/log/snapraid/snapraid.log" + ''; + }; + + snapraid-conf = pkgs.writeTextFile { + name = "snapraid.conf"; + text = '' + ## /etc/snapraid.conf ## + + # Defines the file to use as parity storage + parity /mnt/parity/snapraid.parity + + # Defines the files to use as content list + content /var/snapraid.content + content /mnt/drive1/snapraid.content + content /mnt/drive2/snapraid.content + content /mnt/drive3/snapraid.content + content /mnt/parity/snapraid.content + + # Defines the data disks to use + data d1 /mnt/drive1/ + data d2 /mnt/drive2/ + data d3 /mnt/drive3/ + + # Defines files and directories to exclude + exclude *.unrecoverable + exclude /tmp/ + exclude /lost+found/ + ''; + }; +in +{ + imports = [ + inputs.snapraid-aio.nixosModules.default + ]; + + # Make sure the SnapRAID config exists + environment.etc."snapraid.conf".source = snapraid-conf; + + # Create required directories + systemd.tmpfiles.rules = [ + "d /var/lib/snapraid-aio 0755 root root -" + "d /var/log/snapraid 0755 root root -" + ]; + + # Set up snapraid-aio service + services.snapraid-aio = { + enable = true; + configFile = snapraid-aio-config; + schedule = "*-*-* 03:00:00"; # Run daily at 3am + }; +} diff --git a/hosts/nixos/cloud/default.nix b/hosts/nixos/cloud/default.nix index a8bc76f..2028e92 100644 --- a/hosts/nixos/cloud/default.nix +++ b/hosts/nixos/cloud/default.nix @@ -1,12 +1,11 @@ ############################################################### # -# Prozy - LXC Container -# NixOS container, Ryzen 5 5600G (3 Cores), 2GB/2GB RAM/SWAP +# Cloud - LXC Container +# NixOS container, Ryzen 5 5600G (4th Cores), 4GB/4GB RAM/SWAP # ############################################################### { - inputs, lib, config, pkgs, @@ -19,6 +18,10 @@ let in { imports = lib.flatten [ + + ## Cloud Only ## + ./config + ## Hardware ## ./hardware.nix @@ -28,12 +31,10 @@ in ## Optional Configs ## "hosts/common/optional/acme" - "hosts/common/optional/caddy" "hosts/common/optional/docker.nix" - "hosts/common/optional/containers/cloudflared.nix" - ## Cloud Specific ## - "hosts/users/${username}" # # Not the best solution but I always have one user so ¯\_(ツ)_/¯ + ## Host user ## + "hosts/users/${username}" # Not the best solution but I always have one user so ¯\_(ツ)_/¯ ]) ]; @@ -50,21 +51,21 @@ in networking = { enableIPv6 = false; - firewall.allowedTCPPorts = firewall.allowedTCPPorts; + firewall = { + allowedTCPPorts = firewall.allowedTCPPorts; + allowedUDPPorts = firewall.allowedUDPPorts; + }; }; ## System-wide packages ## programs.nix-ld.enable = true; environment.systemPackages = with pkgs; [ + apprise lazydocker + mergerfs + snapraid ]; - environment.etc = { - "cloudflared/.keep" = { - text = "This directory is used to store cloudflared configuration files."; - }; - }; - # https://wiki.nixos.org/wiki/FAQ/When_do_I_update_stateVersion - system.stateVersion = "24.11"; + system.stateVersion = "25.05"; } diff --git a/hosts/nixos/cloud/hardware.nix b/hosts/nixos/cloud/hardware.nix index f0e401e..934e100 100644 --- a/hosts/nixos/cloud/hardware.nix +++ b/hosts/nixos/cloud/hardware.nix @@ -1,12 +1,45 @@ { lib, + config, ... }: +let + username = config.hostSpec.username; + homeDir = config.hostSpec.home; +in { imports = lib.flatten [ (map lib.custom.relativeToRoot [ "hosts/common/optional/system/lxc.nix" - "hosts/common/optional/system/pool.nix" ]) ]; + + # INFO: Cloud is the pool provider + fileSystems = { + "/pool" = { + fsType = "fuse.mergerfs"; + device = "/mnt/data*"; + options = [ + "cache.files=auto-full" + "defaults" + "allow_other" + "minfreespace=50G" + "fsname=mergerfs" + "category.create=mfs" + "nonempty" + "uid=1000" + "gid=1004" # Ryot group + "posix_acl=true" + ]; + }; + + "${homeDir}/git" = { + fsType = "none"; + device = "/pool/git"; + options = [ + "bind" + "nofail" + ]; + }; + }; } diff --git a/hosts/nixos/proxy/config/caddy.nix b/hosts/nixos/proxy/config/caddy.nix new file mode 100644 index 0000000..85fe253 --- /dev/null +++ b/hosts/nixos/proxy/config/caddy.nix @@ -0,0 +1,13 @@ +{ + services.caddy = { + enable = true; + virtualHosts = { + "cloudflared.ryot.foo" = { + useACMEHost = "ryot.foo"; + extraConfig = '' + reverse_proxy localhost:14333 + ''; + }; + }; + }; +} diff --git a/hosts/common/optional/containers/cloudflared.nix b/hosts/nixos/proxy/config/cloudflared.nix similarity index 100% rename from hosts/common/optional/containers/cloudflared.nix rename to hosts/nixos/proxy/config/cloudflared.nix diff --git a/hosts/nixos/proxy/config/default.nix b/hosts/nixos/proxy/config/default.nix new file mode 100644 index 0000000..b23287b --- /dev/null +++ b/hosts/nixos/proxy/config/default.nix @@ -0,0 +1,4 @@ +{ lib, ... }: +{ + imports = lib.custom.scanPaths ./.; +} diff --git a/hosts/nixos/proxy/default.nix b/hosts/nixos/proxy/default.nix index 42e831a..60722e9 100644 --- a/hosts/nixos/proxy/default.nix +++ b/hosts/nixos/proxy/default.nix @@ -1,6 +1,6 @@ ############################################################### # -# Prozy - LXC Container +# Proxy - LXC Container # NixOS container, Ryzen 5 5600G (3 Cores), 2GB/2GB RAM/SWAP # ############################################################### @@ -19,6 +19,9 @@ let in { imports = lib.flatten [ + ## Proxy Only ## + ./config + ## Hardware ## ./hardware.nix @@ -28,11 +31,9 @@ in ## Optional Configs ## "hosts/common/optional/acme" - "hosts/common/optional/caddy" "hosts/common/optional/docker.nix" - "hosts/common/optional/containers/cloudflared.nix" - ## Proxy Specific ## + ## Proxy User ## "hosts/users/${username}" # # Not the best solution but I always have one user so ¯\_(ツ)_/¯ ]) ]; diff --git a/secrets.nix b/secrets.nix index 9dbfd1b1bdacc146f620fab07060515e54d73a9e..e0b332960fd85c40dd7ea981886d2ffdee9bdc2f 100644 GIT binary patch literal 13184 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 literal 12315 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