Docker Compose 部署 PostgreSQL + pgAdmin(含持久化和健康检查)

开发期想要一个随手起、不污染主机、带管理界面的 PostgreSQL。
Compose 是最低摩擦方案。

目录结构

~/pg-stack/
├── docker-compose.yml
├── .env
├── init/
│   └── 01-init.sql
└── data/        # 卷挂载点(git 忽略)

docker-compose.yml

services:
  db:
    image: postgres:16-alpine
    restart: unless-stopped
    environment:
      POSTGRES_USER: ${PG_USER}
      POSTGRES_PASSWORD: ${PG_PASS}
      POSTGRES_DB: ${PG_DB}
      # 让 init/*.sql 在首次启动时跑
      PGDATA: /var/lib/postgresql/data/pgdata
    volumes:
      - ./data:/var/lib/postgresql/data
      - ./init:/docker-entrypoint-initdb.d:ro
    ports:
      - "127.0.0.1:5432:5432"   # 只在 localhost 暴露
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U $$POSTGRES_USER -d $$POSTGRES_DB"]
      interval: 10s
      timeout: 3s
      retries: 5

  pgadmin:
    image: dpage/pgadmin4:latest
    restart: unless-stopped
    environment:
      PGADMIN_DEFAULT_EMAIL: ${PGADMIN_EMAIL}
      PGADMIN_DEFAULT_PASSWORD: ${PGADMIN_PASS}
      PGADMIN_LISTEN_PORT: 80
    ports:
      - "127.0.0.1:5050:80"
    depends_on:
      db:
        condition: service_healthy
    volumes:
      - pgadmin-data:/var/lib/pgadmin

volumes:
  pgadmin-data:

.env(不要进 git)

PG_USER=appuser
PG_PASS=change-me-some-long-random-string
PG_DB=appdb
PGADMIN_EMAIL=admin@local
PGADMIN_PASS=change-me-too

init/01-init.sql

-- 启用常用扩展
CREATE EXTENSION IF NOT EXISTS pgcrypto;
CREATE EXTENSION IF NOT EXISTS pg_stat_statements;

-- 只读分析账号
CREATE USER analytics WITH PASSWORD 'analytics-pass';
GRANT CONNECT ON DATABASE appdb TO analytics;
GRANT USAGE ON SCHEMA public TO analytics;
ALTER DEFAULT PRIVILEGES IN SCHEMA public
  GRANT SELECT ON TABLES TO analytics;

启动

docker compose up -d
docker compose ps
docker compose logs -f db   # 看初始化

校验

# 通过 psql 客户端连
docker compose exec db psql -U appuser -d appdb -c '\dx'

# 或者本地 psql
psql -h 127.0.0.1 -U appuser -d appdb

pgAdmin 打开 http://localhost:5050,登录用 .env 里的邮箱密码。
进去后 "Add new server":
- Host: db(容器名,pgAdmin 在同一 Compose 网络)
- Port: 5432
- User/Password: 同 .env

备份 / 恢复

# 备份
docker compose exec -T db pg_dump -U appuser -d appdb -Fc > backup-$(date +%F).dump

# 恢复(连接已存在的数据库会失败,先 drop)
docker compose exec -T db psql -U postgres -c 'DROP DATABASE appdb;'
docker compose exec -T db psql -U postgres -c 'CREATE DATABASE appdb OWNER appuser;'
docker compose exec -T db pg_restore -U appuser -d appdb < backup-2026-05-20.dump

升级 Postgres 大版本

主版本(如 16 → 17)跨越时 数据文件不兼容,需要 dump/restore:

docker compose exec -T db pg_dumpall -U postgres > all.sql
# 改 image: postgres:17-alpine
# 清空 ./data 重启
docker compose down
rm -rf ./data
docker compose up -d
docker compose exec -T db psql -U postgres < all.sql

踩过的坑

  • 端口写成 5432:5432 而不是 127.0.0.1:5432:5432:整个公网都能连到
    你的数据库,扫描器十分钟内就来撞密码。一定要绑 127.0.0.1。
  • ./data 不要用 NFS 挂载点,PostgreSQL 对 fsync 行为要求严格,
    NFS 上跑会丢数据。
  • init/ 只在 数据目录为空 时执行;如果你后来改了 init.sql 想重跑,
    得先 docker compose down -v 清掉。
  • Alpine 镜像比 Debian 小但 glibc 差异偶尔翻车(特别是某些 extension),
    生产用 postgres:16-bookworm 更稳。
精确评价 共 0 人评价
可复现性
可复现 · 0 不可复现 · 0
文风
文风流畅 · 0 文风晦涩 · 0
立场
支持 · 0 反对 · 0

登录后即可对本帖作出评价。

评论区 0 条 · 所有人可在此交流

登录后参与评论。

还没有评论,来说两句。