VPS Tutorials
VPS Service Started but Unreachable from the Internet (2026): How to Troubleshoot Ports, Firewalls, Security Groups, and Listen Addresses
The service on your VPS is clearly running, local curl works fine, but the internet just can't reach it. Most problems come down to the listen address, the system firewall, the cloud provider's security group, port conflicts, or reverse proxy configuration. This article walks through the troubleshooting order step by step.

I've seen many beginners get stuck on the same problem: the service is clearly started, systemctl status shows running, local curl 127.0.0.1:3000 returns the page, but the browser can't open http://服务器IP:3000 at all.
What makes this kind of problem so annoying is that it looks like "the server is broken," when in most cases it's just one layer not letting traffic through: the program only listens on a local address, the firewall hasn't opened the port, the cloud provider's security group is blocking it, Nginx isn't forwarding, or the port is taken by another process.
Don't reinstall the system right away. Check in order, and you can usually pinpoint it in ten minutes.
The first step isn't opening the firewall, and it isn't changing Nginx—it's confirming which layer the problem is at.
Run this inside the VPS:
systemctl status your-service
curl -I http://127.0.0.1:3000
curl -I http://服务器内网IP:3000
curl -I http://服务器公网IP:3000
If 127.0.0.1 can't be accessed either, the service itself isn't working properly—check the application logs first.
If 127.0.0.1 works but the public IP doesn't, it's basically a listen address, firewall, security group, or network layer problem.
This judgment matters a lot. Many people have a bug in the service itself but keep changing the firewall; others have a working service but keep restarting the application—the direction is completely backwards.
This is the most common pitfall.
Many development frameworks listen only on localhost or 127.0.0.1 by default, so it's accessible locally but definitely not from the internet.
Check the listen address:
ss -lntp
You'll see something like:
LISTEN 0 511 127.0.0.1:3000 0.0.0.0:* users:(("node",pid=1234))
If it's 127.0.0.1:3000, only local access is allowed. To let the internet connect directly, change it to listen on:
0.0.0.0:3000
Different programs write it differently:
# Vite / Node 常见写法
npm run dev -- --host 0.0.0.0
# Python 简单服务
python3 -m http.server 8000 --bind 0.0.0.0
# Uvicorn
uvicorn app:app --host 0.0.0.0 --port 8000
That said, in production it's not necessarily advisable to expose the application port directly. A more common approach is for the application to listen on 127.0.0.1, then let Nginx/Caddy reverse proxy to the public 80/443.
Once the service listening is fine, check the system firewall.
On Ubuntu it's commonly UFW:
ufw status verbose
ufw allow 3000/tcp
On CentOS / Rocky / AlmaLinux it's commonly firewalld:
firewall-cmd --list-ports
firewall-cmd --add-port=3000/tcp --permanent
firewall-cmd --reload
If you use iptables/nftables directly, check:
iptables -L -n -v
nft list ruleset
One reminder here: don't just turn off the firewall to save trouble. Stopping it briefly for temporary troubleshooting is fine, but running exposed long-term is dangerous. The right approach is to allow only the ports you need, such as 80, 443, 22, or a specific business port.
Many cloud servers have two layers of firewall: one inside the system, one in the console security group.
You've done ufw allow 3000 inside the VPS, but if the cloud provider console hasn't allowed 3000, the internet still can't reach it.
Common platforms are all similar:
- AWS / Lightsail: Security Group / Firewall
- Google Cloud: VPC Firewall
- Azure: NSG
- Oracle Cloud: Security List + Network Security Group
- Vultr / DigitalOcean / Linode: Cloud Firewall
- Some VPS providers: a firewall built into the control panel
In the security group, confirm:
- Whether the inbound rule allows TCP 3000
- Whether the source IP is restricted to a specific address
- Whether the rule is bound to this machine
- Whether IPv4 and IPv6 are allowed separately
- Whether the cloud firewall denies all inbound by default
When troubleshooting, I often first use a temporary test port, like 8080, to confirm both the system and the cloud security group allow it, then narrow down to the official port.
Sometimes the service fails to start because the port is already in use.
Check port usage:
ss -lntp | grep ':3000'
lsof -i :3000
If you find the port is held by an old process, first confirm what it is—don't just kill -9. It could be the same application you started before, or another service.
Common handling:
# 看进程详情
ps -fp 进程ID
# 正常停止 systemd 服务
systemctl stop your-service
# 确认后再杀进程
kill 进程ID
When there's a port conflict, the application logs usually also show hints like EADDRINUSE, address already in use.
If you're not accessing the application port directly but going through a domain, you also need to check the reverse proxy.
Common Nginx configuration:
server {
listen 80;
server_name example.com;
location / {
proxy_pass http://127.0.0.1:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
Check the configuration and reload:
nginx -t
systemctl reload nginx
journalctl -u nginx -n 100 --no-pager
Common mistakes include:
proxy_passport written wrong- The application listens on 127.0.0.1:3000, but Nginx forwards to 127.0.0.1:3001
- The domain DNS doesn't resolve to this VPS
- Nginx config changed but not reloaded
- HTTPS certificate not configured properly, so the browser looks like the site won't open
Caddy users are similar—focus on whether the reverse_proxy address in the Caddyfile is correct.
IPv6 is increasingly common now, and it brings some strange problems.
For example, the service listens on:
[::1]:3000
Or only IPv6 is enabled and IPv4 isn't listening correctly. When you access IPv4 from the browser, it fails.
Check listening:
ss -lntp
curl -4 http://服务器IP:3000
curl -6 http://[IPv6地址]:3000
If you don't plan to support IPv6 for now, you can first bind the service explicitly to 0.0.0.0, and troubleshoot the security group and firewall for IPv4 first. Once IPv4 works, handle IPv6 separately.
Don't change IPv4, IPv6, DNS, and reverse proxy all at once. Change only one layer at a time so you know which step solved it.
A few VPS providers block certain ports, such as 25, 80, 443, SMTP-related ports, or require a ticket to open them.
If you've confirmed:
- The program is listening correctly
- The system firewall allows it
- The security group allows it
- The Nginx configuration is correct
- There's no port conflict
But the internet still can't reach it, try a different port to test:
python3 -m http.server 8080 --bind 0.0.0.0
Then access from your local computer:
curl -I http://服务器IP:8080
If 8080 works but 80/443 doesn't, it could be the provider, the console firewall, or the local web service configuration. At that point, opening a ticket with the test results is far more effective than just saying "the website won't open."
My own order is like this:
- Local
curl 127.0.0.1:端口 ss -lntpto check the listen address- Inside the VPS,
curl 公网IP:端口 - System firewall allows the port
- Cloud provider security group allows the port
- Local computer
curl 公网IP:端口 - Check Nginx/Caddy reverse proxy
- Check DNS, IPv6, HTTPS
- Switch to a test port to confirm whether the provider restricts it
- Contact support with evidence
The benefit of this order is that it goes from inside to outside, eliminating layer by layer, so you won't make random changes.
What's most feared during troubleshooting is random changes:
- Reinstalling the system right away
- Turning off all firewalls and leaving it that way long-term
- Changing Nginx, DNS, security groups, and application config all at once
- Not reading logs, just restarting based on feeling
- Requesting a refund before confirming the port
- Exposing 0.0.0.0 to all services, especially databases, Redis, and admin panels
Especially for services like databases, Redis, and Elasticsearch, don't open them to the public internet just to "make them accessible from outside." When you need remote access, prefer SSH tunnels, VPN, internal networks, or whitelists.
| Symptom | Check first |
|---|---|
| Local curl fails | Application logs, service status, port usage |
| Local works, public IP doesn't | Listen address, firewall, security group |
| IP works, domain doesn't | DNS, Nginx, server_name |
| HTTP works, HTTPS doesn't | Certificate, port 443, reverse proxy |
| IPv4 works, IPv6 doesn't | IPv6 listening, firewall, AAAA record |
| A certain port never works | Provider port restrictions, security group, carrier blocking |
When a VPS is unreachable from the internet, don't rush to doubt the machine's quality. Most problems are "the service is only listening locally" or "some layer of firewall isn't allowing it." Check in order, don't change ten things at once, and the problem is actually easier to solve.