# "localhost" Means Two Different Things, and Docker Compose Doesn't Warn You

Containerized the CVE ingestion service today. Ran perfectly on the VPS directly, the way it had for weeks. Put it in Docker, ran it again, and it broke immediately.

`psycopg2.OperationalError: connection to server at "localhost" (127.0.0.1), port 5432 failed: Connection refused`

Same code. Same database. Same `.env`. The only thing that changed was *where* the code was running, which turned out to be exactly the problem.

## Why "localhost" lied to me

Running the script directly on the VPS, `localhost` means the VPS. Postgres is bound to `127.0.0.1:5432` on that machine, the script runs on that same machine, and `localhost` correctly resolves to "right here."

Put the script inside a container, and "right here" changes. A container has its own network namespace - its own private idea of what `localhost` means, separate from the host it's running on. Inside the container, `localhost` means *the container*, not the VPS. Postgres isn't listening on port 5432 inside that container's namespace, because Postgres isn't in that container. It's in a different one, or on the host, depending on the setup - either way, not where the code was looking.

The connection refused wasn't a fluke or a firewall issue. It was the script doing exactly what it was told, asking the wrong place for something that was never going to be there.

## The fix

Docker Compose gives every service in a compose file a DNS name matching its service name. So instead of pointing at `localhost`, the connection string points at the service:

`DB_HOST=postgres`

Instead of

`DB_HOST=localhost`

One line. Compose's internal network resolves `postgres` to the right container, every time, regardless of which host IP or port mapping is in play. The fix is small. Understanding why `localhost` stopped working is the part that actually matters, because the same class of bug is waiting in every future service that gets containerized.

## Why this one's worth remembering by name

This isn't a Radar bug. It's a Docker Compose fact of life, and it'll be true again the next time any service moves from "running directly on a machine" to "running inside a container on that machine." The code didn't get worse when it moved into Docker. Its assumptions about where it lives just stopped being true, and nothing about the error message says that directly - it just says connection refused, and leaves you to work out why.
