VPS Tutorials
Don't Expose VPS Databases Directly to the Public Internet (2026): How to Make MySQL, PostgreSQL, and Redis Remote Access Secure
Many VPS incidents aren't caused by bad application code, but by MySQL, PostgreSQL, and Redis being opened directly to the public internet. This article explains why you shouldn't expose 3306/5432/6379, and covers safer remote access methods such as SSH tunnels, VPNs, private networks, and allowlists.

A friend of mine once changed MySQL's bind-address to 0.0.0.0 for convenience, then opened port 3306 in the firewall. The reason was simple: it made connecting from local Navicat easy.
At first it really was convenient, until one day the CPU suddenly spiked and a bunch of strange connections appeared in the database. He checked the logs and found that port 3306 had long since been scanned. The password hadn't been cracked, but it had been under continuous brute-force attacks for days.
Redis is even scarier. A Redis instance with no password exposed directly to the public internet is basically just waiting to be scanned. Many of those incidents online where scheduled tasks get written, miners get installed, or databases get wiped come from exactly this.
So this article makes just one point: a database is not a website. Don't expose it directly to the public internet just to make remote connections convenient.
Common database ports include:
- MySQL / MariaDB: 3306
- PostgreSQL: 5432
- Redis: 6379
- MongoDB: 27017
- Elasticsearch: 9200
If these ports are exposed to the public internet, they will be scanned quickly. It's not "someone might scan them" — someone will almost certainly scan them.
The right approach is:
- Application and database on the same VPS: database listens on local
127.0.0.1 - Application and database on the same private network: only allow access from private IPs
- Temporary management from a local computer: use an SSH tunnel
- Long-term management by multiple people: use a VPN or bastion host
- Public access is mandatory: only allow a fixed IP allowlist, and enable strong passwords and TLS
Don't treat "it connects" as successful configuration. For database remote access, first ask: who can connect, from where, is it encrypted, and how do you stop the bleeding if something goes wrong.
Many tutorials tell you: change bind-address in the MySQL config from 127.0.0.1 to 0.0.0.0, then allow 3306, and you can connect remotely.
Technically correct, but dangerous from a security standpoint.
0.0.0.0 means listening on all network interfaces. If the firewall is also opened up, then any public IP can attempt to connect to this database port.
You can check listening ports with:
ss -lntp | grep -E '3306|5432|6379|27017|9200'
If you see:
0.0.0.0:3306
0.0.0.0:6379
be on your guard. Unless you're absolutely sure the firewall only allows trusted IPs, this is public exposure.
The safer default is:
127.0.0.1:3306
127.0.0.1:5432
127.0.0.1:6379
When the application and database are on the same VPS, listening locally is enough. Nginx, PHP, Node, and Python applications can all connect from the local machine; the database doesn't need to be open to the public internet.
If you just want to manage the database from your local computer using Navicat, DataGrip, or TablePlus, the simplest and safest way is an SSH tunnel.
For example, if MySQL is on the VPS's local 127.0.0.1:3306, run this locally:
ssh -L 3307:127.0.0.1:3306 root@你的服务器IP
Then connect your local database tool to:
127.0.0.1:3307
The data actually travels over the encrypted SSH channel, and the database port is not exposed to the public internet.
PostgreSQL is similar:
ssh -L 5433:127.0.0.1:5432 root@你的服务器IP
Redis works too:
ssh -L 6380:127.0.0.1:6379 root@你的服务器IP
This trick suits personal management, temporary troubleshooting, and occasional data edits. The downside is that you can't connect when the tunnel drops, but that's actually a good thing: the port closes naturally when you're not using it.
If multiple people on the team need to access the database, it's not advisable for everyone to connect directly to the public database port.
A more robust approach is:
- Build a private network with WireGuard / Tailscale / ZeroTier
- Have the database listen only on the VPN private address
- Only allow the VPN subnet to access the database port
- Give each person their own VPN identity and permissions
For example, the database server has a WireGuard private IP:
10.8.0.2
Application servers or developer computers access it through the VPN:
10.8.0.2:3306
Neither the public security group nor the system firewall opens 3306. The database port can't be scanned from outside, so the risk is much lower.
If you use multiple VPS instances from the same cloud provider, you can also use a private network/VPC. Only allow private IPs to access the database port; don't open it to the public internet.
Some scenarios really do require public access, such as a third-party system pulling data from a fixed IP, or a fixed office egress IP.
In that case, don't allow 0.0.0.0/0; only allow fixed source IPs.
UFW example:
ufw allow from 你的办公公网IP to any port 3306 proto tcp
ufw deny 3306/tcp
ufw status numbered
iptables follows a similar idea: first allow trusted IPs, then reject other sources.
The same goes for cloud security groups: set the source to your fixed IP, not Anywhere.
At the same time, also do the following:
- Strong passwords, no weak credentials
- Disallow remote root login to the database
- Create a separate least-privilege account for the business
- Enable TLS/SSL (especially across the public internet)
- Regularly review failed login logs
- Backups must be restorable
An allowlist isn't a silver bullet, but it's far better than being open to the whole internet.
Redis is a major disaster zone.
Many people think Redis is just a cache, and if it gets deleted they can just regenerate it. But once Redis is exposed, the risk isn't just data loss — it can also be used to write malicious tasks, write files via master-slave replication, or serve as a mining jump host.
At minimum, Redis should do:
bind 127.0.0.1
protected-mode yes
requirepass 一个足够强的密码
If you need to give applications remote access, prefer a private network or VPN; don't open 6379 directly to the public internet.
Check whether Redis is listening on the public internet:
ss -lntp | grep 6379
If you see 0.0.0.0:6379, don't hesitate — immediately pull it back to local or private network.
If you find that a database port has been public for a while, don't just close the port and call it done.
It's recommended to do the following in order:
- Immediately close public access in the firewall/security group
- Preserve logs; don't clear them right away
- Check database users and permissions
- Change all database passwords
- Check for unfamiliar accounts, databases, and tables
- Check system scheduled tasks, SSH keys, and suspicious processes
- Compare critical data against trusted backups
- If Redis/MongoDB was ever running naked, treat it as an intrusion incident
For MySQL, you can check users:
SELECT user, host FROM mysql.user;
For PostgreSQL, check roles:
\du
For Redis, check the configuration, data, scheduled tasks, and the system level. The impact of a naked Redis can go beyond Redis itself.
Check listening ports on the VPS:
ss -lntp | grep -E '3306|5432|6379|27017|9200'
Check from your local computer whether the ports are reachable:
nmap -Pn -p 3306,5432,6379,27017,9200 你的服务器IP
If your local computer can scan these ports as open and you haven't set up an allowlist, then they're basically publicly accessible.
Then check the firewall:
ufw status verbose
iptables -L -n -v
Also check the security group in your cloud provider's console. Many people only close the system firewall and forget the cloud firewall; others tighten the cloud security group but the service in the system is still listening on the public internet. Check both sides.
Here's a simple table:
| Scenario | Recommended solution |
|---|---|
| Local application connects to local database | Listen on 127.0.0.1 |
| Occasional management from a local computer | SSH tunnel |
| Long-term team management | WireGuard / Tailscale / ZeroTier |
| Internal communication between multiple VPS | Private network / VPC |
| Third-party fixed IP access | Public allowlist + TLS + least privilege |
| Redis cache | Local or private network; avoid public internet if possible |
| Temporary troubleshooting | Temporary allowlist, delete immediately after use |
If you don't know which to choose, default to an SSH tunnel. Simple, secure, and sufficient.
After deploying a database on a new VPS, I check like this:
- Are MySQL/PostgreSQL/Redis listening only on local or private network
- Does the firewall avoid opening 3306/5432/6379 to the whole internet
- Does the cloud security group have no Anywhere inbound rules
- Is remote root/admin login to the database disabled
- Has a least-privilege account been created for the business
- Are there strong passwords or key authentication
- Is TLS/SSL needed
- Are backups and restore drills configured
- Do you know where to look at logs
- Have you recorded who can access the database
With database remote access, convenience and security often conflict. My advice is: if you can avoid the public internet, do; if you can use a tunnel, do; if you can use a private network, do.
When public access really is needed, don't expose the database like an ordinary web service. Web ports can face users; database ports should only face machines and people you trust.