Ports and port forwarding on a server, explained
Make a service reachable: check what listens and where, open the port, test from outside, use SSH tunnels for admin tools and forward ports with nftables.
- Intermediate
- 15 min read
- Updated
Tested on: Ubuntu 24.04 LTS, Ubuntu 26.04 LTS, Debian 12, Debian 13, Windows Server 2022, Windows 11
On this page
A network service is reachable from the internet when two things are true: it listens on an address the outside world can reach, and the firewall allows its port. This guide checks both, tests from outside, and shows two kinds of forwarding: SSH tunnels to reach private services safely, and server-side port forwarding (DNAT) for virtual machines or containers. Replace 203.0.113.10 with your server's address.
Before you start
- Your HyperDC server has its own public IPv4 address, so you do not need router-style port forwarding to make a service reachable.
- Know the port your service uses, from its documentation or configuration.
Step 1: See what listens
Linux
sudo ss -tulpnEach line shows the protocol, the local address and port, and the process. Look at the address:
0.0.0.0:8080or[::]:8080: listens on all addresses, reachable from outside if the firewall allows it.127.0.0.1:8080or[::1]:8080: listens on the server only, not reachable from outside.
Windows
Get-NetTCPConnection -State Listen | Sort-Object LocalPort | Select-Object LocalAddress, LocalPort, OwningProcess0.0.0.0 and :: mean all addresses; 127.0.0.1 means the server only.
If the service listens on 127.0.0.1 but should be public, change its bind or listen address in its configuration. If it should stay private, keep it that way and use a tunnel (Step 4).
Step 2: Allow the port in the firewall
- UFW:
sudo ufw allow 8080/tcp; see UFW firewall. - nftables: add
tcp dport 8080 acceptto the input chain; see nftables firewall. - Windows:
New-NetFirewallRule -DisplayName "Allow 8080" -Direction Inbound -Protocol TCP -LocalPort 8080 -Action Allow; see Windows Defender Firewall rules.
Open only what must be public. Restrict admin ports to your own IP address.
Step 3: Test from outside
Test from your own computer, not from the server:
Linux and macOS
nc -vz 203.0.113.10 8080Windows
Test-NetConnection 203.0.113.10 -Port 8080Verify: the test reports the port as open (succeeded or TcpTestSucceeded : True). UDP services cannot be tested this way reliably; use the service's own client instead.
Step 4: Reach private services with an SSH tunnel
A local forward makes a service that listens on the server's 127.0.0.1 available on your computer, through the encrypted SSH connection. For example, a database admin tool on port 8080 of the server:
ssh -N -L 8080:127.0.0.1:8080 alex@203.0.113.10Keep the command running and open http://localhost:8080 on your computer. -N means no remote command, just the tunnel. To reach a database:
ssh -N -L 5433:127.0.0.1:5432 alex@203.0.113.10Your database client then connects to localhost:5433.
A remote forward does the opposite: it makes a port on your computer reachable from the server, for example for a webhook test:
ssh -N -R 9000:127.0.0.1:3000 alex@203.0.113.10On the server, 127.0.0.1:9000 now reaches port 3000 on your computer.
Step 5: Forward ports on the server (DNAT)
When you run virtual machines on a VDS or dedicated server, or services in a private network behind the server, the server can forward a public port to an internal address. First enable IPv4 forwarding:
echo 'net.ipv4.ip_forward = 1' | sudo tee /etc/sysctl.d/99-forwarding.conf
sudo sysctl --systemThen add a NAT table to /etc/nftables.conf. This example forwards public port 8080 to port 80 of an internal machine at 10.0.0.2 and masquerades its outgoing traffic:
table ip nat {
chain prerouting {
type nat hook prerouting priority dstnat; policy accept;
iifname "eth0" tcp dport 8080 dnat to 10.0.0.2:80
}
chain postrouting {
type nat hook postrouting priority srcnat; policy accept;
oifname "eth0" masquerade
}
}Replace eth0 with your public interface (ip -brief link). If your filter table drops forwarded traffic (as in our nftables guide), allow it in the forward chain:
ct state established,related accept
iifname "eth0" ip daddr 10.0.0.2 tcp dport 80 acceptCheck and load with sudo nft -c -f /etc/nftables.conf and sudo nft -f /etc/nftables.conf, then test from outside as in Step 3.
Troubleshooting
Connection refused. Nothing listens on that port, or it listens on 127.0.0.1 only (Step 1).
Connection timed out. The firewall drops the traffic, or your own network blocks outgoing connections to that port (Step 2, then try another network).
Outgoing connections to port 25 time out. Outbound port 25 is closed by default on HyperDC VPS. For services bought for a term of 3 months or longer, it is opened on request when you open a support ticket. Until then, send mail through a relay on port 587.
The SSH tunnel says "Address already in use". The local port is taken on your computer. Choose another local port, for example -L 18080:127.0.0.1:8080.
DNAT works from outside but not from the server itself. Packets generated on the server do not pass the prerouting chain. Test from another machine.
Next steps
- Firewall basics: UFW firewall and nftables firewall.
- Run your own virtual machines: VDS and nested virtualization.
Frequently asked questions
Do I need port forwarding on my HyperDC server?
Usually not. Your server has its own public IPv4 address, so a service that listens on it and is allowed by the firewall is reachable directly. Port forwarding on a home router solves a problem a server does not have.
What is the difference between 0.0.0.0 and 127.0.0.1?
A service listening on 127.0.0.1 only accepts connections from the server itself. 0.0.0.0 (or :: for IPv6) means all addresses, so it can be reached from outside if the firewall allows it.
How do I reach a database or admin panel without opening its port?
Use an SSH tunnel: ssh -L forwards a port on your computer through the encrypted SSH connection to the service on the server. Nothing extra is exposed to the internet.
Which tool shows open ports?
On Linux, ss -tulpn lists listening TCP and UDP sockets with the process. On Windows, Get-NetTCPConnection -State Listen does the same for TCP.
Why can I connect from the server but not from outside?
The service listens on 127.0.0.1 only, or the firewall blocks the port. Check the bind address first, then the firewall rules.
Sources
- man7.org/linux/man-pages/man8/ss.8.html
- man.openbsd.org/ssh
- wiki.nftables.org/wiki-nftables/index.php/Performing_Network_Addres…
- docs.kernel.org/networking/ip-sysctl.html
- learn.microsoft.com/en-us/powershell/module/nettcpip/get-nettcpconn…
- learn.microsoft.com/en-us/powershell/module/nettcpip/test-netconnec…