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.13
    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.13
    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.

Frequently Asked Questions

Can pgAdmin manage multiple PostgreSQL servers?

Yes. pgAdmin supports connecting to unlimited PostgreSQL servers simultaneously. Add each server through the UI or pre-configure them using a servers.json file mounted into the container. You can organize servers into groups and switch between them in the tree view. This makes it ideal for managing databases across multiple self-hosted applications.

How does pgAdmin compare to Adminer?

pgAdmin is a full-featured tool specifically for PostgreSQL — it includes visual query plans, server monitoring dashboards, backup/restore wizards, and role management. Adminer is a lightweight, multi-database tool that supports PostgreSQL, MySQL, SQLite, and more from a single PHP file. Choose pgAdmin for deep PostgreSQL administration. Choose Adminer if you manage multiple database types and want a simple, unified interface.

Can I run pgAdmin alongside my existing PostgreSQL containers?

Yes. Add pgAdmin to the same Docker network as your PostgreSQL containers. Use the PostgreSQL container name as the hostname when adding a server in pgAdmin. No additional port exposure is needed — containers communicate directly over the shared Docker network.

Does pgAdmin support two-factor authentication?

Yes, pgAdmin 4 supports two-factor authentication using TOTP (Time-based One-Time Password) apps like Google Authenticator or Authy. Enable it in the pgAdmin settings after initial login. This is important if you expose pgAdmin through a reverse proxy.

How much disk space does pgAdmin use?

The Docker image is roughly 400 MB. pgAdmin stores session data, query history, and its internal SQLite database in the data volume, which rarely exceeds 100 MB for typical use. The main disk consideration is your PostgreSQL databases themselves, not pgAdmin.

Can I use pgAdmin to back up and restore databases?

Yes. pgAdmin includes graphical backup and restore wizards that wrap pg_dump and pg_restore. You can create full, schema-only, or data-only backups in custom, tar, or plain SQL formats. For automated backups, use a cron job with pg_dump directly — pgAdmin’s backup tool is best for ad-hoc operations.

Comments