A Postgres inside a Docker container usually has no public port — good. This page is how to reach it anyway, without changing that.
The short version: never expose 5432 to the internet. Pick the row
that matches where the container runs; every path below works with FeatherDB as shipped.
| Where the container runs | The path | FeatherDB connection |
|---|---|---|
| Your own VPS (Coolify, plain Docker) | loopback port + SSH | SSH tunnel |
| A PaaS with a CLI (Fly, Railway, GCP, AWS) | the provider's tunnel CLI | Direct → 127.0.0.1, TLS disable |
| Kubernetes | kubectl port-forward | Direct → 127.0.0.1, TLS disable |
| A managed database product | the provider's endpoint | Direct over TLS |
The container network is private and should stay that way. Publish the database port on the server's loopback only — it becomes reachable from the server itself and from nowhere else — then let SSH carry you there. In the service's compose file:
services:
postgres:
ports:
- "127.0.0.1:5433:5432" # loopback only — invisible to the internet
Redeploy, then in FeatherDB add an SSH tunnel connection: your server as the SSH
host, and 127.0.0.1 port 5433 as the database host as seen from
the server. Nothing new is exposed; the SSH key is the only door.
In Coolify specifically, avoid the database resource's “make it publicly available” switch — it binds the port on every interface, which is exactly the thing this page exists to avoid.
No SSH access to the host — but every serious platform ships a tunnel in its CLI. It opens a port on your Mac and carries the traffic inside your authenticated platform session:
fly proxy 5433:5432 -a my-db # Fly.io
railway connect postgres # Railway
cloud-sql-proxy INSTANCE --port 5433 # Google Cloud SQL
aws ssm start-session … # AWS, via SSM port forwarding
Then add a Direct connection to host 127.0.0.1, the forwarded port,
and set TLS to disable (local proxy/tunnel only). That setting exists
for exactly this shape: the tunnel itself carries the encryption, the local leg is
plaintext, and a database that has no TLS configured would refuse an encrypted handshake
outright.
kubectl port-forward svc/postgres 5433:5432
Same as above: Direct → 127.0.0.1:5433, TLS disable. The
forward lives as long as the kubectl process does.
If you connect to the same stack every day, a mesh VPN (Tailscale, WireGuard) is the low-friction path: run its agent as a sidecar next to the database and the container gets a private IP only your machines can reach. Connect Direct to that IP — no public port, no CLI to keep running.
Supabase, Neon, RDS and friends hand you a public endpoint on purpose — TLS
required, strong auth, a pooler in front. That is not the risky kind of exposure; it is the
product. Use a Direct connection with TLS require, or
verify-full when the provider gives you a CA to pin.
No CLI tunnel, no sidecar, no managed endpoint — rare, but it happens. Then do it the
defensible way: TLS on the server (verify-full from FeatherDB),
scram-sha-256 auth, and a firewall allowlist holding exactly your IP. A bare
containerized Postgres published on 0.0.0.0:5432 with a password as its only
layer is the one configuration this page is written against.