diff --git a/TODO.org b/TODO.org index 1105ce2..5cd5a79 100644 --- a/TODO.org +++ b/TODO.org @@ -209,7 +209,7 @@ - Surname attribute: =sn= - Email attribute: =mail= -** TODO Verify Nextcloud LDAP app configuration +** DONE Verify Nextcloud LDAP app configuration After restoring the Nextcloud volume, check: Admin → LDAP/AD Integration — confirm the LDAP Users and Contacts app is configured. If reconfiguring from scratch, use the same settings as Gitea above but with @@ -232,7 +232,7 @@ * Backup Strategy -** TODO Configure S3-compatible automatic backup target +** DONE Configure S3-compatible automatic backup target Update =homey.backup.repository= in =hosts/pi-main/default.nix= to point at your S3-compatible bucket (Backblaze B2, Wasabi, AWS S3, etc.): #+begin_src nix diff --git a/hosts/pi-main/default.nix b/hosts/pi-main/default.nix index 3ce3ca6..17374b8 100644 --- a/hosts/pi-main/default.nix +++ b/hosts/pi-main/default.nix @@ -100,6 +100,33 @@ # "rclone:remote:homey" homey.backup.repository = "s3:https://s3.us-east-005.backblazeb2.com/zakobar-home-backup"; + # ------------------------------------------------------------------------- + # Reliability hardening + # ------------------------------------------------------------------------- + + # Hardware watchdog — auto-reboot if the system hangs (e.g. blocked USB I/O). + # bcm2835_wdt exposes /dev/watchdog; systemd pets it every runtimeTime/2. + # If systemd itself stops responding, the hardware resets the Pi after 20s. + boot.kernelModules = [ "bcm2835_wdt" ]; + systemd.watchdog = { + runtimeTime = "300s"; # 5 min — generous window for boot I/O storm on USB drive + rebootTime = "360s"; + }; + + # Compressed in-RAM swap via zstd. Pages evicted from RAM are compressed + # (~3:1 ratio) and stored in a 25% RAM region (~2 GB) rather than written + # to disk. Gives the OOM killer breathing room under PHP upload spikes. + # CPU overhead is negligible during normal operation. + zramSwap = { + enable = true; + algorithm = "zstd"; + memoryPercent = 25; + }; + + # hdparm -B udev rule removed: USB-SATA bridges often don't support APM + # commands and hdparm can hang indefinitely, causing boot-time crashes. + environment.systemPackages = [ pkgs.hdparm ]; + # ------------------------------------------------------------------------- # Local DNS overrides (optional — makes LAN clients hit the Pi directly # instead of going through Cloudflare for *.zakobar.com) diff --git a/modules/services/nextcloud.nix b/modules/services/nextcloud.nix index a21b9f1..7ea1ac9 100644 --- a/modules/services/nextcloud.nix +++ b/modules/services/nextcloud.nix @@ -18,6 +18,37 @@ let cfg = config.homey.nextcloud; dataDir = config.homey.storage.mountPoint; domain = homeyConfig.domain; + + # Custom Nextcloud config mounted into the container as an extra config file. + # Nextcloud auto-loads all *.config.php files in /var/www/html/config/. + nextcloudCustomConfig = pkgs.writeText "zakobar.config.php" '' + 1, + 'preview_concurrency_all' => 1, + // Cap preview dimensions to reduce per-preview write size. + 'preview_max_x' => 1024, + 'preview_max_y' => 1024, + 'jpeg_quality' => 75, + ]; + ''; + + # Limit Apache's prefork MPM so at most 4 PHP processes write to the USB + # drive simultaneously. Default is often 150, which causes an I/O storm + # on slow USB HDDs. Lower = fewer concurrent writers = more stable I/O. + apacheMpmConfig = pkgs.writeText "mpm_prefork.conf" '' + + StartServers 2 + MinSpareServers 1 + MaxSpareServers 3 + MaxRequestWorkers 4 + MaxConnectionsPerChild 500 + + ''; in { options.homey.nextcloud = { @@ -123,6 +154,10 @@ in volumes = [ "${dataDir}/nextcloud/html:/var/www/html" + # Extra config auto-loaded by Nextcloud (throttles preview generation) + "${nextcloudCustomConfig}:/var/www/html/config/zakobar.config.php:ro" + # Apache MPM limits (caps concurrent PHP processes / disk writers) + "${apacheMpmConfig}:/etc/apache2/mods-available/mpm_prefork.conf:ro" ]; extraOptions = [