Self-Hosting pgAdmin 4 with Docker Compose

What Is pgAdmin?

pgAdmin 4 is the leading open-source management tool for PostgreSQL. It provides a web-based GUI for managing databases, running SQL queries, monitoring server activity, and configuring replication — replacing the need for command-line-only database administration. If you self-host any app that uses PostgreSQL (Nextcloud, Immich, Authentik, dozens more), pgAdmin gives you visibility into what’s happening inside those databases.

pgAdmin official site

Prerequisites

  • A Linux server (Ubuntu 22.04+ recommended)
  • Docker and Docker Compose installed (guide)
  • 512 MB of free RAM (1 GB recommended)
  • At least one PostgreSQL instance to manage (can be on the same server or remote)

Docker Compose Configuration

Create a directory for pgAdmin and add a docker-compose.yml:

mkdir -p ~/pgadmin && cd ~/pgadmin
services:
  pgadmin:
    image: dpage/pgadmin4:9.12.0
    container_name: pgadmin
    environment:
      # CHANGE THESE — used for initial admin login
      PGADMIN_DEFAULT_EMAIL: [email protected]
      PGADMIN_DEFAULT_PASSWORD: ChangeThisPassword123!
      # Disable built-in Postfix mail server (not needed for most setups)
      PGADMIN_DISABLE_POSTFIX: "true"
    ports:
      - "5050:80"
    volumes:
      - pgadmin-data:/var/lib/pgadmin
    restart: unless-stopped
    networks:
      - pgadmin-net

volumes:
  pgadmin-data:

networks:
  pgadmin-net:

Start the stack:

docker compose up -d

pgAdmin will be available at http://your-server:5050. Log in with the email and password you set in the environment variables.

Connecting pgAdmin to Your PostgreSQL Databases

Once logged in, click Add New Server in the dashboard. For a PostgreSQL instance running on the same Docker network:

FieldValue
NameAny label (e.g., “Nextcloud DB”)
HostContainer name or IP of your PostgreSQL container
Port5432 (default)
UsernameYour PostgreSQL user
PasswordYour PostgreSQL password

If your PostgreSQL is in a different Docker Compose stack, create an external network both stacks share, or use the host IP address.

Pre-Loading Server Connections

To skip manual setup, create a servers.json file:

{
  "Servers": {
    "1": {
      "Name": "Local PostgreSQL",
      "Group": "Self-Hosted",
      "Host": "postgres",
      "Port": 5432,
      "Username": "postgres",
      "SSLMode": "prefer",
      "MaintenanceDB": "postgres"
    }
  }
}

Mount it in your Compose file by adding to the volumes section:

    volumes:
      - pgadmin-data:/var/lib/pgadmin
      - ./servers.json:/pgadmin4/servers.json:ro

Restart pgAdmin and the server appears automatically — you still need to enter the password on first connection.

Running pgAdmin Alongside PostgreSQL

Most self-hosters already run PostgreSQL for other apps. Here’s a complete stack with both pgAdmin and a fresh PostgreSQL instance:

services:
  postgres:
    image: postgres:17.3
    container_name: postgres
    environment:
      POSTGRES_USER: admin
      POSTGRES_PASSWORD: StrongDatabasePassword!
      POSTGRES_DB: default
    volumes:
      - postgres-data:/var/lib/postgresql/data
    restart: unless-stopped
    networks:
      - db-net

  pgadmin:
    image: dpage/pgadmin4:9.12.0
    container_name: pgadmin
    environment:
      PGADMIN_DEFAULT_EMAIL: [email protected]
      PGADMIN_DEFAULT_PASSWORD: ChangeThisPassword123!
      PGADMIN_DISABLE_POSTFIX: "true"
    ports:
      - "5050:80"
    volumes:
      - pgadmin-data:/var/lib/pgadmin
    restart: unless-stopped
    depends_on:
      - postgres
    networks:
      - db-net

volumes:
  postgres-data:
  pgadmin-data:

networks:
  db-net:

In this setup, pgAdmin connects to PostgreSQL using the hostname postgres (the container name) on port 5432.

Configuration

Key Environment Variables

VariableDescriptionDefault
PGADMIN_DEFAULT_EMAILAdmin login email (required)
PGADMIN_DEFAULT_PASSWORDAdmin login password (required)
PGADMIN_DISABLE_POSTFIXDisable built-in mail serverfalse
PGADMIN_LISTEN_PORTPort inside container80
PGADMIN_ENABLE_TLSEnable HTTPS inside containerfalse
GUNICORN_THREADSWorker threads for performance25

File Permissions

pgAdmin runs as user pgadmin (UID 5050, GID 5050) inside the container. If you use bind mounts instead of named volumes, set ownership:

sudo chown -R 5050:5050 /path/to/pgadmin-data

Reverse Proxy

Behind Nginx Proxy Manager, Traefik, or Caddy, point your domain to port 5050. No special headers are required — pgAdmin works behind reverse proxies without additional configuration.

For HTTPS termination at the proxy level, keep PGADMIN_ENABLE_TLS disabled (the default) and let your reverse proxy handle SSL.

See Reverse Proxy Setup for full configuration.

Backup

The pgAdmin data volume (/var/lib/pgadmin) stores user sessions, saved queries, server definitions, and preferences. Back it up with your standard volume backup approach:

docker run --rm -v pgadmin_pgadmin-data:/data -v $(pwd):/backup alpine tar czf /backup/pgadmin-backup.tar.gz /data

For backing up the actual PostgreSQL databases managed through pgAdmin, use pg_dump — pgAdmin’s GUI provides a point-and-click backup tool under Tools > Backup for each database.

See Backup Strategy for a comprehensive approach.

Troubleshooting

Permission Denied on Volume Mount

Symptom: pgAdmin fails to start with permission errors on /var/lib/pgadmin.

Fix: The container runs as UID 5050. Set ownership on host-mounted directories:

sudo chown -R 5050:5050 /path/to/pgadmin-data

Cannot Connect to PostgreSQL Container

Symptom: “Connection refused” when adding a server.

Fix: Ensure both containers are on the same Docker network. Use the container name (not localhost) as the host. Check that PostgreSQL is accepting connections (pg_hba.conf allows the pgAdmin container’s subnet).

Slow Initial Load

Symptom: First page load takes 10-15 seconds.

Fix: This is normal — pgAdmin initializes its internal SQLite database on first access. Subsequent loads are faster. Increase GUNICORN_THREADS to 50 if managing many servers.

Resource Requirements

  • RAM: ~150 MB idle, 300-500 MB under active use with multiple queries
  • CPU: Low (spikes during large query execution)
  • Disk: ~100 MB for the application, plus space for query history and session data

Verdict

pgAdmin is the standard PostgreSQL GUI — mature, full-featured, and actively maintained. If you run any self-hosted apps backed by PostgreSQL, pgAdmin belongs in your stack. The web UI makes database inspection, query writing, and backup management accessible without memorizing psql commands.

For MySQL/MariaDB databases, look at phpMyAdmin or Adminer instead. If you want a multi-database GUI that handles PostgreSQL, MySQL, SQLite, and more, consider CloudBeaver — though it lacks pgAdmin’s depth for PostgreSQL-specific features.