The Discrepancy: App Works, pgAdmin Fails

You've set up a Dockerized PostgreSQL container. Next to it, your application, perhaps built with FastAPI, is running and communicating with the database flawlessly. It creates tables, writes data, and reads it back without a hitch. The connection string points to localhost:5432. Everything seems nominal.

Then you open pgAdmin, your preferred GUI tool, to inspect the data directly. You configure it to connect to localhost:5432. But instead of seeing the databases and tables your application created, you find only the default, empty PostgreSQL databases like postgres, template0, and template1. Your meticulously crafted fastapi-db database and its products table are nowhere to be found.

This is a common, and maddening, scenario: your application is talking to a database, and pgAdmin is talking to a database, but they are not talking to the *same* database. The assumption that localhost:5432 always refers to the same accessible instance is broken by Docker's networking layer.

Diagram showing two distinct network paths: App to Docker DB, pgAdmin to Host DB

Understanding Docker Networking for Databases

Docker containers operate within their own network namespaces. When you run a PostgreSQL container, it exposes its port (defaulting to 5432) within the Docker network. To access this port from your host machine, or from other containers, you need to explicitly map it.

The critical distinction lies in how your application and pgAdmin are connecting. Your application, likely running within another Docker container or configured to use Docker's network bridge, is able to resolve and connect to the PostgreSQL container's exposed port directly via the Docker network. This often happens automatically if your application container is on the same Docker network as the PostgreSQL container, or if you've explicitly used Docker Compose to link them.

pgAdmin, however, is typically running directly on your host machine, outside of the Docker network. When you tell pgAdmin to connect to localhost:5432, it attempts to connect to a PostgreSQL instance running directly on your host operating system, not within the Docker container. If you don't have a PostgreSQL server running natively on your host, or if it's configured on a different port or with different credentials, pgAdmin will find nothing.

The Solution: Port Mapping and Docker Compose

The solution involves ensuring that the PostgreSQL container's port is correctly mapped to your host machine's network interface, making it accessible to applications running outside the container, including pgAdmin.

If you are using Docker Compose, this is straightforward. You need to define the ports mapping in your docker-compose.yml file. The format is HOST_PORT:CONTAINER_PORT.

For example, to map the container's port 5432 to your host's port 5432, you would add the following to your PostgreSQL service definition:

services:
  db:
    image: postgres:latest
    environment:
      POSTGRES_DB: fastapi-db
      POSTGRES_USER: user
      POSTGRES_PASSWORD: password
    ports:
      - "5432:5432" # Map host's 5432 to container's 5432

With this mapping in place, when your application connects to localhost:5432, it will connect to the port exposed by the Docker container. Crucially, when pgAdmin connects to localhost:5432, it will now also be connecting to the *same* port exposed by the Docker container.

Verifying the Connection

After applying the port mapping, restart your Docker containers. You should then be able to connect pgAdmin to localhost:5432 using the same user credentials and database name that your application uses (e.g., fastapi-db, user, password). You should now see your products table and its contents.

If you are not using Docker Compose, you can achieve the same result using the docker run command with the -p flag:

docker run --name some-postgres -e POSTGRES_PASSWORD=mysecretpassword -p 5432:5432 -d postgres

This command starts a PostgreSQL container and maps port 5432 on your host machine to port 5432 inside the container. This ensures that any connection made to localhost:5432 on your host will be forwarded to the PostgreSQL service running within the Docker container.

Beyond localhost: Docker Networks

While mapping to localhost is common for direct access via GUI tools, it's also important to understand Docker's internal networking. If your application and database are both Docker containers, they can often communicate using the service names defined in your docker-compose.yml file, without needing to expose ports to the host at all. For instance, your application might connect to a database service named db using a connection string like postgresql://user:password@db:5432/fastapi-db.

This internal networking is generally more secure and efficient for container-to-container communication. However, for debugging and direct inspection with tools like pgAdmin, the localhost port mapping remains essential.

The core takeaway is that localhost on your host machine is distinct from localhost as seen by a process running inside a Docker container. Explicitly mapping ports bridges this gap, resolving the common confusion when your application works but your GUI tools do not.