HetznerDocs

Search documentation

Find a Hetzner dedicated Linux topic

Firewall, port forwarding, and iptables

A firewall decides which traffic may enter the server. A port is a numbered door on an IP address. iptables (and nftables) are the Linux tools that enforce those rules. Two programs cannot normally sit on the same door.

How a port works

An IP address is the building. A port is a door number from 1 to 65535. SSH is usually door 22, HTTP 80, HTTPS 443. A program binds (sits on) an address plus a port plus a protocol (TCP or UDP). That combination must be unique.

PieceExampleStands for
IP203.0.113.10Internet Protocol address of the server
TCP22Transmission Control Protocol — reliable streams (SSH, web)
UDP53User Datagram Protocol — faster, no connection (DNS, games)
Bind0.0.0.0:80Listen on all IPv4 addresses, port 80

Can two programs use the same port?

Usually no. If nginx already holds 0.0.0.0:443/tcp, Apache cannot bind that same socket. Linux returns Address already in use (EADDRINUSE). That is not a firewall problem — the door is occupied.

SituationSame port OK?Why
Two apps on 0.0.0.0:80 TCPNoOne TCP listener per address:port
TCP 80 and UDP 80YesDifferent protocol — two separate doors
127.0.0.1:8080 and 203.0.113.10:8080YesDifferent bind addresses
Two public IPs, both port 80YesEach extra Hetzner IP is its own address
nginx on 443, apps on 3000 / 8080YesReverse proxy: one public door, many internal services
Port forward 8080 → VM:80, host also on 8080NoForwarding does not create a second listener. Something still owns 8080 on the host, or NAT steals that port before a local app sees it — pick one design.

Who is sitting on a port?

ss -tulpnss -tulpn | grep ':80'ss -tulpn | grep ':443'lsof -iTCP:22 -sTCP:LISTEN# Address already in use? this shows the occupant

ss = socket statistics (replaces old netstat). -t TCP, -u UDP, -l listening, -p process, -n numeric ports (no name lookup).

Three layers on a Hetzner dedicated box

  1. Hetzner Robot firewall — packet filter in front of the server. Robot → your server → Firewall. Allow SSH before you enable a default-deny policy, or you lock yourself out.
  2. Host firewallufw (easiest on Ubuntu) or iptables / nftables on the OS.
  3. App bind — even with the firewall open, nothing answers unless a program is listening.

UFW — beginner firewall (Ubuntu / Debian)

ufw = Uncomplicated Fire Wall. It writes iptables/nftables for you.

Safe first UFW policy

apt install -y ufwufw default deny incomingufw default allow outgoingufw allow OpenSSHufw allow 80/tcpufw allow 443/tcpufw enableufw status verbose

ufw allow 8080/tcp opens an extra TCP port. Prefer HTTPS on 443 via a reverse proxy instead of exposing many ports to the internet.

Open, list, delete

ufw allow 8080/tcp comment 'app'ufw status numberedufw delete 3ufw deny 23/tcpufw reloadufw disable

iptables — the long name

iptables = IP TABLES: tables of IPv4 packet rules. Modern Debian and Ubuntu often use nftables underneath; the iptables command still works as a compatibility front-end (iptables-nft). IPv6 is ip6tables.

WordLong formRole
filterFILTER tableAllow or drop (the usual firewall)
natNetwork Address TranslationRewrite IPs/ports — used for forwarding
INPUTPackets to this serverSSH, websites, or other services on the host
OUTPUTPackets from this serverapt update, DNS lookups
FORWARDPackets through this serverHost is a router to VMs/containers
PREROUTINGBefore routing decisionDNAT: change the destination port/IP
POSTROUTINGAfter routing decisionSNAT / MASQUERADE: change the source IP

See current rules

iptables -L -n -viptables -t nat -L -n -viptables -Snft list ruleset# nft = NetFilter Tables, the newer engine

-L list, -n numeric, -v verbose, -t table, -S rules as commands you could replay. nft list ruleset shows what the kernel actually runs today.

Port forwarding

Port forwarding (DNAT) sends traffic that hits this machine’s public IP and port to another IP and port — usually a VM or container on a private network. The public door stays one number; the guest can listen on 80 internally.

Typical later use: Proxmox/Incus guest 10.10.10.5:80 published as YOUR.PUBLIC.IP:8080.

Enable routing, then DNAT (example)

# 1. Allow the kernel to forward packetssysctl -w net.ipv4.ip_forward=1echo 'net.ipv4.ip_forward=1' >> /etc/sysctl.d/99-forward.conf# 2. Public TCP 8080 -> guest 10.10.10.5:80iptables -t nat -A PREROUTING -p tcp --dport 8080 -j DNAT --to-destination 10.10.10.5:80iptables -t nat -A POSTROUTING -j MASQUERADEiptables -A FORWARD -p tcp -d 10.10.10.5 --dport 80 -j ACCEPTiptables -A FORWARD -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT

DNAT = Destination NAT (rewrite where the packet goes). MASQUERADE = hide the guest behind the host’s public IP for replies. sysctl = kernel system control.

Keep iptables after reboot (Debian/Ubuntu)

apt install -y iptables-persistentnetfilter-persistent save

SSH forwarding (laptop, not iptables)

You can also forward a port through an SSH login, useful so a web UI or panel is reachable only while you are connected — nothing public.

Local forward: your laptop:8080 -> server:8080

ssh -N -L 8080:127.0.0.1:8080 root@YOUR.SERVER.IP

Then open http://127.0.0.1:8080 on the laptop. -L = local forward, -N = no remote shell. Bind the app on the server to 127.0.0.1 only so the public firewall never sees it.

Quick diagnosis

Firewall vs nothing listening

ss -tulpnufw status verboseiptables -L INPUT -n -vcurl -I http://127.0.0.1:80# Works locally but not from the internet? Robot firewall or UFW.# Connection refused? nothing listening.# Timeout? filtered (firewall drop).