{ lib, pkgs, modulesPath, ... }: let siteDomain = "blogbox.alisceon.com"; repoDir = "/home/alisceon/blogbox-site"; stateDir = "/var/lib/blogbox"; publicDir = "${stateDir}/www"; updateBlogboxSite = pkgs.writeShellApplication { name = "update-blogbox-site"; runtimeInputs = [ pkgs.coreutils pkgs.git pkgs.hugo pkgs.rsync ]; text = '' set -euo pipefail if [ ! -d ${lib.escapeShellArg repoDir}/.git ]; then echo "${repoDir} is not a git checkout yet; skipping Hugo publish" exit 0 fi install -d -m 0755 ${lib.escapeShellArg stateDir} ${lib.escapeShellArg publicDir} git -C ${lib.escapeShellArg repoDir} pull --ff-only git -C ${lib.escapeShellArg repoDir} submodule sync --recursive git -C ${lib.escapeShellArg repoDir} submodule update --init --recursive rm -rf ${lib.escapeShellArg stateDir}/hugo-public hugo \ --source ${lib.escapeShellArg repoDir} \ --destination ${lib.escapeShellArg stateDir}/hugo-public \ --minify \ --cleanDestinationDir rsync -a --delete ${lib.escapeShellArg stateDir}/hugo-public/ ${lib.escapeShellArg publicDir}/ ''; }; updateNamecheapDyndns = pkgs.writeShellApplication { name = "update-namecheap-dyndns"; runtimeInputs = [ pkgs.coreutils pkgs.ddclient ]; text = '' set -euo pipefail : "''${NAMECHEAP_DOMAIN:?Set NAMECHEAP_DOMAIN in /etc/blogbox-namecheap-ddns.env}" : "''${NAMECHEAP_PASSWORD:?Set NAMECHEAP_PASSWORD in /etc/blogbox-namecheap-ddns.env}" : "''${NAMECHEAP_HOSTS:?Set NAMECHEAP_HOSTS in /etc/blogbox-namecheap-ddns.env}" config_file="''${RUNTIME_DIRECTORY}/ddclient.conf" install -m 0600 /dev/null "$config_file" { printf 'daemon=0\n' printf 'cache=/var/cache/blogbox-dyndns/ddclient.cache\n' printf 'ssl=yes\n' printf 'protocol=namecheap\n' printf 'usev4=webv4, webv4=dynamicdns.park-your-domain.com/getip\n' printf 'server=dynamicdns.park-your-domain.com\n' printf 'login=%s\n' "$NAMECHEAP_DOMAIN" printf 'password=%s\n' "$NAMECHEAP_PASSWORD" printf '%s\n' "$NAMECHEAP_HOSTS" } > "$config_file" ddclient -file "$config_file" ''; }; ensureBlogboxSwapfile = pkgs.writeShellApplication { name = "ensure-blogbox-swapfile"; runtimeInputs = [ pkgs.coreutils pkgs.gnugrep pkgs.util-linux ]; text = '' set -euo pipefail if swapon --show=NAME --noheadings | grep -Fxq /swapfile; then exit 0 fi if [ ! -f /swapfile ] || [ "$(stat -c %s /swapfile)" -ne 8589934592 ]; then rm -f /swapfile install -m 0600 /dev/null /swapfile fallocate -l 8G /swapfile || dd if=/dev/zero of=/swapfile bs=1M count=8192 status=none chmod 0600 /swapfile fi mkswap -f /swapfile swapon /swapfile ''; }; in { imports = [ "${modulesPath}/virtualisation/oci-image.nix" ../../modules/services/cloud-init.nix ../../modules/services/nginx.nix ../../modules/services/oci-authorized-keys.nix ]; nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux"; oci.efi = lib.mkForce false; virtualisation.diskSize = lib.mkForce (16 * 1024); networking = { hostName = "blogbox"; networkmanager.enable = lib.mkForce false; firewall.allowedTCPPorts = [ 22 80 443 ]; }; boot = { initrd.availableKernelModules = [ "virtio_pci" "virtio_blk" "virtio_scsi" "virtio_net" "xhci_pci" ]; kernelParams = lib.mkForce [ "nvme.shutdown_timeout=10" "nvme_core.shutdown_timeout=10" "libiscsi.debug_libiscsi_eh=1" "crash_kexec_post_notifiers" "console=tty1" "console=ttyS0,115200n8" ]; kernelPackages = lib.mkForce pkgs.linuxPackages; loader.systemd-boot.configurationLimit = lib.mkForce 3; }; documentation = { enable = lib.mkForce false; man.enable = lib.mkForce false; doc.enable = lib.mkForce false; info.enable = lib.mkForce false; nixos.enable = lib.mkForce false; }; environment = { defaultPackages = lib.mkForce [ ]; shells = lib.mkForce [ pkgs.bash ]; systemPackages = lib.mkForce (with pkgs; [ curl git hugo vim ]); }; programs = { command-not-found.enable = lib.mkForce false; fish.enable = lib.mkForce false; fzf.fuzzyCompletion = lib.mkForce false; xonsh.enable = lib.mkForce false; }; users = { defaultUserShell = lib.mkForce pkgs.bash; groups.blogbox-dyndns = { }; users.alisceon = { createHome = true; extraGroups = lib.mkForce [ "wheel" "systemd-journal" ]; openssh.authorizedKeys.keys = [ "ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBPN1Cd2UlHo03Jqgi5Yb4io/3gh/X4wCb8LcmKlpAovQa271CKDBtYOUKn+Fts03g6dBMfaWMty6VGPMGDMONmc= alisceon@electra" ]; shell = lib.mkForce pkgs.bash; }; users.blogbox-dyndns = { group = "blogbox-dyndns"; isSystemUser = true; }; }; alisceon = { cloud-init.enable = true; ociAuthorizedKeys.enable = true; }; nix = { settings = { cores = lib.mkForce 1; max-jobs = lib.mkForce 1; min-free = lib.mkForce (256 * 1024 * 1024); max-free = lib.mkForce (1024 * 1024 * 1024); }; gc = { dates = lib.mkForce "daily"; options = lib.mkForce "--delete-older-than 3d"; }; }; system.autoUpgrade = { persistent = lib.mkForce false; randomizedDelaySec = lib.mkForce "4h"; }; security = { acme = { acceptTerms = true; defaults.email = "acme@alisceon.com"; }; sudo-rs.wheelNeedsPassword = false; }; services = { openssh.settings = { KbdInteractiveAuthentication = false; PasswordAuthentication = false; PermitRootLogin = lib.mkForce "prohibit-password"; }; journald.extraConfig = '' SystemMaxUse=64M RuntimeMaxUse=32M ''; nginx.virtualHosts.${siteDomain} = { serverName = siteDomain; forceSSL = true; enableACME = true; root = publicDir; locations."/".extraConfig = '' try_files $uri $uri/ =404; ''; }; }; systemd = { tmpfiles.rules = [ "d ${repoDir} 0755 alisceon users - -" "d ${stateDir} 0755 alisceon users - -" "d ${publicDir} 0755 alisceon users - -" ]; services.update-blogbox-site = { description = "Pull and publish the Blogbox Hugo site"; after = [ "network-online.target" ]; wants = [ "network-online.target" ]; serviceConfig = { Type = "oneshot"; User = "alisceon"; Group = "users"; ExecStart = lib.getExe updateBlogboxSite; Nice = 10; IOSchedulingClass = "idle"; LockPersonality = true; MemoryHigh = "384M"; MemoryMax = "512M"; NoNewPrivileges = true; OOMPolicy = "stop"; PrivateDevices = true; PrivateIPC = true; ProtectClock = true; ProtectControlGroups = true; ProtectHostname = true; ProtectKernelLogs = true; ProtectKernelModules = true; ProtectKernelTunables = true; ProtectSystem = "strict"; ReadWritePaths = [ repoDir stateDir ]; RestrictAddressFamilies = [ "AF_INET" "AF_INET6" "AF_UNIX" ]; RestrictNamespaces = true; RestrictRealtime = true; SystemCallArchitectures = "native"; TimeoutStartSec = "15min"; UMask = "0022"; }; }; services.blogbox-dyndns = { description = "Update Namecheap dynamic DNS records for Blogbox"; after = [ "network-online.target" ]; wants = [ "network-online.target" ]; unitConfig.ConditionPathExists = "/etc/blogbox-namecheap-ddns.env"; serviceConfig = { Type = "oneshot"; User = "blogbox-dyndns"; Group = "blogbox-dyndns"; ExecStart = lib.getExe updateNamecheapDyndns; CacheDirectory = "blogbox-dyndns"; EnvironmentFile = "/etc/blogbox-namecheap-ddns.env"; LockPersonality = true; MemoryMax = "128M"; MemoryDenyWriteExecute = true; NoNewPrivileges = true; OOMPolicy = "stop"; PrivateDevices = true; PrivateIPC = true; PrivateTmp = true; ProtectClock = true; ProtectControlGroups = true; ProtectHome = true; ProtectHostname = true; ProtectKernelLogs = true; ProtectKernelModules = true; ProtectKernelTunables = true; ProtectProc = "invisible"; ProtectSystem = "strict"; RestrictAddressFamilies = [ "AF_INET" "AF_INET6" "AF_NETLINK" ]; RestrictNamespaces = true; RestrictRealtime = true; RestrictSUIDSGID = true; RuntimeDirectory = "blogbox-dyndns"; RuntimeDirectoryMode = "0700"; SystemCallArchitectures = "native"; SystemCallErrorNumber = "EPERM"; SystemCallFilter = "@system-service"; TimeoutStartSec = "2min"; UMask = "0177"; }; }; timers.update-blogbox-site = { wantedBy = [ "timers.target" ]; timerConfig = { OnBootSec = "10min"; OnUnitInactiveSec = "5min"; Persistent = true; }; }; timers.blogbox-dyndns = { wantedBy = [ "timers.target" ]; timerConfig = { OnBootSec = "5min"; OnUnitInactiveSec = "10min"; Persistent = true; }; }; }; systemd.services.nixos-upgrade.serviceConfig = { IOSchedulingClass = "idle"; MemoryHigh = "512M"; MemoryMax = "768M"; Nice = 15; OOMPolicy = "stop"; }; systemd.services.growpart.serviceConfig = { IOSchedulingClass = "idle"; Nice = 15; TimeoutStartSec = "2min"; }; systemd.services.blogbox-swapfile = { description = "Create and enable Blogbox swapfile"; after = [ "sshd.service" "systemd-growfs-root.service" ]; wantedBy = [ "multi-user.target" ]; serviceConfig = { Type = "oneshot"; ExecStart = lib.getExe ensureBlogboxSwapfile; IOSchedulingClass = "idle"; Nice = 19; RemainAfterExit = true; TimeoutStartSec = "20min"; }; }; zramSwap = { enable = true; memoryPercent = 50; }; virtualisation = { containers.enable = lib.mkForce false; docker.enable = lib.mkForce false; libvirtd = { enable = lib.mkForce false; qemu.swtpm.enable = lib.mkForce false; }; podman.enable = lib.mkForce false; }; system.stateVersion = lib.mkForce "25.11"; }