{"id":3272,"date":"2026-05-26T22:00:47","date_gmt":"2026-05-26T22:00:47","guid":{"rendered":"https:\/\/muhammadsoliman.com\/?p=3272"},"modified":"2026-05-26T22:11:34","modified_gmt":"2026-05-26T22:11:34","slug":"how-to-install-proxmox-ve-on-a-hetzner-dedicated-server-with-zfs-raid1-ssh-hardening-firewall-and-ssl","status":"publish","type":"post","link":"https:\/\/muhammadsoliman.com\/index.php\/2026\/05\/26\/how-to-install-proxmox-ve-on-a-hetzner-dedicated-server-with-zfs-raid1-ssh-hardening-firewall-and-ssl\/","title":{"rendered":"How to Install Proxmox VE on a Hetzner Dedicated Server with ZFS RAID1, SSH Hardening, Firewall, and SSL"},"content":{"rendered":"\n<h1 class=\"wp-block-heading\">Install Proxmox on Dedicated server <\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">This tutorial shows how to install Proxmox VE on a dedicated Hetzner server using the official Proxmox ISO, Hetzner Rescue Mode, QEMU\/VNC, ZFS RAID1 mirror, hardened SSH access, Hetzner firewall rules, and a trusted Let\u2019s Encrypt certificate through Cloudflare DNS. <\/p>\n\n\n\n<p class=\"wp-block-paragraph\">This guide is written for a server with <strong>2 NVMe drives<\/strong> where you want safety first. The result is a clean Proxmox host with mirrored storage, SSH key-only login, a custom SSH port, DNS hostname access, firewall protection, and trusted HTTPS for the Proxmox web panel.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Proxmox VE is an open-source virtualization platform for managing KVM virtual machines, LXC containers, storage, networking, and clustering from a web interface. Hetzner\u2019s own Proxmox guide confirms that on dedicated servers you can either install Debian first or use the official Proxmox ISO through QEMU from Rescue Mode. (<a href=\"https:\/\/community.hetzner.com\/tutorials\/install-and-configure-proxmox_ve\/\" target=\"_blank\" rel=\"noopener\">Hetzner Community<\/a>)<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Example Server<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>CPU: AMD Ryzen 9 3900\nRAM: 128 GB ECC\nDisks: 2 \u00d7 1.92 TB NVMe\nNetwork: 1 Gbit\nStorage plan: ZFS RAID1 mirror\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Important Placeholders<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Never paste real passwords, private keys, API tokens, or server IPs into public places. Replace these placeholders locally:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&#91;SENSITIVE: SERVER_MAIN_IPV4]\n&#91;SENSITIVE: SERVER_GATEWAY]\n&#91;SENSITIVE: PROXMOX_ROOT_PASSWORD]\n&#91;SENSITIVE: ADMIN_EMAIL]\n&#91;SENSITIVE: CLOUDFLARE_API_TOKEN]\n&#91;SENSITIVE: CLOUDFLARE_ZONE_ID]\nexample.com\npve1.example.com\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">1. Boot Hetzner Rescue Mode<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">In Hetzner Robot:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Server \u2192 Rescue \u2192 Linux \u2192 Keyboard: us \u2192 Activate\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Then reboot the server using:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Reset \u2192 Execute an automatic hardware reset\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">SSH into Rescue Mode:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>ssh root@&#91;SENSITIVE: SERVER_MAIN_IPV4]\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Check disks:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>lsblk -o NAME,SIZE,TYPE,MODEL\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Example:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>nvme0n1  1.7T disk\nnvme1n1  1.7T disk\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Check network information:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>ip -br addr\nip route\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Write down privately:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&#91;SENSITIVE: SERVER_MAIN_IPV4]\/CIDR\n&#91;SENSITIVE: SERVER_GATEWAY]\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Example CIDR may be <code>\/26<\/code>, <code>\/27<\/code>, <code>\/28<\/code>, etc. Do not assume <code>\/24<\/code>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Check boot mode:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&#91; -d \/sys\/firmware\/efi ] &amp;&amp; echo \"UEFI\" || echo \"Legacy\"\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">This tutorial uses the <strong>Legacy<\/strong> QEMU command. If your server returns UEFI, use the UEFI\/OVMF QEMU path from Hetzner\u2019s documentation. Hetzner\u2019s unattended Proxmox guide also shows gathering boot mode, network, disk information, and running the installer ISO through QEMU. (<a href=\"https:\/\/community.hetzner.com\/tutorials\/install-proxmox-unattended-hetzner\/\" target=\"_blank\" rel=\"noopener\">Hetzner Community<\/a>)<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n<style>.kb-row-layout-id3272_946561-79 > .kt-row-column-wrap{align-content:start;}:where(.kb-row-layout-id3272_946561-79 > .kt-row-column-wrap) > .wp-block-kadence-column{justify-content:start;}.kb-row-layout-id3272_946561-79 > .kt-row-column-wrap{column-gap:var(--global-kb-gap-md, 2rem);row-gap:var(--global-kb-gap-md, 2rem);padding-top:var(--global-kb-spacing-sm, 1.5rem);padding-bottom:var(--global-kb-spacing-sm, 1.5rem);grid-template-columns:minmax(0, 1fr);}.kb-row-layout-id3272_946561-79 > .kt-row-layout-overlay{opacity:0.30;}@media all and (max-width: 1024px){.kb-row-layout-id3272_946561-79 > .kt-row-column-wrap{grid-template-columns:minmax(0, 1fr);}}@media all and (max-width: 767px){.kb-row-layout-id3272_946561-79 > .kt-row-column-wrap{grid-template-columns:minmax(0, 1fr);}}<\/style><div class=\"kb-row-layout-wrap kb-row-layout-id3272_946561-79 alignnone wp-block-kadence-rowlayout\"><div class=\"kt-row-column-wrap kt-has-1-columns kt-row-layout-equal kt-tab-layout-inherit kt-mobile-layout-row kt-row-valign-top\">\n<style>.kadence-column3272_e5172e-d1 > .kt-inside-inner-col,.kadence-column3272_e5172e-d1 > .kt-inside-inner-col:before{border-top-left-radius:0px;border-top-right-radius:0px;border-bottom-right-radius:0px;border-bottom-left-radius:0px;}.kadence-column3272_e5172e-d1 > .kt-inside-inner-col{column-gap:var(--global-kb-gap-sm, 1rem);}.kadence-column3272_e5172e-d1 > .kt-inside-inner-col{flex-direction:column;}.kadence-column3272_e5172e-d1 > .kt-inside-inner-col > .aligncenter{width:100%;}.kadence-column3272_e5172e-d1 > .kt-inside-inner-col:before{opacity:0.3;}.kadence-column3272_e5172e-d1{position:relative;}@media all and (max-width: 1024px){.kadence-column3272_e5172e-d1 > .kt-inside-inner-col{flex-direction:column;justify-content:center;}}@media all and (max-width: 767px){.kadence-column3272_e5172e-d1 > .kt-inside-inner-col{flex-direction:column;justify-content:center;}}<\/style>\n<div class=\"wp-block-kadence-column kadence-column3272_e5172e-d1\"><div class=\"kt-inside-inner-col\"><style>.kb-image3272_1aed32-b7 .kb-image-has-overlay:after{opacity:0.3;}<\/style>\n<figure class=\"wp-block-kadence-image kb-image3272_1aed32-b7 size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"1024\" src=\"http:\/\/muhammadsoliman.com\/wp-content\/uploads\/2026\/05\/ChatGPT-Image-May-26-2026-04_54_23-PM-2-1024x1024.png\" alt=\"\" class=\"kb-img wp-image-3280\" srcset=\"https:\/\/muhammadsoliman.com\/wp-content\/uploads\/2026\/05\/ChatGPT-Image-May-26-2026-04_54_23-PM-2-1024x1024.png 1024w, https:\/\/muhammadsoliman.com\/wp-content\/uploads\/2026\/05\/ChatGPT-Image-May-26-2026-04_54_23-PM-2-300x300.png 300w, https:\/\/muhammadsoliman.com\/wp-content\/uploads\/2026\/05\/ChatGPT-Image-May-26-2026-04_54_23-PM-2-150x150.png 150w, https:\/\/muhammadsoliman.com\/wp-content\/uploads\/2026\/05\/ChatGPT-Image-May-26-2026-04_54_23-PM-2-768x768.png 768w, https:\/\/muhammadsoliman.com\/wp-content\/uploads\/2026\/05\/ChatGPT-Image-May-26-2026-04_54_23-PM-2.png 1254w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n<\/div><\/div>\n\n<\/div><\/div>\n\n\n<h2 class=\"wp-block-heading\">2. Download and Verify the Official Proxmox ISO<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Always check the current Proxmox download page before installing. At the time this tutorial was written, Proxmox showed VE 9.2 as the current version. (<a href=\"https:\/\/www.proxmox.com\/en\/\" target=\"_blank\" rel=\"noopener\">Proxmox<\/a>)<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Example for Proxmox VE 9.2-1:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>cd \/root\n\nwget -O proxmox-ve_9.2-1.iso https:\/\/enterprise.proxmox.com\/iso\/proxmox-ve_9.2-1.iso\n\necho \"4e88fe416df9b527624a175f24c9aa07c714d3332afb1ee3dbf3879573ef2c6c  proxmox-ve_9.2-1.iso\" | sha256sum -c\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Expected:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>proxmox-ve_9.2-1.iso: OK\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">3. Open a Secure VNC Tunnel<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">On your local computer, open an SSH tunnel:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>ssh -L 5900:127.0.0.1:5900 root@&#91;SENSITIVE: SERVER_MAIN_IPV4]\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Keep this terminal open.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The VNC port is bound to <code>127.0.0.1<\/code> on the server, so it is not publicly exposed. You will view it locally through:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>127.0.0.1:5900\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Use RealVNC Viewer or TigerVNC Viewer. macOS Screen Sharing may hang with QEMU VNC.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">4. Start the Proxmox Installer with QEMU<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">In the Rescue Mode SSH session, run:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>qemu-system-x86_64 \\\n  -enable-kvm \\\n  -m 4096 \\\n  -smp 4 \\\n  -boot d \\\n  -cdrom \/root\/proxmox-ve_9.2-1.iso \\\n  -drive file=\/dev\/nvme0n1,format=raw,media=disk,if=virtio \\\n  -drive file=\/dev\/nvme1n1,format=raw,media=disk,if=virtio \\\n  -vnc 127.0.0.1:0 \\\n  -k en-us\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The terminal will look \u201cstuck.\u201d That is normal. QEMU is running the installer.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Open RealVNC Viewer and connect to:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>127.0.0.1:5900\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">If asked for a password, leave it blank.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">5. Proxmox Installer Choices<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Choose:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Install Proxmox VE Graphical\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">If you see a warning about missing KVM acceleration inside QEMU, click <strong>OK<\/strong>. The installed Proxmox system will later boot directly on the real hardware.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Use:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Country: United States\nTime zone: America\/Chicago or UTC\nKeyboard: English (US)\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Set your root password and admin email:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Password: &#91;SENSITIVE: PROXMOX_ROOT_PASSWORD]\nEmail: &#91;SENSITIVE: ADMIN_EMAIL]\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Target Harddisk<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">This is the most important part.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Click <strong>Options<\/strong> and choose:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Filesystem: ZFS RAID1\nDisk 1: \/dev\/vda\nDisk 2: \/dev\/vdb\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Inside QEMU, the real NVMe disks appear as:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/dev\/vda = \/dev\/nvme0n1\n\/dev\/vdb = \/dev\/nvme1n1\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Use:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>ashift: 12\ncompress: on\nchecksum: on\ncopies: 1\nhdsize: full disk size\nARC max size: use the max the installer allows, or adjust later\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The ARC value may look small because the installer only sees the temporary QEMU memory. You can tune ZFS ARC later after Proxmox boots on the real 128 GB RAM server.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Network Screen<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Use:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Hostname: pve1.example.com\nIP Address: &#91;SENSITIVE: SERVER_MAIN_IPV4]\/CIDR\nGateway: &#91;SENSITIVE: SERVER_GATEWAY]\nDNS Server: 1.1.1.1\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Do <strong>not<\/strong> use a temporary QEMU DNS such as <code>10.0.2.3<\/code>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Leave this unchecked:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Pin network interface names: unchecked\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Because the installer is running inside QEMU, the network interface shown there is temporary.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Also leave this unchecked:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Automatically reboot after successful installation: unchecked\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">When installation finishes, do <strong>not<\/strong> click reboot.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">6. Verify ZFS Before First Boot<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Go back to the SSH terminal running QEMU and press:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Ctrl + C\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Now you are back in Rescue Mode.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">If Rescue Mode does not have ZFS tools, <code>zpool import<\/code> may ask to temporarily install ZFS support. Accept it.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Run:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>zpool import\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Expected:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>pool: rpool\nstate: ONLINE\n\nrpool\n  mirror-0\n    nvme0n1p3 ONLINE\n    nvme1n1p3 ONLINE\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">If you see only one disk online, or one disk still has LVM, stop and clean\/reinstall before booting. A correct ZFS RAID1 install must show both NVMe partitions online.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Import and mount the new system:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>zpool import -f -R \/mnt rpool\nzfs list\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">You should see:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>rpool\/ROOT\/pve-1  \/mnt\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">7. Fix the Network Interface Name<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The installer may have saved the temporary QEMU interface, such as <code>ens3<\/code>. We need to replace it with the real physical NIC.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Find the real NIC name:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>udevadm test-builtin net_id \/sys\/class\/net\/eth0 2&gt;\/dev\/null | grep ID_NET_NAME\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Example:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>ID_NET_NAME_PATH=enp35s0\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Check the Proxmox network file:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>cat \/mnt\/etc\/network\/interfaces\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">If you see something like:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>iface ens3 inet manual\n\nauto vmbr0\niface vmbr0 inet static\n    bridge-ports ens3\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">replace <code>ens3<\/code> with the real interface:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>cp \/mnt\/etc\/network\/interfaces \/mnt\/etc\/network\/interfaces.bak-before-first-boot\n\nsed -i 's\/ens3\/enp35s0\/g' \/mnt\/etc\/network\/interfaces\n\ncat \/mnt\/etc\/network\/interfaces\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Expected format:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>auto lo\niface lo inet loopback\n\niface enp35s0 inet manual\n\nauto vmbr0\niface vmbr0 inet static\n    address &#91;SENSITIVE: SERVER_MAIN_IPV4]\/CIDR\n    gateway &#91;SENSITIVE: SERVER_GATEWAY]\n    bridge-ports enp35s0\n    bridge-stp off\n    bridge-fd 0\n\nsource \/etc\/network\/interfaces.d\/*\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Check hostname files:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>cat \/mnt\/etc\/hostname\ncat \/mnt\/etc\/hosts\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Expected:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>pve1\n&#91;SENSITIVE: SERVER_MAIN_IPV4] pve1.example.com pve1\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Export and reboot:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>zpool export rpool\nreboot\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">8. First Proxmox Login<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Open:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>https:\/\/&#91;SENSITIVE: SERVER_MAIN_IPV4]:8006\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Login:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>User: root\nRealm: Linux PAM\nPassword: &#91;SENSITIVE: PROXMOX_ROOT_PASSWORD]\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">A browser SSL warning is normal at this stage because Proxmox uses a self-signed certificate before ACME is configured.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Check ZFS:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>zpool status\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Expected:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>rpool ONLINE\nmirror-0 ONLINE\nnvme0n1p3 ONLINE\nnvme1n1p3 ONLINE\nerrors: No known data errors\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">9. Fix Proxmox Repositories<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Fresh Proxmox installs may show:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>You do not have a valid subscription\napt-get update failed code 100\n401 Unauthorized\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">That happens when enterprise repositories are enabled without a Proxmox subscription. The enterprise repository is for subscription users; the no-subscription repository does not need a subscription, but Proxmox notes it is not as heavily tested as enterprise. (<a href=\"https:\/\/pve.proxmox.com\/wiki\/Package_Repositories?utm_source=chatgpt.com\" target=\"_blank\" rel=\"noopener\">Proxmox VE<\/a>)<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Check repos:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>grep -R \"enterprise.proxmox.com\\|download.proxmox.com\\|proxmox\" \\\n  \/etc\/apt\/sources.list \\\n  \/etc\/apt\/sources.list.d\/*.list \\\n  \/etc\/apt\/sources.list.d\/*.sources 2&gt;\/dev\/null\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Disable enterprise repos and add no-subscription:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>mkdir -p \/root\/apt-sources-backup\n\ncp -a \/etc\/apt\/sources.list \/root\/apt-sources-backup\/ 2&gt;\/dev\/null || true\ncp -a \/etc\/apt\/sources.list.d \/root\/apt-sources-backup\/\n\nmv \/etc\/apt\/sources.list.d\/pve-enterprise.sources \/etc\/apt\/sources.list.d\/pve-enterprise.sources.disabled 2&gt;\/dev\/null || true\nmv \/etc\/apt\/sources.list.d\/ceph.sources \/etc\/apt\/sources.list.d\/ceph.sources.disabled 2&gt;\/dev\/null || true\n\ncat &gt; \/etc\/apt\/sources.list.d\/pve-no-subscription.sources &lt;&lt;'EOF'\nTypes: deb\nURIs: http:\/\/download.proxmox.com\/debian\/pve\nSuites: trixie\nComponents: pve-no-subscription\nSigned-By: \/usr\/share\/keyrings\/proxmox-archive-keyring.gpg\nEOF\n\napt update\napt dist-upgrade -y\nreboot\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">After reboot:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>pveversion\nzpool status\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">10. Configure DNS Hostname<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">In Cloudflare DNS, create:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Type: A\nName: pve1\nValue: &#91;SENSITIVE: SERVER_MAIN_IPV4]\nProxy status: DNS only \/ gray cloud\nTTL: Auto\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Keep it <strong>DNS only<\/strong>, because Cloudflare\u2019s normal proxy only supports specific HTTP\/HTTPS ports and Proxmox uses port <code>8006<\/code>. Cloudflare documents the ports it proxies by default; <code>8006<\/code> is not one of the standard proxied HTTPS ports. (<a href=\"https:\/\/developers.cloudflare.com\/fundamentals\/reference\/network-ports\/?utm_source=chatgpt.com\" target=\"_blank\" rel=\"noopener\">Cloudflare Docs<\/a>)<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Test from your computer:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>dig +short pve1.example.com\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Then access:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>https:&#47;&#47;pve1.example.com:8006\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">11. Harden SSH<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Create a dedicated SSH key on your desktop:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>mkdir -p ~\/.ssh\nchmod 700 ~\/.ssh\n\nssh-keygen -t ed25519 -a 100 -C \"desktop1-proxmox-pve1\" -f ~\/.ssh\/pve1_desktop1_ed25519\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Add the key to Proxmox:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>cat ~\/.ssh\/pve1_desktop1_ed25519.pub | ssh root@&#91;SENSITIVE: SERVER_MAIN_IPV4] \"mkdir -p ~\/.ssh &amp;&amp; chmod 700 ~\/.ssh &amp;&amp; cat &gt;&gt; ~\/.ssh\/authorized_keys &amp;&amp; chmod 600 ~\/.ssh\/authorized_keys\"\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Test:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>ssh -i ~\/.ssh\/pve1_desktop1_ed25519 root@&#91;SENSITIVE: SERVER_MAIN_IPV4]\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Add a shortcut on your Mac:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>nano ~\/.ssh\/config\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Add:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Host proxmox\n    HostName pve1.example.com\n    User root\n    Port 19\n    IdentityFile ~\/.ssh\/pve1_desktop1_ed25519\n    IdentitiesOnly yes\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Now configure Proxmox SSH to use port <code>19<\/code> and key-only login:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>cat > \/etc\/ssh\/sshd_config.d\/99-custom-ssh-hardening.conf &lt;&lt;'EOF'\nPort 19\n\nPubkeyAuthentication yes\nPasswordAuthentication no\nKbdInteractiveAuthentication no\nChallengeResponseAuthentication no\n\nPermitRootLogin prohibit-password\nUsePAM yes\nEOF\n\n\/usr\/sbin\/sshd -t\nsystemctl reload ssh\nss -tlnp | grep sshd\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Open a new terminal and test:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>ssh proxmox\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Only after key login works should you confirm port <code>22<\/code> is closed:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>ss -tlnp | grep sshd\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Expected:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>0.0.0.0:19\n&#91;::]:19\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">12. Use Hetzner Firewall First<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">For the host firewall, use Hetzner Robot firewall first. Hetzner\u2019s dedicated-server firewall is stateless and configured at the switch port. Hetzner warns that enabling it before adding rules can block all incoming traffic. (<a href=\"https:\/\/docs.hetzner.com\/robot\/dedicated-server\/firewall\/?utm_source=chatgpt.com\" target=\"_blank\" rel=\"noopener\">Hetzner Docs<\/a>)<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">In Hetzner Robot Firewall, add these incoming rules before removing \u201cAllow all\u201d:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>Name<\/th><th>Version<\/th><th>Protocol<\/th><th>Source IP<\/th><th>Destination IP<\/th><th>Source port<\/th><th>Destination port<\/th><th>TCP flags<\/th><th>Action<\/th><\/tr><\/thead><tbody><tr><td>Allow SSH 19<\/td><td>IPv4<\/td><td>TCP<\/td><td>empty<\/td><td>empty<\/td><td>empty<\/td><td>19<\/td><td>empty<\/td><td>accept<\/td><\/tr><tr><td>Allow Proxmox 8006<\/td><td>IPv4<\/td><td>TCP<\/td><td>empty<\/td><td>empty<\/td><td>empty<\/td><td>8006<\/td><td>empty<\/td><td>accept<\/td><\/tr><tr><td>Allow TCP Replies<\/td><td>IPv4<\/td><td>TCP<\/td><td>empty<\/td><td>empty<\/td><td>empty<\/td><td>32768-65535<\/td><td>ack<\/td><td>accept<\/td><\/tr><tr><td>Allow DNS Replies<\/td><td>IPv4<\/td><td>UDP<\/td><td>empty<\/td><td>empty<\/td><td>53<\/td><td>32768-65535<\/td><td>empty<\/td><td>accept<\/td><\/tr><tr><td>Allow Ping<\/td><td>IPv4<\/td><td>ICMP<\/td><td>empty<\/td><td>empty<\/td><td>empty<\/td><td>empty<\/td><td>empty<\/td><td>accept<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Then remove\/disable:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Allow all\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Test immediately:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>ssh proxmox\napt update\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">And test the web UI:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>https:&#47;&#47;pve1.example.com:8006\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Do not enable Proxmox\u2019s internal firewall until you understand the rule order and have rescue access ready. If you enable it incorrectly, you can block both SSH and the web UI.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">13. Add Trusted SSL with Let\u2019s Encrypt and Cloudflare<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Proxmox supports ACME\/Let\u2019s Encrypt certificates, including DNS challenge plugins. The domain must resolve correctly to the node when using certificate validation. (<a href=\"https:\/\/pve.proxmox.com\/wiki\/Certificate_Management?utm_source=chatgpt.com\" target=\"_blank\" rel=\"noopener\">Proxmox VE<\/a>)<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">In Cloudflare, create an API token:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Profile \u2192 API Tokens \u2192 Create Token \u2192 Edit zone DNS template\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Cloudflare documents creating API tokens through <strong>My Profile \u2192 API Tokens \u2192 Create Token<\/strong>, including using templates such as \u201cEdit zone DNS.\u201d (<a href=\"https:\/\/developers.cloudflare.com\/fundamentals\/api\/get-started\/create-token\/?utm_source=chatgpt.com\" target=\"_blank\" rel=\"noopener\">Cloudflare Docs<\/a>)<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Use permissions:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Zone \u2192 DNS \u2192 Edit\nZone \u2192 Zone \u2192 Read\nScope: Specific zone \u2192 example.com\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Do not paste the token publicly.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">In Proxmox:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Datacenter \u2192 ACME \u2192 Accounts \u2192 Add\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Create:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Account Name: letsencrypt\nEmail: &#91;SENSITIVE: ADMIN_EMAIL]\nDirectory: Let\u2019s Encrypt V2\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Then:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Datacenter \u2192 ACME \u2192 Challenge Plugins \u2192 Add\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Use:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Plugin ID: cloudflare-example\nDNS API: Cloudflare Managed DNS\nAPI Data:\nCF_Token=&#91;SENSITIVE: CLOUDFLARE_API_TOKEN]\nCF_Zone_ID=&#91;SENSITIVE: CLOUDFLARE_ZONE_ID]\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Then add the ACME domain:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>pve1 \u2192 System \u2192 Certificates \u2192 ACME \u2192 Add\nChallenge Type: DNS\nDomain: pve1.example.com\nPlugin: cloudflare-example\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">If the GUI does not show an order button, run from SSH:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>pvenode config set --acme account=letsencrypt\npvenode acme cert order\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Expected:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>All domains validated!\nSetting pveproxy certificate and key\nRestarting pveproxy\nTask OK\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Refresh:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>https:&#47;&#47;pve1.example.com:8006\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">You should now have a trusted SSL certificate.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">For wildcard certificates, use Traefik + Cloudflare later inside a Docker VM. Do not force wildcard management through the Proxmox host unless you specifically need it there.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">14. Final Health Check<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Run:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>zpool status\napt update\npveversion\nss -tlnp | grep sshd\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Expected:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>ZFS: ONLINE, no known data errors\nAPT: no repository errors\nSSH: listening only on 19\nWeb UI: https:\/\/pve1.example.com:8006\nSSL: valid certificate\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Final Result<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">You now have:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Proxmox installed from official ISO\nZFS RAID1 mirror across both NVMe drives\nCorrect physical network interface\nHostname and DNS configured\nRepositories fixed\nSystem updated\nSSH key-only login\nCustom SSH port 19\nHetzner firewall protecting the host\nTrusted Let\u2019s Encrypt SSL certificate\nClean base ready for VMs\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The next recommended steps are to create VM templates, configure off-server backups, and build your production VMs one at a time.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Install Proxmox on Dedicated server This tutorial shows how to install Proxmox VE on a dedicated&#8230;<\/p>\n","protected":false},"author":1,"featured_media":3278,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_kad_blocks_custom_css":"","_kad_blocks_head_custom_js":"","_kad_blocks_body_custom_js":"","_kad_blocks_footer_custom_js":"","_kadence_starter_templates_imported_post":false,"_kad_post_transparent":"","_kad_post_title":"","_kad_post_layout":"","_kad_post_sidebar_id":"","_kad_post_content_style":"","_kad_post_vertical_padding":"","_kad_post_feature":"","_kad_post_feature_position":"above","_kad_post_header":false,"_kad_post_footer":false,"_kad_post_classname":"","footnotes":""},"categories":[30,31],"tags":[],"class_list":["post-3272","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-server","category-linux"],"taxonomy_info":{"category":[{"value":30,"label":"Server"},{"value":31,"label":"Linux"}]},"featured_image_src_large":["https:\/\/muhammadsoliman.com\/wp-content\/uploads\/2026\/05\/ChatGPT-Image-May-26-2026-04_54_23-PM-1-1024x1024.png",1024,1024,true],"author_info":{"display_name":"Muhammad Soliman","author_link":"https:\/\/muhammadsoliman.com\/author\/muhmmad-soliman\/"},"comment_info":0,"category_info":[{"term_id":30,"name":"Server","slug":"server","term_group":0,"term_taxonomy_id":30,"taxonomy":"category","description":"","parent":0,"count":1,"filter":"raw","cat_ID":30,"category_count":1,"category_description":"","cat_name":"Server","category_nicename":"server","category_parent":0},{"term_id":31,"name":"Linux","slug":"linux","term_group":0,"term_taxonomy_id":31,"taxonomy":"category","description":"","parent":0,"count":1,"filter":"raw","cat_ID":31,"category_count":1,"category_description":"","cat_name":"Linux","category_nicename":"linux","category_parent":0}],"tag_info":false,"_links":{"self":[{"href":"https:\/\/muhammadsoliman.com\/index.php\/wp-json\/wp\/v2\/posts\/3272","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/muhammadsoliman.com\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/muhammadsoliman.com\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/muhammadsoliman.com\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/muhammadsoliman.com\/index.php\/wp-json\/wp\/v2\/comments?post=3272"}],"version-history":[{"count":3,"href":"https:\/\/muhammadsoliman.com\/index.php\/wp-json\/wp\/v2\/posts\/3272\/revisions"}],"predecessor-version":[{"id":3281,"href":"https:\/\/muhammadsoliman.com\/index.php\/wp-json\/wp\/v2\/posts\/3272\/revisions\/3281"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/muhammadsoliman.com\/index.php\/wp-json\/wp\/v2\/media\/3278"}],"wp:attachment":[{"href":"https:\/\/muhammadsoliman.com\/index.php\/wp-json\/wp\/v2\/media?parent=3272"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/muhammadsoliman.com\/index.php\/wp-json\/wp\/v2\/categories?post=3272"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/muhammadsoliman.com\/index.php\/wp-json\/wp\/v2\/tags?post=3272"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}