“Opening a port” has two halves, and both must be true for a connection to work:
- A program is listening on that port.
- The firewall allows traffic to it — on the server and anywhere in between (your provider’s network firewall, a cloud security group).
Most “I opened the port but it doesn’t work” problems come from the first half. Let’s cover both.
Step 1: is something listening?
sudo ss -tulpn
Netid State Local Address:Port Process
tcp LISTEN 0.0.0.0:80 users:(("nginx",pid=812,fd=6))
tcp LISTEN 127.0.0.1:3306 users:(("mysqld",pid=901,fd=21))
udp UNCONN 0.0.0.0:51820 -
Look at the local address:
0.0.0.0:80or[::]:80— listening on all interfaces; reachable from outside if the firewall allows it.127.0.0.1:3306— localhost only. No firewall rule will make this reachable; you must change the application’s bind address.
If your port isn’t in the list, the service isn’t running or is configured for another port. Fix that first. More in checking open ports.
Step 2: open the port in the firewall
Find out which firewall your system uses:
sudo ufw status 2>/dev/null; sudo firewall-cmd --state 2>/dev/null; sudo nft list ruleset | head
UFW (Ubuntu, Debian)
sudo ufw allow 8080/tcp
sudo ufw allow 27015/udp
sudo ufw allow 6000:6010/tcp # range
sudo ufw allow from 203.0.113.5 to any port 5432 proto tcp # one source IP only
sudo ufw status numbered
Close it again with sudo ufw delete allow 8080/tcp. See the UFW guide.
firewalld (AlmaLinux, Rocky, Fedora)
sudo firewall-cmd --permanent --add-port=8080/tcp
sudo firewall-cmd --permanent --add-service=https # predefined service names
sudo firewall-cmd --reload
sudo firewall-cmd --list-all
Without --permanent the rule disappears on reload or reboot; with --permanent it needs --reload to take effect. To restrict by source IP, use a rich rule:
sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="203.0.113.5" port port="5432" protocol="tcp" accept'
Details in the firewalld guide.
iptables
sudo iptables -I INPUT -p tcp --dport 8080 -j ACCEPT
sudo iptables -L INPUT -n --line-numbers
iptables rules vanish at reboot unless saved: on Debian/Ubuntu install iptables-persistent and run sudo netfilter-persistent save. See the iptables tutorial.
nftables
sudo nft add rule inet filter input tcp dport 8080 accept
Persist rules in /etc/nftables.conf. See the nftables tutorial.
Step 3: SELinux (RHEL family)
On AlmaLinux and Rocky, SELinux may stop a service from binding to a non-standard port even when the firewall is open. For example, to run Nginx on 8080:
sudo semanage port -l | grep http_port_t
sudo semanage port -a -t http_port_t -p tcp 8080
Step 4: test from outside
From another machine (testing from the server itself bypasses the firewall):
nc -zv 203.0.113.10 8080 # TCP
curl -I http://203.0.113.10:8080
nmap -p 8080 203.0.113.10
succeeded/open— working.Connection refused— the packet arrived, but nothing listens on that port and interface. Back to step 1.- Timeout /
filtered— a firewall is dropping the packet: the server’s, the provider’s, or one on your own network.
For UDP, nc -zvu is unreliable because UDP has no handshake; test with the actual client (game client, WireGuard).
Docker containers
Published Docker ports (-p 8080:80) are opened by Docker itself, bypassing UFW. You don’t need a UFW rule for them — and a UFW deny rule won’t block them. Bind to 127.0.0.1 for internal services. See Docker networking.
Keep the attack surface small
Every open port is something an attacker can probe. Good habits:
- Open only what the service needs; databases, Redis and admin panels should listen on localhost or a private/VPN network.
- Restrict admin ports (SSH, database) to known IPs where possible.
- Remove rules for services you’ve retired.
- Audit periodically with
ss -tulpnand an externalnmapscan.
Our KVM VPS plans give you full control of the server firewall, and a dedicated IPv4 address for each server.