Manual fan mode pins the fans at a fixed duty and never ramps, so a sustained
load had no cooling response until the BMC's own critical-temperature
override stepped in. docker-idrac.sh solved that, but needs Docker, which is
not installed on the host.
ipmi-fancontrol-daemon polls the CPU sensors and switches between the fixed
duty and the BMC's automatic curve around a threshold, which is what that
container did. Measured on the host: 1440 RPM pinned versus roughly 4200 RPM
on the automatic curve at the same 60C.
- Hysteresis, default 5C, stops the fans oscillating at the boundary.
- An unreadable temperature fails safe to automatic rather than staying
pinned.
- SIGTERM restores the automatic curve, with ExecStopPost as the backstop for
an outright kill.
Default to the local "open" interface. It needs no credentials and no
network, which matters for the shutdown path, where lanplus to the iDRAC
could fail once networking is down. ipmi-fancontrol-auto and -manual honour
the same setting and stay available for ad-hoc use.
Enable the third-party PCIe cooling response suppression that docker-idrac.sh
was configured with, so the automatic curve is not held to a raised floor by
the aftermarket NICs.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Docker is not installed on pet630, so neither of these can run there:
- docker-idrac.sh is the container-based alternative to the ipmi-fancontrol
scripts, which remain in service.
- webnut-docker/ is the web UI for the NUT daemon. NUT itself is still
running on the host; only this front end is retired.
The gitignored .env moves with the compose file and is still excluded at the
new path.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The version line printed unconditionally, so the nightly job would mail a
one-line message on every run even when there was nothing to download. Guard
it like the other status output, leaving interactive runs unchanged.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The deployed /etc/cron.d/download-virtio-win never ran. It omitted the user
field that /etc/cron.d entries require between the schedule and the command,
so cron parsed the script path as a username, and its day-of-month field was
0, outside the valid range of 1-31.
Corroborated on the host: the newest ISO is virtio-win-0.1.285 from
2025-09-20, the same day the cron file was created.
Pass --cron so a no-op run emits no progress bar and generates no cron mail.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Checked against the live host. None of these are in service:
- sriov-enp4s0.service and set-ixgbe-vfmacs-enp4s0.sh target enp4s0f1np1,
which no longer exists; sriov_numvfs is 0 on every port and the unit was
never installed.
- nic-performance-tweaks.service was never installed either, though its
interfaces do still exist, so it would work if reinstated.
- vm-watchdog.sh watches VMID 2801 "pfsense", which no longer exists.
- lxc-watchdog.sh still carries the upstream example targets: CT 100 and
CT 101 on 192.168.31.0/24, a subnet that is not in use.
Neither watchdog is scheduled; there is no root crontab.
The watchdog config moves to unused/conf/ alongside them, so the scripts
resolve it unchanged from their new location.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Both watchdogs hardcoded the notification address. Read it from
conf/watchdog.conf instead, matching the pattern used for the other
credentials, so the repo carries no real contact details.
If the config is absent the watchdogs still run and log; sendAlert notes the
missing address and skips the mail rather than invoking mail(1) with an empty
recipient.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
build-cloudinit-template.sh supersedes make-debian-cloudinit-template.sh,
covering Alpine, Ubuntu, Debian, Fedora, AlmaLinux and Arch, with UEFI,
vendor snippets and an optional --docker runtime.
Carry forward the hardening the Debian script had gained, which the new one
was missing: set -euo pipefail, root/binary/numeric-VMID checks, a VMID
collision check, storage validation, an ERR trap naming the cleanup command,
image caching under /var/tmp, and env-overridable STORAGE, BRIDGE, MEMORY,
CORES, DISK_SIZE, SSH_KEY_FILE and CI_PASSWORD.
Quote the openssl passwd substitution feeding --cipassword. A SHA-512 crypt
hash contains $ and /, so unquoted it was subject to word splitting.
Derive the snippet directory from SNIPPET_STORAGE via pvesm path instead of
hardcoding /var/lib/vz/snippets, which silently disagreed with the storage
whenever SNIPPET_STORAGE was changed. Falls back to parsing storage.cfg.
Resize the imported disk with qm disk resize rather than resizing the image
file. Growing the file in place would mutate the shared cache and defeat the
size comparison wget -N uses to decide whether to re-download.
Check the Snippets content type on the target storage up front. It is off by
default, and without this the run failed much later at qm set --cicustom.
Fall back to timedatectl when /etc/timezone is absent, as it is outside
Debian.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Web UI for the NUT UPS daemon.
UPS credentials come from .env alongside the compose file, which Compose
loads automatically; see .env.example. Required values use ${VAR:?} so a
missing .env fails loudly rather than starting the container with a blank
password.
Drop the obsolete top-level version key.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
stop-pa-vm.service calls the PAN-OS XML API on host shutdown so the firewall
VM goes down cleanly rather than being killed.
The API key is read from conf/pa-vm.conf, resolved relative to the script.
See conf/pa-vm.conf.example.
Pass the request through curl --config on stdin with the key in an X-PAN-KEY
header, instead of embedding it in the URL. A key in the query string is
visible in ps and would be recorded in any proxy or webserver log along the
path.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
ipmi-fancontrol.service pins the fans to a fixed speed on start and hands
control back to the BMC's automatic curve on stop, to quiet the chassis.
BMC credentials are read from conf/ipmi-fancontrol.conf, resolved relative
to the scripts themselves so the tree stays self-contained whether it is run
from a checkout or from /opt/ergosteur. See conf/ipmi-fancontrol.conf.example.
Use ipmitool -E rather than -P so the password is passed via the environment
instead of argv, where any local user could read it out of ps.
docker-idrac.sh is the alternative temperature-aware controller.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
sriov-enp4s0.service allocates VFs on the ixgbe ports at boot and applies
persistent VF MACs via set-ixgbe-vfmacs-enp4s0.sh, so guests keep stable
addresses across host reboots. The VF MAC assignments are currently all
commented out.
nic-performance-tweaks.service raises ring buffers and txqueuelen on the
uplinks and vmbr0.
unused/ holds the equivalent pair for the retired enp130s0 adapter.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Two implementations of the same OVA/OVF import flow: a Python version that
parses the OVF with ElementTree and detects UEFI, and a shell version using
xmllint that handles multi-disk appliances. Both check free space on the
target storage before importing.
download-virtio-win-progress.py fetches the latest virtio-win ISO, skipping
the download when the local copy is already current.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
prox-maintenance-mode.sh saves and clears onboot/startup across all VMs and
CTs so a host can be rebooted without guests coming back up, then restores
the saved state. Supports --status and --dry-run.
nut-udev-restart.sh restarts the NUT UPS driver on a USB re-enumeration
event. tailscale-optimize-router.sh applies the recommended UDP GRO
forwarding offloads for subnet routers.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Cron-driven liveness checks that ping each guest, restart it via qm/pct if
unreachable, and log plus email the outcome. Guest lists are configured in
the associative array at the top of each script.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Ignore *.conf and .env (keeping the committed *.conf.example and
.env.example templates) so runtime credentials stay out of the repo.
Also ignore bridge-vlan-show.txt, tmp/, and downloaded disk images, which
are host-specific dumps and build artifacts rather than source.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Second positional arg is now a codename (bullseye/bookworm/trixie/
forky) mapped to the corresponding cloud.debian.org image, with an
IMAGE_URL env var escape hatch for pointing at something else.
Adds error handling (set -euo pipefail), VM ID/storage validation,
dependency checks, configurable storage/bridge/memory/cores via env
vars, image download caching, and auto-detection of the imported
disk instead of hardcoding filesystem-specific paths.