Docker 部署 PostgreSQL:密码对了仍连不上
背景与目标
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。
实操截图
容器运行

工具连接


踩坑与解决
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 起收紧了
publicschema 的默认权限——不再允许所有人建对象,只有库属主或被显式授权的角色可以。典型场景是用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白名单决定,建表由publicschema 的属主决定,两者都在库内,compose 管不到。改账号、改密码、补初始化脚本,都要
docker compose down -v删卷重来。标识符不加引号会被折叠成小写,统一 snake_case 最省事。
参考:
镜像:
swr.cn-north-4.myhuaweicloud.com/ddn-k8s/docker.io/postgres:17-alpinePostgreSQL 官方文档:https://www.postgresql.org/docs/

