# Securing publicly accessible services on your VPS
Source: https://vpsdime.com/knowledgebase/getting-started/securing-public-services
Last reviewed: 2026-07-02
Summary: Service-by-service instructions for closing accidentally exposed services, bind MySQL/MariaDB, PostgreSQL, MongoDB, Redis, Elasticsearch, and Memcached to localhost, handle Docker's port publishing, give remote access safely (whitelists, SSH tunnels), and fix the quick wins vulnerability scanners flag (version disclosure, old TLS).

## What this is

A service that listens on **all interfaces** (`0.0.0.0`) is reachable by the entire internet, and internet scanners find it within hours, not days. An exposed database, cache, or API with a weak (or missing) password is the single most common way a VPS gets compromised. This guide closes those doors service by service. It's also the fix-list for when a **vulnerability scanner** has flagged your server.

The general rule behind every section: **if only the VPS itself needs a service, it should listen on `127.0.0.1`.** Localhost can't be reached from outside, no matter what.

## First, see what's actually exposed

On the VPS:

```
ss -tulnp
```

Read the **Local Address** column: entries on `127.0.0.1` are safe by construction; entries on `0.0.0.0` or `[::]` are offered to the whole internet and should each have a reason. For the outside view, scan yourself from home: `nmap -Pn YOUR.VPS.IP` shows what the world actually sees.

If the `ss` output is a wall of mystery, paste it whole into an [AI chatbot](https://vpsdime.com/knowledgebase/support-coverage/software-and-server-management) and ask it to identify each listener and flag any that look unintentional, genuinely one of the best uses of the tool.

## Layer 1: what our firewall already covers

Linux VPS sit behind our [managed network firewall](https://vpsdime.com/knowledgebase/client-area/services/firewall), which blocks internet access to the most-attacked service ports by default, PostgreSQL, MongoDB, Redis, Elasticsearch, Memcached, RabbitMQ, the Docker API, and more (the full port table is in that guide). So a mistakenly exposed Redis on a Linux VPS is shielded even before you fix it.

Two important gaps in that safety net:

- **MySQL/MariaDB (port 3306) is deliberately not blocked**, too many customers need it reachable, so securing MySQL is entirely on you (next section).
- **Premium and Windows VPS** don't have this firewall, everything below is on you there.

The firewall is a net, not a substitute: fix the binds anyway, then a mistake in either layer is covered by the other.

## Layer 2: bind services to localhost

For each service you run, apply the change, then restart it (`systemctl restart <service>`) and re-check with `ss -tulnp`.

**MySQL / MariaDB**, the one our firewall doesn't cover:

```
# /etc/mysql/mariadb.conf.d/50-server.cnf  (MariaDB)
# /etc/mysql/mysql.conf.d/mysqld.cnf       (MySQL)
bind-address = 127.0.0.1
```

**PostgreSQL** (`/etc/postgresql/*/main/postgresql.conf`):

```
listen_addresses = 'localhost'
```

**MongoDB** (`/etc/mongod.conf`):

```
net:
  bindIp: 127.0.0.1
```

**Redis** (`/etc/redis/redis.conf`), bind it *and* keep protected mode:

```
bind 127.0.0.1
protected-mode yes
```

Set `requirepass` too, an unauthenticated Redis is a remote-code-execution kit even semi-exposed.

**Elasticsearch** (`/etc/elasticsearch/elasticsearch.yml`):

```
network.host: 127.0.0.1
```

**Memcached** (`/etc/memcached.conf`):

```
-l 127.0.0.1
```

**Docker deserves special attention:** publishing a port with `-p 5432:5432` exposes it to the internet **bypassing ufw/iptables rules on the VPS**, Docker manages its own firewall chains. Publish to localhost when only the VPS needs it:

```
docker run -p 127.0.0.1:5432:5432 ...
```

(and the same `127.0.0.1:` prefix in compose `ports:`). Our edge firewall still shields the [blocked-list ports](https://vpsdime.com/knowledgebase/client-area/services/firewall) even when Docker sidesteps your local rules, one more reason to keep it on.

## When something must be reachable from outside

Don't fall back to `0.0.0.0` and hope. In order of preference:

1. **SSH tunnel**, zero exposure, great for admin access: `ssh -L 3306:127.0.0.1:3306 root@YOUR.VPS.IP` on your machine, then connect your client to `localhost:3306`. The service stays bound to localhost.
2. **A private overlay** (WireGuard or Tailscale) between your machines, then bind services to the overlay interface, our recommendation for server-to-server traffic generally.
3. **IP allowlists.** For services on our [blocked list](https://vpsdime.com/knowledgebase/client-area/services/firewall), use its **Per-Application Whitelist**. For MySQL and anything custom, allowlist in your own firewall (`ufw allow from YOUR.OFFICE.IP to any port 3306`), never a blanket `ufw allow 3306`.

## Layer 3: credentials still matter

A correct bind plus a default password is one config regression away from disaster:

- Strong, unique passwords for **every** service, including localhost-only ones, and no default accounts left enabled.
- Lock down secrets files: `find /var/www -name ".env"`, then `chmod 600` them, a readable `.env` through a misconfigured web server leaks every credential it holds.
- Admin panels that must face the internet get a strong password plus an IP allowlist. The broader checklist is in [Securing your VPS](https://vpsdime.com/knowledgebase/getting-started/securing-your-vps).

## Scanner quick wins (version disclosure and old TLS)

If a scan report is what brought you here, two findings appear on nearly every fresh web server and take a minute each:

- **Hide version info.** nginx: `server_tokens off;` in the `http` block. Apache: `ServerTokens Prod` and `ServerSignature Off` in `conf-available/security.conf`, plus `Options -Indexes` on your web root to kill directory listings.
- **Modern TLS only.** nginx: `ssl_protocols TLSv1.2 TLSv1.3;`. Apache: `SSLProtocol all -SSLv3 -TLSv1 -TLSv1.1`. That retires the POODLE/FREAK-era findings in one line. (Certificates themselves: [free with Let's Encrypt](https://vpsdime.com/knowledgebase/getting-started/hosting-a-website).)

Restart the web server after either change.

## Verify, and re-verify

Finish the way you started: `ss -tulnp` on the VPS, `nmap -Pn YOUR.VPS.IP` from outside. Everything still listed public should be something you deliberately serve to the world, with authentication in front of it. Re-run the pair after big installs, new software loves binding to `0.0.0.0` by default.

## Still need help?

You can [open a support ticket](https://vpsdime.com/open-ticket/). So we can help on the first reply, it's worth mentioning:

- the VPS hostname or IP,
- the service you're locking down and its `ss -tulnp` line,
- the scanner finding you're chasing, if there is one.

## Related questions

- "A vulnerability scanner flagged services on my VPS, how do I fix them?"
- "How do I stop MySQL/PostgreSQL/Redis accepting connections from any IP?"
- "Is my database port open to the internet?"
- "Why is my Docker container's port public even though I have ufw?"
- "How do I access my database remotely without exposing it?"
- "How do I hide my web server version and disable old TLS?"
