Docker Compose: PostgreSQL + pgAdmin
PostgreSQL with pgAdmin in Docker Compose: connect pgAdmin to the database by service name, bind the panel to localhost, .env.example and why exposing pgAdmin to the Internet is a bad idea.
pgAdmin gives you a web interface to view and edit your PostgreSQL database by clicking, instead of through psql. It is very handy in development. This guide sets up both services together and focuses on what confuses people most the first time: how to connect pgAdmin to Postgres and why the panel should not be exposed.
compose.yaml for Postgres + pgAdmin
Notice one detail: the database does not publish a port. Only pgAdmin publishes its own, and bound to loopback.
services:
postgres:
image: postgres:16-alpine
restart: unless-stopped
environment:
POSTGRES_DB: ${POSTGRES_DB}
POSTGRES_USER: ${POSTGRES_USER}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U $$POSTGRES_USER -d $$POSTGRES_DB"]
interval: 10s
timeout: 5s
retries: 5
start_period: 20s
pgadmin:
image: dpage/pgadmin4:8.12
restart: unless-stopped
environment:
PGADMIN_DEFAULT_EMAIL: ${PGADMIN_DEFAULT_EMAIL}
PGADMIN_DEFAULT_PASSWORD: ${PGADMIN_DEFAULT_PASSWORD}
ports:
- "127.0.0.1:5050:80"
depends_on:
postgres:
condition: service_healthy
volumes:
- pgadmin_data:/var/lib/pgadmin
volumes:
postgres_data:
pgadmin_data:The .env.example:
POSTGRES_DB=app POSTGRES_USER=app POSTGRES_PASSWORD=CHANGE_ME PGADMIN_DEFAULT_EMAIL=admin@example.com PGADMIN_DEFAULT_PASSWORD=CHANGE_ME
Bring it up and open http://localhost:5050. Log in with the email and password from PGADMIN_DEFAULT_*.
Connect pgAdmin to Postgres: use the service name
This is mistake number one. Inside pgAdmin, when registering a new server, many people type localhost in Host name/address and get a could not connect to server. The reason: inside the pgAdmin container, localhost is pgAdmin itself, not Postgres.
The correct connection details are:
- Host name/address:
postgres(the service name) - Port:
5432 - Username / Password: those of
POSTGRES_USERandPOSTGRES_PASSWORD
Two different passwords
They are easy to mix up. The pgAdmin login (PGADMIN_DEFAULT_EMAIL / PGADMIN_DEFAULT_PASSWORD) is for entering the web interface. The database credentials (POSTGRES_USER / POSTGRES_PASSWORD) are what you enter when registering the server inside pgAdmin. They are not the same thing.
Why not to expose the panel
pgAdmin is an administration tool: whoever gets in can read, modify or delete all your data. That is why the port here is bound to 127.0.0.1: you only reach it from your machine. Publishing it to the Internet with 5050:80 and a weak password is an open door to your database.
In production, the sensible choice is not to deploy pgAdmin publicly: keep it behind a VPN or a reverse proxy with TLS and authentication, or do not deploy it and connect with psql when needed.
Make pgAdmin remember your servers
The pgadmin_data:/var/lib/pgadmin volume stores the panel's configuration (registered servers, preferences). Without it, every down forces you to register the server again. With it, pgAdmin remembers your connection between restarts.
Next steps
Generate this stack in the Docker Compose generator. If you just want the database without a panel, go back to the PostgreSQL guide.
Frequently asked questions
- In pgAdmin, what do I put as Host name/address when registering the server?
- Put postgres, the compose service name, with port 5432. Do not put localhost: inside the pgAdmin container, localhost is pgAdmin itself, not the database. Both containers talk to each other by name on the Compose internal network. The username and password are those of POSTGRES_USER and POSTGRES_PASSWORD.
- Why does the database not publish port 5432?
- Because it does not need to: pgAdmin reaches Postgres over the internal network. Publishing 5432 would only open the database to your network with no benefit here. The only published port is pgAdmin's, bound to 127.0.0.1 so the panel is reachable from your browser but not from outside your machine.
- There are two passwords, which is which?
- Yes, do not mix them up. PGADMIN_DEFAULT_EMAIL and PGADMIN_DEFAULT_PASSWORD are the pgAdmin panel login (to enter the web interface). POSTGRES_USER and POSTGRES_PASSWORD are the database credentials, which you enter when you register the server inside pgAdmin. They are different things.
- Is it safe to leave pgAdmin accessible?
- Locally, bound to 127.0.0.1, yes with care. The dangerous part is publishing it to the Internet: pgAdmin grants full access to your database, so an exposed panel with a weak password is an open door to all your data. In production, keep it behind a VPN or a reverse proxy with TLS and authentication, or simply do not deploy it and use psql when needed.
- pgAdmin does not remember my servers between restarts, why?
- Because the pgAdmin volume is missing. The panel's configuration (registered servers, preferences) lives in /var/lib/pgadmin. With the pgadmin_data volume that configuration persists; without it, every docker compose down forces you to register the server again.