FeatherDB

Connect to a containerized database

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 runsThe pathFeatherDB connection
Your own VPS (Coolify, plain Docker)loopback port + SSHSSH tunnel
Your own VPS, nothing publishedthe container, found by name + SSHSSH tunnel → A container on the server
A PaaS with a CLI (Fly, Railway, GCP, AWS)the provider's tunnel CLIDirect → 127.0.0.1, TLS disable
Kuberneteskubectl port-forwardDirect → 127.0.0.1, TLS disable
A managed database productthe provider's endpointDirect over TLS

Your own VPS (Coolify, Docker Compose)

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 published port at all

You can also skip the port mapping entirely. The server itself can reach every container on its Docker networks, so an SSH tunnel can carry you straight to the container itself. In FeatherDB, add an SSH tunnel connection, and on the database step choose A container on the server: type the Compose service name (usually db), click Find it on the server and pick your stack. FeatherDB looks up the container's current address every time you connect, so a redeploy that moves it changes nothing for you. To do that lookup it runs docker ps and docker inspect on the server over your SSH login — nothing else, and only for connections where you chose it; the SSH user must be allowed to use Docker (root, or a member of the docker group).

Older versions, or if you'd rather type the address yourself, find it on the server:

docker ps                      # the database container's name
docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}} {{end}}' <name>

Then use that address (for example 10.0.5.4) and port 5432 as the database host. The trade-off: Docker hands out that address when the container starts, so a redeploy can move it — if the connection stops answering after a deploy, look it up again, or switch the connection to A container on the server.

A platform you don't manage (Fly, Railway, GCP, AWS, …)

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.

Kubernetes

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.

Long-lived setups: an overlay network

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.

Managed databases

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.

If you truly must expose a port

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.

← featherdb.dev