| 1 | --- |
| 2 | title: Networking |
| 3 | description: Private networks, default-deny firewalls, managed load balancers with TLS, and stable Floating & Primary IPs for Hetzner Cloud. |
| 4 | --- |
| 5 | |
| 6 | The networking toolkit around your |
| 7 | [Servers](/hetzner/compute/servers): private **Networks** for |
| 8 | east-west traffic, **Firewalls** to close the public interface, |
| 9 | **Load Balancers** as the health-checked (and TLS-terminated) front |
| 10 | door, and **Floating/Primary IPs** for addresses that outlive any |
| 11 | one machine. |
| 12 | |
| 13 | ## Private networks |
| 14 | |
| 15 | A [`Network`](/providers/hetzner/network) is an RFC1918 range with |
| 16 | subnets per network zone (`eu-central`, `us-east`, `us-west`, |
| 17 | `ap-southeast`). Attach Servers via their `networks` prop: |
| 18 | |
| 19 | ```typescript |
| 20 | import * as Hetzner from "alchemy/Hetzner"; |
| 21 | |
| 22 | export const Net = Hetzner.Network("net", { |
| 23 | ipRange: "10.0.0.0/16", |
| 24 | subnets: [ |
| 25 | { type: "cloud", networkZone: "eu-central", ipRange: "10.0.1.0/24" }, |
| 26 | ], |
| 27 | }); |
| 28 | |
| 29 | export const Box = Hetzner.Server("box", { |
| 30 | serverType: "cx22", |
| 31 | image: "ubuntu-24.04", |
| 32 | location: "nbg1", |
| 33 | networks: [Net], |
| 34 | }); |
| 35 | ``` |
| 36 | |
| 37 | Traffic inside the network never touches the public interface — |
| 38 | which is what lets a [Firewall](#firewalls) close the public app |
| 39 | port entirely while a [Load Balancer](#load-balancer) still reaches |
| 40 | the Server over its private IP. The `ipRange` can be extended in |
| 41 | place; shrinking it replaces the network. |
| 42 | |
| 43 | ## Firewalls |
| 44 | |
| 45 | A [`Firewall`](/providers/hetzner/firewall) is a named rule set |
| 46 | applied to servers' public interfaces. Once applied it is |
| 47 | **default-deny inbound** (outbound stays open) — only what a rule |
| 48 | allows gets through: |
| 49 | |
| 50 | ```typescript |
| 51 | export const Wall = Hetzner.Firewall("wall", { |
| 52 | applyTo: [Box], |
| 53 | rules: [ |
| 54 | { |
| 55 | direction: "in", |
| 56 | protocol: "tcp", |
| 57 | port: "22", |
| 58 | sourceIps: ["0.0.0.0/0", "::/0"], |
| 59 | description: "ssh", |
| 60 | }, |
| 61 | { |
| 62 | direction: "in", |
| 63 | protocol: "tcp", |
| 64 | port: "3000", |
| 65 | sourceIps: ["10.0.0.0/16"], |
| 66 | description: "app, private net only", |
| 67 | }, |
| 68 | ], |
| 69 | }); |
| 70 | ``` |
| 71 | |
| 72 | `port` takes a single port (`"443"`) or a range (`"1024-5000"`). |
| 73 | Everything about a firewall — name, rules, targets — updates in |
| 74 | place; nothing replaces. |
| 75 | |
| 76 | ## Load Balancer |
| 77 | |
| 78 | A [`LoadBalancer`](/providers/hetzner/loadbalancer) fronts one or |
| 79 | more servers with health-checked forwarding. Target servers over |
| 80 | their private IP so the public port can stay closed: |
| 81 | |
| 82 | ```typescript |
| 83 | export const Edge = Hetzner.LoadBalancer("edge", { |
| 84 | location: "nbg1", |
| 85 | loadBalancerType: "lb11", |
| 86 | networks: [Net], |
| 87 | algorithm: "round_robin", |
| 88 | targets: [{ type: "server", server: Box, usePrivateIp: true }], |
| 89 | services: [ |
| 90 | { |
| 91 | protocol: "http", |
| 92 | listenPort: 80, |
| 93 | destinationPort: 3000, |
| 94 | healthCheck: { |
| 95 | protocol: "http", |
| 96 | port: 3000, |
| 97 | interval: 3, |
| 98 | timeout: 2, |
| 99 | retries: 2, |
| 100 | http: { path: "/health" }, |
| 101 | }, |
| 102 | }, |
| 103 | ], |
| 104 | }); |
| 105 | ``` |
| 106 | |
| 107 | Each entry in `services` is one listener, keyed by `listenPort`. |
| 108 | The resolved LB exposes `ipv4`/`ipv6`. The `loadBalancerType` |
| 109 | (`lb11`, `lb21`, …) upgrades in place but can't downgrade; changing |
| 110 | `location` replaces. |
| 111 | |
| 112 | ### TLS termination |
| 113 | |
| 114 | Terminate HTTPS at the LB with a |
| 115 | [`Certificate`](/providers/hetzner/certificate). A **managed** |
| 116 | certificate is issued by Let's Encrypt — its domains must be zones |
| 117 | in [Hetzner DNS](/hetzner/networking/dns): |
| 118 | |
| 119 | ```typescript |
| 120 | const cert = yield* Hetzner.Certificate("cert", { |
| 121 | type: "managed", |
| 122 | domainNames: ["example.com", "www.example.com"], |
| 123 | }); |
| 124 | |
| 125 | const lb = yield* Hetzner.LoadBalancer("edge", { |
| 126 | // ... |
| 127 | services: [ |
| 128 | { |
| 129 | protocol: "https", |
| 130 | listenPort: 443, |
| 131 | destinationPort: 3000, |
| 132 | certificates: [cert], |
| 133 | http: { redirectHttp: true }, |
| 134 | }, |
| 135 | ], |
| 136 | }); |
| 137 | ``` |
| 138 | |
| 139 | `redirectHttp: true` makes the LB answer port 80 with a redirect to |
| 140 | HTTPS. Bring-your-own PEM works too: `type: "uploaded"` with |
| 141 | `certificate` and `privateKey`. |
| 142 | |
| 143 | ## Floating & Primary IPs |
| 144 | |
| 145 | Servers get ephemeral public IPs by default — replace the server, |
| 146 | lose the address. Two resources decouple addresses from machines: |
| 147 | |
| 148 | - A [`PrimaryIp`](/providers/hetzner/primaryip) is the address a |
| 149 | server boots with. Declaring it as its own resource keeps it |
| 150 | alive independent of any server. |
| 151 | - A [`FloatingIp`](/providers/hetzner/floatingip) can be re-pointed |
| 152 | at any server in its home location at any time — the classic |
| 153 | failover/blue-green address. |
| 154 | |
| 155 | ```typescript |
| 156 | const float = yield* Hetzner.FloatingIp("float", { |
| 157 | type: "ipv4", |
| 158 | homeLocation: "nbg1", |
| 159 | }); |
| 160 | |
| 161 | yield* Hetzner.FloatingIpAssignment("float-on-box", { |
| 162 | floatingIp: float, |
| 163 | server: Box, |
| 164 | }); |
| 165 | ``` |
| 166 | |
| 167 | Re-deploying the assignment with a different `server` moves the |
| 168 | address in place — no replacement, no DNS change. |
| 169 | |
| 170 | ## Where next |
| 171 | |
| 172 | - [Tutorial part 4](/hetzner/tutorial/part-4) — wire a Network, |
| 173 | Firewall, and Load Balancer around a live Service. |
| 174 | - [DNS](/hetzner/networking/dns) — point your domain at the LB. |
| 175 | - [Providers reference](/providers) — every prop and attribute of |
| 176 | the resources above. |