[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"public-config":3,"i-flag:ph-4x3":14,"i-flag:us-4x3":19,"i-flag:de-4x3":21,"i-flag:hk-4x3":23,"i-tabler:lifebuoy":25,"i-tabler:timeline":28,"i-tabler:apps":30,"i-tabler:package":32,"i-tabler:circle-half-2":34,"i-tabler:sun":36,"i-tabler:moon":38,"i-tabler:menu-2":40,"i-tabler:user":42,"i-tabler:notebook":44,"i-simple-icons:forgejo":46,"i-tabler:shield-lock":48,"i-tabler:tools":50,"i-tabler:calendar":52,"i-tabler:x":54,"$f3fi8953kn3n83":56,"i-tabler:arrow-left":114,"i-tabler:tag":116},{"matomo":4,"booking":7,"site":9,"blog":11,"newsletter":12},{"enabled":5,"siteId":6},true,"7",{"enabled":5,"url":8},"https:\u002F\u002Fcal.com\u002Fjdbungart",{"url":10},"https:\u002F\u002Fjdneer.com",{"enabled":5},{"enabled":13},false,{"left":15,"top":15,"width":16,"height":17,"rotate":15,"vFlip":13,"hFlip":13,"body":18},0,640,480,"\u003Cpath fill=\"#0038a8\" d=\"M0 0h640v240H0z\"\u002F>\u003Cpath fill=\"#ce1126\" d=\"M0 240h640v240H0z\"\u002F>\u003Cpath fill=\"#fff\" d=\"M415.7 240L0 480V0\"\u002F>\u003Cpath fill=\"#fcd116\" d=\"M26.7 42.4L41 55l16.6-9.2l-7.4 17.5l14 13l-19-1.6l-8.1 17.2l-4.3-18.5L14 71l16.3-10zm323.8 172.3l.4 19l18 6.3l-18 6.2l-.4 19l-11.5-15.1l-18.2 5.5l10.8-15.6l-10.8-15.6l18.2 5.5zM37.2 388.1l8 17.2l19-1.6l-13.9 13l7.4 17.5l-16.6-9.1l-14.4 12.4l3.6-18.7L14 409l18.9-2.4zm114.2-249l-6.2 6.2l3.1 47l-3 .3l-5.7-42.9l-5.1 5l7.6 38.4a48 48 0 0 0-17.2 7.1l-21.7-32.4H96l26.4 34.3l-2.4 2l-31.1-35.5h-8.8v8.8l35.4 31l-2 2.5l-34.3-26.3v7.1l32.5 21.7q-5.2 7.8-7.1 17.2L66.3 223l-5.1 5l42.9 5.7q-.3 1.6-.3 3.1l-47-3l-6.2 6.2l6.2 6.2l47-3.1l.3 3.1l-42.9 5.7l5 5l38.4-7.6a48 48 0 0 0 7.1 17.2l-32.5 21.7v7.2l34.3-26.3l2 2.4l-35.4 31v8.8H89l31-35.4l2.5 2L96 312.2h7.2l21.7-32.5q7.8 5.2 17.2 7.1l-7.6 38.4l5 5l5.7-42.9q1.5.3 3.1.3l-3 47l6.1 6.2l6.3-6.2l-3.1-47l3-.3l5.7 43l5.1-5.1l-7.6-38.4a48 48 0 0 0 17.2-7.1l21.7 32.5h7.2l-26.4-34.3l2.4-2l31.1 35.4h8.8v-8.8l-35.4-31l2-2.4l34.3 26.3v-7.2l-32.5-21.7q5.2-7.8 7.1-17.2l38.3 7.6l5.1-5l-42.9-5.7q.3-1.5.3-3.1l47 3l6.2-6.1l-6.2-6.2l-47 3l-.3-3l42.9-5.7l-5-5l-38.4 7.5a48 48 0 0 0-7.1-17.2l32.5-21.7v-7.1l-34.3 26.3l-2-2.4l35.4-31v-8.9H214l-31 35.5l-2.5-2l26.4-34.3h-7.2L178 200.2q-7.8-5.2-17.2-7.1l7.6-38.3l-5-5l-5.7 42.8l-3.1-.3l3-47z\"\u002F>",{"left":15,"top":15,"width":16,"height":17,"rotate":15,"vFlip":13,"hFlip":13,"body":20},"\u003Cpath fill=\"#bd3d44\" d=\"M0 0h640v480H0\"\u002F>\u003Cpath stroke=\"#fff\" stroke-width=\"37\" d=\"M0 55.3h640M0 129h640M0 203h640M0 277h640M0 351h640M0 425h640\"\u002F>\u003Cpath fill=\"#192f5d\" d=\"M0 0h364.8v258.5H0\"\u002F>\u003Cmarker id=\"SVGEq3dreKJ\" markerHeight=\"30\" markerWidth=\"30\">\u003Cpath fill=\"#fff\" d=\"m14 0l9 27L0 10h28L5 27z\"\u002F>\u003C\u002Fmarker>\u003Cpath fill=\"none\" marker-mid=\"url(#SVGEq3dreKJ)\" d=\"m0 0l16 11h61h61h61h61h60L47 37h61h61h60h61L16 63h61h61h61h61h60L47 89h61h61h60h61L16 115h61h61h61h61h60L47 141h61h61h60h61L16 166h61h61h61h61h60L47 192h61h61h60h61L16 218h61h61h61h61h60z\"\u002F>",{"left":15,"top":15,"width":16,"height":17,"rotate":15,"vFlip":13,"hFlip":13,"body":22},"\u003Cpath fill=\"#fc0\" d=\"M0 320h640v160H0z\"\u002F>\u003Cpath fill=\"#000001\" d=\"M0 0h640v160H0z\"\u002F>\u003Cpath fill=\"red\" d=\"M0 160h640v160H0z\"\u002F>",{"left":15,"top":15,"width":16,"height":17,"rotate":15,"vFlip":13,"hFlip":13,"body":24},"\u003Cpath fill=\"#ec1b2e\" d=\"M0 0h640v480H0\"\u002F>\u003Cpath id=\"SVG5tfvBPpj\" fill=\"#fff\" d=\"M346.3 103.1C267 98 230.6 201.9 305.6 240.3c-26-22.4-20.6-55.3-10.1-72.4l1.9 1.1c-13.8 23.5-11.2 52.7 11.1 71c-12.7-12.3-9.5-39 12.1-48.9s23.6-39.3 16.4-49.1q-14.7-25.6 9.3-38.9M307.9 164l-4.7 7.4l-1.8-8.6l-8.6-2.3l7.8-4.3l-.6-8.9l6.5 6.1l8.3-3.3l-3.7 8.1l5.6 6.8z\"\u002F>\u003Cuse href=\"#SVG5tfvBPpj\" transform=\"rotate(72 312.5 243.5)\"\u002F>\u003Cuse href=\"#SVG5tfvBPpj\" transform=\"rotate(144 312.5 243.5)\"\u002F>\u003Cuse href=\"#SVG5tfvBPpj\" transform=\"rotate(216 312.5 243.5)\"\u002F>\u003Cuse href=\"#SVG5tfvBPpj\" transform=\"rotate(288 312.5 243.5)\"\u002F>",{"left":15,"top":15,"width":26,"height":26,"rotate":15,"vFlip":13,"hFlip":13,"body":27},24,"\u003Cg fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\">\u003Cpath d=\"M8 12a4 4 0 1 0 8 0a4 4 0 1 0-8 0\"\u002F>\u003Cpath d=\"M3 12a9 9 0 1 0 18 0a9 9 0 1 0-18 0m12 3l3.35 3.35M9 15l-3.35 3.35m0-12.7L9 9m9.35-3.35L15 9\"\u002F>\u003C\u002Fg>",{"left":15,"top":15,"width":26,"height":26,"rotate":15,"vFlip":13,"hFlip":13,"body":29},"\u003Cg fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\">\u003Cpath d=\"m4 16l6-7l5 5l5-6\"\u002F>\u003Cpath d=\"M14 14a1 1 0 1 0 2 0a1 1 0 1 0-2 0M9 9a1 1 0 1 0 2 0a1 1 0 1 0-2 0m-6 7a1 1 0 1 0 2 0a1 1 0 1 0-2 0m16-8a1 1 0 1 0 2 0a1 1 0 1 0-2 0\"\u002F>\u003C\u002Fg>",{"left":15,"top":15,"width":26,"height":26,"rotate":15,"vFlip":13,"hFlip":13,"body":31},"\u003Cpath fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M4 5a1 1 0 0 1 1-1h4a1 1 0 0 1 1 1v4a1 1 0 0 1-1 1H5a1 1 0 0 1-1-1zm0 10a1 1 0 0 1 1-1h4a1 1 0 0 1 1 1v4a1 1 0 0 1-1 1H5a1 1 0 0 1-1-1zm10 0a1 1 0 0 1 1-1h4a1 1 0 0 1 1 1v4a1 1 0 0 1-1 1h-4a1 1 0 0 1-1-1zm0-8h6m-3-3v6\"\u002F>",{"left":15,"top":15,"width":26,"height":26,"rotate":15,"vFlip":13,"hFlip":13,"body":33},"\u003Cpath fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"m12 3l8 4.5v9L12 21l-8-4.5v-9zm0 9l8-4.5M12 12v9m0-9L4 7.5m12-2.25l-8 4.5\"\u002F>",{"left":15,"top":15,"width":26,"height":26,"rotate":15,"vFlip":13,"hFlip":13,"body":35},"\u003Cpath fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M3 12a9 9 0 1 0 18 0a9 9 0 1 0-18 0m9-9v18m0-7l7-7m-7 12l8.5-8.5M12 9l4.5-4.5\"\u002F>",{"left":15,"top":15,"width":26,"height":26,"rotate":15,"vFlip":13,"hFlip":13,"body":37},"\u003Cpath fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M8 12a4 4 0 1 0 8 0a4 4 0 1 0-8 0m-5 0h1m8-9v1m8 8h1m-9 8v1M5.6 5.6l.7.7m12.1-.7l-.7.7m0 11.4l.7.7m-12.1-.7l-.7.7\"\u002F>",{"left":15,"top":15,"width":26,"height":26,"rotate":15,"vFlip":13,"hFlip":13,"body":39},"\u003Cpath fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M12 3h.393a7.5 7.5 0 0 0 7.92 12.446A9 9 0 1 1 12 2.992z\"\u002F>",{"left":15,"top":15,"width":26,"height":26,"rotate":15,"vFlip":13,"hFlip":13,"body":41},"\u003Cpath fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M4 6h16M4 12h16M4 18h16\"\u002F>",{"left":15,"top":15,"width":26,"height":26,"rotate":15,"vFlip":13,"hFlip":13,"body":43},"\u003Cpath fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M8 7a4 4 0 1 0 8 0a4 4 0 0 0-8 0M6 21v-2a4 4 0 0 1 4-4h4a4 4 0 0 1 4 4v2\"\u002F>",{"left":15,"top":15,"width":26,"height":26,"rotate":15,"vFlip":13,"hFlip":13,"body":45},"\u003Cpath fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M6 4h11a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H6a1 1 0 0 1-1-1V5a1 1 0 0 1 1-1m3 0v18m4-14h2m-2 4h2\"\u002F>",{"left":15,"top":15,"width":26,"height":26,"rotate":15,"vFlip":13,"hFlip":13,"body":47},"\u003Cpath fill=\"currentColor\" d=\"M16.777 0a2.9 2.9 0 1 1-2.529 4.322H12.91a4.266 4.266 0 0 0-4.265 4.195v2.118a7.08 7.08 0 0 1 4.147-1.42l.118-.002h1.338a2.9 2.9 0 0 1 5.43 1.422a2.9 2.9 0 0 1-5.43 1.422H12.91a4.266 4.266 0 0 0-4.265 4.195v2.319A2.9 2.9 0 0 1 7.222 24A2.9 2.9 0 0 1 5.8 18.57V8.589a7.11 7.11 0 0 1 6.991-7.108l.118-.001h1.338A2.9 2.9 0 0 1 16.778 0M7.223 19.905a1.194 1.194 0 1 0 0 2.389a1.194 1.194 0 0 0 0-2.389m9.554-10.464a1.194 1.194 0 1 0 0 2.389a1.194 1.194 0 0 0 0-2.39m0-7.735a1.194 1.194 0 1 0 0 2.389a1.194 1.194 0 0 0 0-2.389\"\u002F>",{"left":15,"top":15,"width":26,"height":26,"rotate":15,"vFlip":13,"hFlip":13,"body":49},"\u003Cg fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\">\u003Cpath d=\"M12 3a12 12 0 0 0 8.5 3A12 12 0 0 1 12 21A12 12 0 0 1 3.5 6A12 12 0 0 0 12 3\"\u002F>\u003Cpath d=\"M11 11a1 1 0 1 0 2 0a1 1 0 1 0-2 0m1 1v2.5\"\u002F>\u003C\u002Fg>",{"left":15,"top":15,"width":26,"height":26,"rotate":15,"vFlip":13,"hFlip":13,"body":51},"\u003Cg fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\">\u003Cpath d=\"M3 21h4L20 8a1.5 1.5 0 0 0-4-4L3 17zM14.5 5.5l4 4\"\u002F>\u003Cpath d=\"M12 8L7 3L3 7l5 5M7 8L5.5 9.5M16 12l5 5l-4 4l-5-5m4 1l-1.5 1.5\"\u002F>\u003C\u002Fg>",{"left":15,"top":15,"width":26,"height":26,"rotate":15,"vFlip":13,"hFlip":13,"body":53},"\u003Cpath fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M4 7a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2zm12-4v4M8 3v4m-4 4h16m-9 4h1m0 0v3\"\u002F>",{"left":15,"top":15,"width":26,"height":26,"rotate":15,"vFlip":13,"hFlip":13,"body":55},"\u003Cpath fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M18 6L6 18M6 6l12 12\"\u002F>",{"slug":57,"title":58,"excerpt":59,"image":60,"publishedAt":61,"topics":62,"readMinutes":66,"html":67,"metaTitle":68,"metaDescription":69,"shareImage":70,"updatedAt":74,"author":75,"related":77},"hetzner-cloud-private-network-behind-your-own-firewall","Building a Private Network on Hetzner Cloud Behind Your Own Firewall","Keep servers off the public internet: a Hetzner Cloud private network with one route, your own firewall as the gateway, and working configs for Ubuntu and Rocky Linux 8.","\u002Fcontent\u002Fimages\u002F2026\u002F10\u002Fhetzner-private-network-feature-a1.png","2026-10-22T20:00:00.000+08:00",[63],{"key":64,"label":65},"guides","Guides",6,"\u003Cp>I like keeping servers off the public internet. On Hetzner Cloud that is easy: put them in a private network, give one server a public IP to act as the firewall, and send everything else through it. Here is how I set it up, following Hetzner's \u003Ca href=\"https:\u002F\u002Fdocs.hetzner.com\u002Fnetworking\u002Fnetworks\u002Ftechnical-concepts\u002Farchitecture\u002F?ref=ghost.jdneer.com\">network architecture docs\u003C\u002Fa>, the \u003Ca href=\"https:\u002F\u002Fdocs.hetzner.com\u002Fcloud\u002Fnetworks\u002Fserver-configuration\u002F?ref=ghost.jdneer.com\">server configuration guide\u003C\u002Fa>, and the \u003Ca href=\"https:\u002F\u002Fcommunity.hetzner.com\u002Ftutorials\u002Fhow-to-set-up-nat-for-cloud-networks\u002F?ref=ghost.jdneer.com\">NAT gateway tutorial\u003C\u002Fa>.\u003C\u002Fp>\u003Cfigure class=\"kg-card kg-image-card kg-card-hascaption\">\u003Cimg src=\"\u002Fcontent\u002Fimages\u002F2026\u002F10\u002Fhetzner-network-overview-a1.png\" class=\"kg-image\" alt=\"Diagram: Server A 10.0.1.3 and Server B 10.0.1.10 send traffic to the network gateway 10.0.1.1, which routes it to the firewall 10.0.1.2 and out to the Internet; route 0.0.0.0\u002F0 via 10.0.1.2\" loading=\"lazy\" title=\"Network overview\" width=\"1600\" height=\"1000\" srcset=\"\u002Fcontent\u002Fimages\u002Fsize\u002Fw600\u002F2026\u002F10\u002Fhetzner-network-overview-a1.png 600w, \u002Fcontent\u002Fimages\u002Fsize\u002Fw1000\u002F2026\u002F10\u002Fhetzner-network-overview-a1.png 1000w, \u002Fcontent\u002Fimages\u002F2026\u002F10\u002Fhetzner-network-overview-a1.png 1600w\" sizes=\"(min-width: 720px) 720px\" \u002F>\u003Cfigcaption>Example layout with sample addresses.\u003C\u002Ffigcaption>\u003C\u002Ffigure>\u003Cp>One thing to understand before you start: there is no layer 2 adjacency between hosts on a Hetzner Cloud private network. Cloud subnets work at layer 3, so every packet is routed through the network gateway 10.0.1.1, even between two servers in the same subnet. Server A never talks to Server B directly. Each server only resolves the gateway's MAC address via ARP, never another server's, and there is no broadcast traffic between hosts. That is why each server gets a \u002F32 and an on-link route to the gateway. Hetzner explains this in its \u003Ca href=\"https:\u002F\u002Fdocs.hetzner.com\u002Fnetworking\u002Fnetworks\u002Ftechnical-concepts\u002Farchitecture\u002F?ref=ghost.jdneer.com\">network architecture docs\u003C\u002Fa>.\u003C\u002Fp>\u003Ch2 id=\"1-create-the-network-and-subnet\">1. Create the network and subnet\u003C\u002Fh2>\u003Cp>In the Hetzner Console, go to Networks and create a network. I use 10.0.1.0\u002F24 and add one subnet with the same range in my network zone. Hetzner takes the first address, 10.0.1.1, as the network gateway. You cannot assign it to a server.\u003C\u002Fp>\u003Cfigure class=\"kg-card kg-image-card\">\u003Cimg src=\"\u002Fcontent\u002Fimages\u002F2026\u002F10\u002Fhetzner-mock-subnets.png\" class=\"kg-image\" alt=\"Hetzner Console Subnets tab: subnet 10.0.1.0\u002F24 with fw1, server-a and server-b attached\" loading=\"lazy\" width=\"1800\" height=\"1040\" srcset=\"\u002Fcontent\u002Fimages\u002Fsize\u002Fw600\u002F2026\u002F10\u002Fhetzner-mock-subnets.png 600w, \u002Fcontent\u002Fimages\u002Fsize\u002Fw1000\u002F2026\u002F10\u002Fhetzner-mock-subnets.png 1000w, \u002Fcontent\u002Fimages\u002Fsize\u002Fw1600\u002F2026\u002F10\u002Fhetzner-mock-subnets.png 1600w, \u002Fcontent\u002Fimages\u002F2026\u002F10\u002Fhetzner-mock-subnets.png 1800w\" sizes=\"(min-width: 720px) 720px\" \u002F>\u003C\u002Ffigure>\u003Ch2 id=\"2-attach-the-servers\">2. Attach the servers\u003C\u002Fh2>\u003Cp>Attach the firewall and every private server to the network. Each one gets a private IP, shown in the Console next to the server. In my example the firewall is 10.0.1.2, Server A (Rocky Linux 8) is 10.0.1.3, and Server B (Ubuntu) is 10.0.1.10. Hetzner notes that private connections only work with the IP the Console shows, so do not invent your own.\u003C\u002Fp>\u003Cfigure class=\"kg-card kg-image-card\">\u003Cimg src=\"\u002Fcontent\u002Fimages\u002F2026\u002F10\u002Fhetzner-mock-resources.png\" class=\"kg-image\" alt=\"Hetzner Console Resources tab: fw1 10.0.1.2, server-a 10.0.1.3, server-b 10.0.1.10\" loading=\"lazy\" width=\"1800\" height=\"760\" srcset=\"\u002Fcontent\u002Fimages\u002Fsize\u002Fw600\u002F2026\u002F10\u002Fhetzner-mock-resources.png 600w, \u002Fcontent\u002Fimages\u002Fsize\u002Fw1000\u002F2026\u002F10\u002Fhetzner-mock-resources.png 1000w, \u002Fcontent\u002Fimages\u002Fsize\u002Fw1600\u002F2026\u002F10\u002Fhetzner-mock-resources.png 1600w, \u002Fcontent\u002Fimages\u002F2026\u002F10\u002Fhetzner-mock-resources.png 1800w\" sizes=\"(min-width: 720px) 720px\" \u002F>\u003C\u002Ffigure>\u003Cp>Give only the firewall a public IP. The other servers stay private-only.\u003C\u002Fp>\u003Ch2 id=\"3-add-the-default-route\">3. Add the default route\u003C\u002Fh2>\u003Cp>Under the network's Routes tab, add a route with destination 0.0.0.0\u002F0 and gateway 10.0.1.2, the firewall's private IP. This tells the Hetzner network gateway to hand all traffic for outside addresses to the firewall. Per the \u003Ca href=\"https:\u002F\u002Fdocs.hetzner.com\u002Fnetworking\u002Fnetworks\u002Ffaq\u002F?ref=ghost.jdneer.com\">Networks FAQ\u003C\u002Fa>, only cloud servers can be route gateways.\u003C\u002Fp>\u003Cfigure class=\"kg-card kg-image-card\">\u003Cimg src=\"\u002Fcontent\u002Fimages\u002F2026\u002F10\u002Fhetzner-mock-routes.png\" class=\"kg-image\" alt=\"Hetzner Console Routes tab: 0.0.0.0\u002F0 via gateway 10.0.1.2\" loading=\"lazy\" width=\"1800\" height=\"1120\" srcset=\"\u002Fcontent\u002Fimages\u002Fsize\u002Fw600\u002F2026\u002F10\u002Fhetzner-mock-routes.png 600w, \u002Fcontent\u002Fimages\u002Fsize\u002Fw1000\u002F2026\u002F10\u002Fhetzner-mock-routes.png 1000w, \u002Fcontent\u002Fimages\u002Fsize\u002Fw1600\u002F2026\u002F10\u002Fhetzner-mock-routes.png 1600w, \u002Fcontent\u002Fimages\u002F2026\u002F10\u002Fhetzner-mock-routes.png 1800w\" sizes=\"(min-width: 720px) 720px\" \u002F>\u003C\u002Ffigure>\u003Ch2 id=\"4-set-up-the-firewall\">4. Set up the firewall\u003C\u002Fh2>\u003Cp>The firewall keeps its normal default route on the public interface. It needs three things: IP forwarding, masquerade NAT for 10.0.1.0\u002F24 out of its public interface, and a DNS resolver on 10.0.1.2 for the clients. \u003Ca href=\"https:\u002F\u002Fhelp.mikrotik.com\u002Fdocs\u002Fspaces\u002FROS\u002Foverview?ref=ghost.jdneer.com\">MikroTik RouterOS (CHR)\u003C\u002Fa> or \u003Ca href=\"https:\u002F\u002Fdocs.vyos.io\u002F?ref=ghost.jdneer.com\">VyOS\u003C\u002Fa> come with all of that on board.\u003C\u002Fp>\u003Cp>Return traffic needs no extra route, because servers in the network already reach 10.0.1.0\u002F24 through 10.0.1.1.\u003C\u002Fp>\u003Ch2 id=\"5-set-up-the-clients\">5. Set up the clients\u003C\u002Fh2>\u003Cp>The private servers get a static address on the private interface, as Hetzner's \u003Ca href=\"https:\u002F\u002Fdocs.hetzner.com\u002Fcloud\u002Fnetworks\u002Fserver-configuration\u002F?ref=ghost.jdneer.com\">server configuration guide\u003C\u002Fa> documents for manual setups: the server's own IP as a \u002F32, and a default route via the network gateway 10.0.1.1. Use exactly the IP the Console shows for each server. For DNS I point the clients at the firewall, 10.0.1.2.\u003C\u002Fp>\u003Cp>On Rocky Linux 8 (Server A), first deal with hc-utils. Hetzner's images configure private interfaces through hc-utils, which runs its own \u003Ccode>hc-net-ifup@.service\u003C\u002Fcode> DHCP client per interface. Left in place, it fights your static config, and Hetzner warns this causes outages. Remove the package:\u003C\u002Fp>\u003Cpre>\u003Ccode class=\"language-bash\">[root@server-a ~]# dnf remove hc-utils\u003C\u002Fcode>\u003C\u002Fpre>\u003Cp>Then create \u003Ccode>\u002Fetc\u002Fsysconfig\u002Fnetwork-scripts\u002Fifcfg-enp7s0\u003C\u002Fcode>:\u003C\u002Fp>\u003Cpre>\u003Ccode class=\"language-bash\">[root@server-a ~]# cat \u002Fetc\u002Fsysconfig\u002Fnetwork-scripts\u002Fifcfg-enp7s0\nTYPE=Ethernet\nBOOTPROTO=none\nDEVICE=enp7s0\nONBOOT=yes\nIPADDR=10.0.1.3\nPREFIX=32\nDEFROUTE=yes\nDNS1=10.0.1.2\u003C\u002Fcode>\u003C\u002Fpre>\u003Cp>And \u003Ccode>\u002Fetc\u002Fsysconfig\u002Fnetwork-scripts\u002Froute-enp7s0\u003C\u002Fcode>. The first line makes the gateway reachable on-link, the same way Hetzner's doc does it, and the second sends everything else through it:\u003C\u002Fp>\u003Cpre>\u003Ccode class=\"language-bash\">[root@server-a ~]# cat \u002Fetc\u002Fsysconfig\u002Fnetwork-scripts\u002Froute-enp7s0\n10.0.1.1\u002F32 via 0.0.0.0 dev enp7s0 scope link\ndefault via 10.0.1.1 dev enp7s0\u003C\u002Fcode>\u003C\u002Fpre>\u003Cpre>\u003Ccode class=\"language-bash\">[root@server-a ~]# nmcli con reload\n[root@server-a ~]# nmcli con up \"System enp7s0\"\u003C\u002Fcode>\u003C\u002Fpre>\u003Cp>On Ubuntu (Server B), check whether hc-utils is installed too. Its Debian package ships the same rule that starts dhclient on the private interface, which would fight the static netplan config below. If it shows up, remove it:\u003C\u002Fp>\u003Cpre>\u003Ccode class=\"language-bash\">root@server-b:~# dpkg -l hc-utils\nroot@server-b:~# apt purge hc-utils\u003C\u002Fcode>\u003C\u002Fpre>\u003Cp>Then I use \u002Fetc\u002Fnetplan\u002F60-private.yaml, following Hetzner's static netplan example with a default route instead of the network range:\u003C\u002Fp>\u003Cpre>\u003Ccode class=\"language-yaml\">root@server-b:~# cat \u002Fetc\u002Fnetplan\u002F60-private.yaml\nnetwork:\n  version: 2\n  renderer: networkd\n  ethernets:\n    enp7s0:\n      addresses:\n        - 10.0.1.10\u002F32\n      routes:\n        - to: default\n          via: 10.0.1.1\n          on-link: true\n      nameservers:\n        addresses: [10.0.1.2]\u003C\u002Fcode>\u003C\u002Fpre>\u003Cpre>\u003Ccode class=\"language-bash\">root@server-b:~# netplan generate\nroot@server-b:~# netplan apply\u003C\u002Fcode>\u003C\u002Fpre>\u003Ch2 id=\"6-verify-it-worked\">6. Verify it worked\u003C\u002Fh2>\u003Cp>Check the address and routes first:\u003C\u002Fp>\u003Cpre>\u003Ccode class=\"language-bash\">[root@server-a ~]# ip -4 addr show enp7s0   # expect 10.0.1.3\u002F32\n[root@server-a ~]# ip route                 # expect default via 10.0.1.1\u003C\u002Fcode>\u003C\u002Fpre>\u003Cp>Then test each hop. The output below is a sample from my setup, so your addresses and times will differ. First, the firewall reaches Server A over the private network:\u003C\u002Fp>\u003Cpre>\u003Ccode class=\"language-bash\">root@fw1:~# ping -c4 -i .5 10.0.1.3\nPING 10.0.1.3 (10.0.1.3) 56(84) bytes of data.\n64 bytes from 10.0.1.3: icmp_seq=1 ttl=64 time=0.612 ms\n64 bytes from 10.0.1.3: icmp_seq=2 ttl=64 time=0.489 ms\n64 bytes from 10.0.1.3: icmp_seq=3 ttl=64 time=0.501 ms\n64 bytes from 10.0.1.3: icmp_seq=4 ttl=64 time=0.477 ms\u003C\u002Fcode>\u003C\u002Fpre>\u003Cp>Server A reaches the firewall:\u003C\u002Fp>\u003Cpre>\u003Ccode class=\"language-bash\">[root@server-a ~]# ping -c4 -i .5 10.0.1.2\nPING 10.0.1.2 (10.0.1.2) 56(84) bytes of data.\n64 bytes from 10.0.1.2: icmp_seq=1 ttl=64 time=0.598 ms\n64 bytes from 10.0.1.2: icmp_seq=2 ttl=64 time=0.455 ms\n64 bytes from 10.0.1.2: icmp_seq=3 ttl=64 time=0.470 ms\n64 bytes from 10.0.1.2: icmp_seq=4 ttl=64 time=0.462 ms\u003C\u002Fcode>\u003C\u002Fpre>\u003Cp>Server A goes outbound. This only works if the firewall NAT is doing its job:\u003C\u002Fp>\u003Cpre>\u003Ccode class=\"language-bash\">[root@server-a ~]# ping -c4 -i .5 1.1.1.1\nPING 1.1.1.1 (1.1.1.1) 56(84) bytes of data.\n64 bytes from 1.1.1.1: icmp_seq=1 ttl=57 time=27.1 ms\n64 bytes from 1.1.1.1: icmp_seq=2 ttl=57 time=27.1 ms\n64 bytes from 1.1.1.1: icmp_seq=3 ttl=57 time=27.4 ms\n64 bytes from 1.1.1.1: icmp_seq=4 ttl=57 time=27.6 ms\u003C\u002Fcode>\u003C\u002Fpre>\u003Cp>Server A resolves names through the firewall, then pings by name:\u003C\u002Fp>\u003Cpre>\u003Ccode class=\"language-bash\">[root@server-a ~]# dig +short jdneer.com @10.0.1.2\n5.223.46.104\u003C\u002Fcode>\u003C\u002Fpre>\u003Cpre>\u003Ccode class=\"language-bash\">[root@server-a ~]# ping -c4 -i .5 jdneer.com\nPING jdneer.com (5.223.46.104) 56(84) bytes of data.\n64 bytes from jdneer.com (5.223.46.104): icmp_seq=1 ttl=62 time=70.4 ms\n64 bytes from jdneer.com (5.223.46.104): icmp_seq=2 ttl=62 time=69.8 ms\n64 bytes from jdneer.com (5.223.46.104): icmp_seq=3 ttl=62 time=70.1 ms\n64 bytes from jdneer.com (5.223.46.104): icmp_seq=4 ttl=62 time=69.9 ms\u003C\u002Fcode>\u003C\u002Fpre>\u003Cp>Then do the real test: reboot the firewall and Server A, and run the same checks without touching anything.\u003C\u002Fp>","Hetzner Cloud Private Network Behind Your Own Firewall","Build a private network on Hetzner Cloud with your own firewall as the gateway: subnet, 0.0.0.0\u002F0 route, IP forwarding and NAT, client configs, and checks.",{"src":60,"width":71,"height":72,"type":73},1600,900,"image\u002Fpng","2026-10-22T23:18:21.000+08:00",{"name":76,"url":10,"owner":5},"JD Bungart",[78,90,98,106],{"slug":79,"title":80,"excerpt":81,"image":82,"publishedAt":83,"topics":84,"readMinutes":89},"package-cooldowns-github-package-firewall-npm-pnpm-bun-pip-uv-poetry","Package Cooldowns Compared: GitHub Package Firewall vs. What npm, pnpm, Yarn, Bun, pip, uv and Poetry Already Do","GitHub now offers an install-time firewall for npm, free in preview and metered later. Most package managers already ship a free cooldown. Here is what each tool does, the copy-paste config, and where cooldowns stop helping.","\u002Fcontent\u002Fimages\u002F2026\u002F10\u002Fpkg-cooldowns-conveyor-alberto-rodriguez-unsplash.jpg","2026-10-26T14:00:00.000+08:00",[85,86],{"key":64,"label":65},{"key":87,"label":88},"security","Security",1,{"slug":91,"title":92,"excerpt":93,"image":94,"publishedAt":95,"topics":96,"readMinutes":89},"guide-to-setting-up-an-ookla-speedtest-server-on-rockylinux-8","Guide to Setting Up an OOKLA Speedtest Server on RockyLinux 8\u002F9","I'm gonna walk you through a simple guide to get your OOKLA Speedtest server(s) up and running on RockyLinux 8. We'll even make it start automatically when your server boots. Oh, and we'll sprinkle in some basic security tips to keep things safe.","https:\u002F\u002Fimages.unsplash.com\u002Fphoto-1610034534785-4706b86dd6a5?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3wxMTc3M3wwfDF8c2VhcmNofDN8fHNwZWVkdGVzdHxlbnwwfHx8fDE2OTM3NTMxNjR8MA&ixlib=rb-4.0.3&q=80&w=2000","2025-01-10T14:10:00.000+08:00",[97],{"key":64,"label":65},{"slug":99,"title":100,"excerpt":101,"image":102,"publishedAt":103,"topics":104,"readMinutes":89},"stretch-ixgbe-driver-allow-unsupported-sfp-modules-on-intel-x520-cards","Debian: IXGBE Driver - Allow unsupported SFP+ Modules on Intel X520 Cards","The Server hardware we use for Proxmox deployments often has Intel X520 (ixgbe driver) or X710 (i40e driver) cards installed. These provide for several SFP+ ports required for 10 Gigabit connectivity. Enterprise vendors such as DELL and Intel provide compatibility lists for SFP transceivers that are supported by their network adapters. Often times adapter firmware requires specifically encoded modules, preventing any unsupported modules from being initialized. One way to work around this probl","\u002Fcontent\u002Fimages\u002F2019\u002F09\u002FIMG_20190926_171628.resized-2.jpg","2019-09-27T17:50:00.000+08:00",[105],{"key":64,"label":65},{"slug":107,"title":108,"excerpt":109,"image":110,"publishedAt":111,"topics":112,"readMinutes":89},"cost-efficient-virtual-machine-clustering-with-proxmox-ve-for-smes-and-enterprises","Cost-efficient Virtual Machine Clustering with Proxmox VE for SMEs and Enterprises","Proxmox VE is a web based, open source hardware virtualization platform based on Debian GNU\u002FLinux. It allows for virtualizing unmodified guests using the kernel based virtual machine (KVM) which is the native implementation of hardware assisted virtualization that's embedded into the linux kernel. In addition, Proxmox VE also supports Linux containers (LXC) being an OS level type of virtualization. Hardware assisted virtualization requires special support of a machine's processor commonly known","\u002Fcontent\u002Fimages\u002F2018\u002F09\u002FIMG_20180927_002650_1500-1.jpg","2018-09-29T22:37:00.000+08:00",[113],{"key":64,"label":65},{"left":15,"top":15,"width":26,"height":26,"rotate":15,"vFlip":13,"hFlip":13,"body":115},"\u003Cpath fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M5 12h14M5 12l6 6m-6-6l6-6\"\u002F>",{"left":15,"top":15,"width":26,"height":26,"rotate":15,"vFlip":13,"hFlip":13,"body":117},"\u003Cg fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\">\u003Cpath d=\"M6.5 7.5a1 1 0 1 0 2 0a1 1 0 1 0-2 0\"\u002F>\u003Cpath d=\"M3 6v5.172a2 2 0 0 0 .586 1.414l7.71 7.71a2.41 2.41 0 0 0 3.408 0l5.592-5.592a2.41 2.41 0 0 0 0-3.408l-7.71-7.71A2 2 0 0 0 11.172 3H6a3 3 0 0 0-3 3\"\u002F>\u003C\u002Fg>"]