9
0
0

Docker 部署 PostgreSQL:密码对了仍连不上

2026-10-29
2026-10-30
文章摘要
|

背景与目标

PostgreSQL 常被拿来和 MySQL 对比:功能更"正统",类型系统、事务、JSON 支持都更强。Windows 上它有官方安装包,比 SQL Server 轻,但也逃不掉服务注册、端口占用、卸载残留这些老问题,容器化依然是最省心的装法。

只是容器化之后,坑换了个位置:不是"装不上",而是"装上了却连不上、连上了却建不了表"。

这正是 PostgreSQL 与前四篇最大的不同。POSTGRES_USER / POSTGRES_PASSWORD / POSTGRES_DB 只在首次针对空数据目录初始化时生效,作用是把账号和库建出来;此后:

  • "这个 IP 能不能连"由容器里的 pg_hba.conf 决定;

  • "这个账号能不能建表"由 schema 的属主与授权决定。

这两件事都不在 compose 里。

读者将得到:一份可直接跑的 compose、容器内与宿主机两侧的连接验证,以及四个"密码明明对了却报错"的真实排查路径。

提示:宿主 Windows (Docker Desktop),镜像走华为云。版本钉在 postgres:17-alpine;PostgreSQL 18 起官方镜像的默认数据目录位置有变化,换版本时留意卷路径。

步骤

第 0 步:确认 Docker Desktop 已就绪

docker version
docker compose version

输出版本号即 OK。

第 1 步:准备目录

mkdir postgres && cd postgres

第 2 步:写 docker-compose.yaml

新建 docker-compose.yaml,整段复制:

services:
  postgres:
    image: swr.cn-north-4.myhuaweicloud.com/ddn-k8s/docker.io/postgres:17-alpine
    container_name: postgres
    environment:
      POSTGRES_USER: "appuser"
      POSTGRES_PASSWORD: "YourStrong!Passw0rd"
      POSTGRES_DB: "appdb"
      TZ: "Asia/Shanghai"
    ports:
      - "5432:5432"
    volumes:
      - ./pg-data:/var/lib/postgresql/data
    restart: unless-stopped

要点:

  • POSTGRES_USER:超级用户名。设了它,默认的 postgres 账号就不会被创建,见踩坑第 2 条。

  • POSTGRES_PASSWORD:必填,不设容器直接退出,见踩坑第 1 条。

  • POSTGRES_DB:首次初始化时创建的库,属主就是 POSTGRES_USER。

  • 以上三个变量只在空数据目录上生效一次;想跑初始化 SQL,把 .sql 放进 /docker-entrypoint-initdb.d。

  • 数据卷 ./pg-data 绑定挂载到 /var/lib/postgresql/data,数据落盘。

第 3 步:启动

docker compose up -d

首次启动要跑 initdb,需要一点时间。看日志:

docker compose logs -f postgres

看到 database system is ready to accept connections 即就绪。

第 4 步:连接验证

先在容器内连(本地回环默认放行,不用输密码):

docker compose exec postgres psql -U appuser -d appdb -c "SELECT version();"

再从宿主机连(走发布端口,要密码;没装 psql 可用 pgAdmin / DBeaver 代替):

psql -h localhost -p 5432 -U appuser -d appdb

能进到 appdb=# 提示符即成功。做一轮读写验证:

CREATE TABLE demo (id serial PRIMARY KEY, name text);
INSERT INTO demo (name) VALUES ('hello');
SELECT * FROM demo;

看到插入的行,说明账号、库、建表权限都正常。退出用 \q。

实操截图

容器运行

ScreenShot_2026-10-29_095104_317.png

工具连接

ScreenShot_2026-10-29_095205_712.png
ScreenShot_2026-10-29_095221_478.png

踩坑与解决

1. 容器秒退:没设 POSTGRES_PASSWORD

  • 问题:docker compose up -d 后容器立刻退出。

  • 复现:docker compose logs postgres 看到 Error: Database is uninitialized and superuser password is not specified.

  • 根因:镜像拒绝以"无密码超管"启动。这与 MySQL 允许空密码不同,是 PostgreSQL 镜像的硬性要求。

  • 解决:在 environment 里设 POSTGRES_PASSWORD,值要够强。仅本机临时用可设 POSTGRES_HOST_AUTH_METHOD: "trust" 跳过密码,但任何可被访问的环境都不要这么干。

  • 验证:重新 up 后日志出现 database system is ready to accept connections。

2. psql -U postgres 报 role "postgres" does not exist

  • 问题:照抄网上的命令用 postgres 账号连,提示角色不存在。

  • 复现:docker compose exec postgres psql -U postgres 返回 FATAL: role "postgres" does not exist。

  • 根因:设了 POSTGRES_USER 之后,镜像用它作为唯一超级用户,不会再额外创建 postgres 账号;而且账号只在首次初始化时创建,容器跑起来后改 yaml 不会补建。

  • 解决:用 yaml 里的用户名连(本文是 appuser)。确实需要 postgres 这个名字,就改 yaml 后 docker compose down -v 删卷重建——数据目录一旦生成,初始化就不会再执行。

  • 验证:psql -U appuser 能进,\du 里能看到 appuser 是 Superuser。

3. 宿主机连不上:no pg_hba.conf entry for host "172.x.x.x"

  • 问题:容器起来了、密码也对,从宿主机或其他机器连却被拒绝。

  • 复现:报错形如 FATAL: no pg_hba.conf entry for host "172.17.0.1", user "appuser", database "appdb", no encryption。

  • 根因:pg_hba.conf 是一张"来源地址 + 认证方式"的白名单,逐条匹配。默认镜像只放行回环与本机容器网络;一旦你挂载了自定义配置、改了 POSTGRES_HOST_AUTH_METHOD,或从别的网段连接,就会撞上这张白名单。这就是为什么密码对了仍然连不上。

  • 解决:先看容器里实际的规则:

    docker compose exec postgres cat /var/lib/postgresql/data/pg_hba.conf

    在最前面加一行(生产环境把 0.0.0.0/0 收窄成实际网段):

    host    all    all    0.0.0.0/0    scram-sha-256

    然后重载配置,不用重启容器:

    docker compose exec postgres psql -U appuser -d appdb -c "SELECT pg_reload_conf();"
  • 验证:宿主机 psql -h localhost -U appuser 能连上。

4. 建表报 permission denied for schema public

  • 问题:连接成功了,CREATE TABLE 却报权限不足。

  • 复现:普通业务账号连上后执行建表,返回 ERROR: permission denied for schema public。

  • 根因:PostgreSQL 15 起收紧了 public schema 的默认权限——不再允许所有人建对象,只有库属主或被显式授权的角色可以。典型场景是用 postgres 超管建库但没把 owner 交给业务账号。

  • 解决:把库交给业务账号,或单独授权(二选一,然后重连生效):

    ALTER DATABASE appdb OWNER TO appuser;
    GRANT ALL ON SCHEMA public TO appuser;
  • 验证:appuser 再次执行 CREATE TABLE 成功。

5. 建表成功,查询却报"表不存在"

  • 问题:CREATE TABLE User (...) 成功了,SELECT * FROM User 也像能用,但换成别的客户端或带引号就找不到表。

  • 复现:CREATE TABLE User 后查 \dt,表名显示为 user;用 SELECT * FROM "User" 则报 relation "User" does not exist。

  • 根因:PostgreSQL 对不加引号的标识符一律折叠成小写,User 实际存成 user;加了双引号的 "User" 是另一个大小写敏感的名字。MySQL 的习惯直接搬过来必踩。

  • 解决:统一小写 snake_case(app_user、order_item),不滥用双引号;历史表名要用双引号精确引用或 ALTER TABLE ... RENAME。

  • 验证:\dt 列出的表名与代码里的查询完全一致。

通用坑(与 SQL Server / MySQL / Redis / MongoDB 篇相同,不再重复展开)

  • 端口 5432 被占:本机装过 PostgreSQL,或别的服务占了 5432。报 port is already allocated,改外部端口 5433:5432,连接时用 localhost:5433。

  • 数据丢了:没挂数据卷,容器销毁即丢失。本教程已挂 ./pg-data:/var/lib/postgresql/data(17 及更早版本)。

  • 容器秒退:先看 docker compose logs postgres,再对照上面第 1 条。

小结与参考

  • 用 Docker 部署 PostgreSQL 17,本机只留一份 docker-compose.yaml。

  • 三个变量只在空数据目录首次初始化时生效:POSTGRES_USER(同时顶替 postgres 超管)、POSTGRES_PASSWORD(必填)、POSTGRES_DB。

  • 本篇核心矛盾:密码对了仍可能连不上——连接由 pg_hba.conf 白名单决定,建表由 public schema 的属主决定,两者都在库内,compose 管不到。

  • 改账号、改密码、补初始化脚本,都要 docker compose down -v 删卷重来。

  • 标识符不加引号会被折叠成小写,统一 snake_case 最省事。

参考:

支持与分享

如果这篇文章对你有帮助,欢迎分享给更多人或者给予支持!

Docker 部署 PostgreSQL:密码对了仍连不上
/archives/docker-bu-shu-postgresql-mi-ma-dui-liao-reng-lian-bu-shang
作者
隔壁老陈
发布于
2026-10-29
许可协议
CC BY-NC-SA 4.0

评论