Proxmox VE Post-Install Hardening
Essential post-install steps for a new Proxmox VE 9 node: switch to the free repo (deb822 format), disable the enterprise repo, remove the subscription nag, update the system, and set the basics — with a PVE 8 fallback.
bash# Proxmox VE 9 (Debian 13 "Trixie"). PVE 8 fallback is handled automatically.
# 1. Add the community (free) repository — deb822 format used by PVE 9
if grep -q trixie /etc/os-release; then
cat > /etc/apt/sources.list.d/proxmox.sources <<'EOF'
Types: deb
URIs: http://download.proxmox.com/debian/pve
Suites: trixie
Components: pve-no-subscription
Signed-By: /usr/share/keyrings/proxmox-archive-keyring.gpg
EOF
# Disable the enterprise (paid) repos shipped by the installer
for f in /etc/apt/sources.list.d/pve-enterprise.sources /etc/apt/sources.list.d/ceph.sources; do
[ -f "$f" ] && sed -i 's/^Types:/Enabled: false\nTypes:/' "$f"
done
else
# PVE 8 (Debian 12 "Bookworm") — legacy one-line format
echo "deb http://download.proxmox.com/debian/pve bookworm pve-no-subscription" \
> /etc/apt/sources.list.d/pve-community.list
sed -i 's/^deb/#deb/' /etc/apt/sources.list.d/pve-enterprise.list 2>/dev/null || true
sed -i 's/^deb/#deb/' /etc/apt/sources.list.d/ceph.list 2>/dev/null || true
fi
# 2. Remove subscription nag from the web UI (UI-only; reverts on
# proxmox-widget-toolkit updates — reapply after upgrades)
PLIB=/usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js
cp "$PLIB" "$PLIB.bak"
sed -i "s/data.status.toLowerCase() !== 'active'/data.status.toLowerCase() === 'active'/" "$PLIB"
systemctl restart pveproxy
# 3. Update all packages
apt-get update && apt-get dist-upgrade -y
# 4. Install useful tools
apt-get install -y \
sudo curl wget git htop iotop ncdu \
net-tools nmap dnsutils \
qemu-guest-agent
# 5. Disable IPv6 if not in use (optional — comment out if you use it)
echo "net.ipv6.conf.all.disable_ipv6 = 1" >> /etc/sysctl.d/99-proxmox.conf
sysctl -p /etc/sysctl.d/99-proxmox.conf
# 6. Set timezone — replace with yours (list: timedatectl list-timezones)
timedatectl set-timezone <YOUR-TIMEZONE> # e.g. America/Chicago
echo ""
echo "Done. Reboot recommended: reboot"
What this does
Prepares a freshly installed Proxmox VE node for homelab use:
- Switches to the community (free) repo — the enterprise repo requires a paid subscription and will block updates otherwise. On PVE 9 this uses the deb822
.sourcesformat; on PVE 8 the script falls back to the legacy one-line format automatically - Removes the subscription nag popup that appears on every login (UI-only change, no functionality impact)
- Runs a full system update via
dist-upgrade - Installs common tools useful for day-to-day administration
- Optionally disables IPv6 to reduce attack surface if you’re not using it
- Sets the timezone
Why this approach
A fresh Proxmox VE install has a few defaults that need to change before you do anything productive with the node. The most important is the package repository: the default configuration points at the enterprise repository, which requires a paid subscription. Without a subscription, apt update produces an authorization error and the system can’t receive updates. Switching to the community (free/no-subscription) repository is the first thing to do after install.
Proxmox VE 9 (on Debian 13 “Trixie”) moved apt configuration to the deb822 .sources format — multi-line stanzas in /etc/apt/sources.list.d/*.sources instead of the old one-line .list entries. That’s why the script branches: on a Trixie base it writes a proxmox.sources stanza and disables the installer’s pve-enterprise.sources and ceph.sources with an Enabled: false field; on a Bookworm (PVE 8) base it uses the legacy .list files.
The subscription nag is separate from the repository issue — it’s a JavaScript check in the Proxmox web UI that displays a popup on login, not a functional limitation. The sed in this playbook flips the subscription-status comparison in the UI JavaScript so the dialog no longer fires. This is a UI change only; it doesn’t affect any Proxmox functionality. It will revert when the proxmox-widget-toolkit package updates, so you’ll occasionally need to reapply it after an upgrade. Hard-refresh the browser (or use a private window) after applying, or the cached JS will still show the popup once.
Running apt dist-upgrade rather than apt upgrade is intentional. dist-upgrade handles kernel and package dependency changes that apt upgrade (which never removes packages to satisfy dependencies) can’t. Proxmox kernel updates often require this, and running upgrade instead can leave the system on an older kernel even when a newer one is available.
Prerequisites
- Fresh Proxmox VE 9.x install (Debian 13 “Trixie” base) — the script also handles PVE 8.x (Debian 12 “Bookworm”)
- Root access (SSH or console)
- Internet connectivity from the node
Set your timezone
Replace <YOUR-TIMEZONE> with your zone. Find yours:
timedatectl list-timezones | grep Your_Region
Harden SSH on the Proxmox node
Proxmox uses the standard Debian sshd. Apply the SSH hardening playbook after this one.
Notes
- Run as
rootdirectly — Proxmox nodes don’t use sudo by default - The nag patch modifies
/usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js; a.bakbackup is created first. It reverts after aproxmox-widget-toolkitpackage update — rerun the nag section if the popup returns qemu-guest-agentenables memory/disk reporting from within VMs that have it installed; harmless if you only run LXC containers- The Ceph enterprise repo is also disabled — if you use Ceph, enable the community Ceph repo separately (on PVE 9 it’s a
ceph.sourcesstanza with theno-subscriptioncomponent)