1---
2title: Networking
3description: Private networks, default-deny firewalls, managed load balancers with TLS, and stable Floating & Primary IPs for Hetzner Cloud.
4---
5
6The networking toolkit around your
7[Servers](/hetzner/compute/servers): private **Networks** for
8east-west traffic, **Firewalls** to close the public interface,
9**Load Balancers** as the health-checked (and TLS-terminated) front
10door, and **Floating/Primary IPs** for addresses that outlive any
11one machine.
12
13## Private networks
14
15A [`Network`](/providers/hetzner/network) is an RFC1918 range with
16subnets per network zone (`eu-central`, `us-east`, `us-west`,
17`ap-southeast`). Attach Servers via their `networks` prop:
18
19```typescript
20import * as Hetzner from "alchemy/Hetzner";
21
22export 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
29export const Box = Hetzner.Server("box", {
30 serverType: "cx22",
31 image: "ubuntu-24.04",
32 location: "nbg1",
33 networks: [Net],
34});
35```
36
37Traffic inside the network never touches the public interface —
38which is what lets a [Firewall](#firewalls) close the public app
39port entirely while a [Load Balancer](#load-balancer) still reaches
40the Server over its private IP. The `ipRange` can be extended in
41place; shrinking it replaces the network.
42
43## Firewalls
44
45A [`Firewall`](/providers/hetzner/firewall) is a named rule set
46applied to servers' public interfaces. Once applied it is
47**default-deny inbound** (outbound stays open) — only what a rule
48allows gets through:
49
50```typescript
51export 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"`).
73Everything about a firewall — name, rules, targets — updates in
74place; nothing replaces.
75
76## Load Balancer
77
78A [`LoadBalancer`](/providers/hetzner/loadbalancer) fronts one or
79more servers with health-checked forwarding. Target servers over
80their private IP so the public port can stay closed:
81
82```typescript
83export 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
107Each entry in `services` is one listener, keyed by `listenPort`.
108The 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
114Terminate HTTPS at the LB with a
115[`Certificate`](/providers/hetzner/certificate). A **managed**
116certificate is issued by Let's Encrypt — its domains must be zones
117in [Hetzner DNS](/hetzner/networking/dns):
118
119```typescript
120const cert = yield* Hetzner.Certificate("cert", {
121 type: "managed",
122 domainNames: ["example.com", "www.example.com"],
123});
124
125const 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
140HTTPS. Bring-your-own PEM works too: `type: "uploaded"` with
141`certificate` and `privateKey`.
142
143## Floating & Primary IPs
144
145Servers get ephemeral public IPs by default — replace the server,
146lose 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
156const float = yield* Hetzner.FloatingIp("float", {
157 type: "ipv4",
158 homeLocation: "nbg1",
159});
160
161yield* Hetzner.FloatingIpAssignment("float-on-box", {
162 floatingIp: float,
163 server: Box,
164});
165```
166
167Re-deploying the assignment with a different `server` moves the
168address 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.