news 2026/10/20 16:49:36

PostgreSQL自动备份密码配置指南:.pgpass与pg_hba.conf实战解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
PostgreSQL自动备份密码配置指南:.pgpass与pg_hba.conf实战解析

PostgreSQL的自动备份,说白了就是个“到点干活”的定时任务,但几乎所有第一次搭这套家伙的人,都会在“密码验证”这一关被卡一下。手动执行 pg_dump 的时候敲个密码很正常,一旦丢给 crontab 跑,脚本里没密码不行,把密码明文写进去又怕泄露,用 .pgpass 又经常会遇到权限不对、格式不对、认证方式不匹配之类的怪问题。再加上官方文档对这一块的描述比较零散,翻半天可能只看懂了“请参阅 pg_hba.conf”几个字,然后继续原地转圈。

这篇文章就专门围绕“PostgreSql自动备份密码设置问题”来写,把官方给出的密码认证机制、非交互连接方式、.pgpass 文件细则,以及实际备份脚本里怎么用密码,全部串起来讲一遍。文中会给出可以直接照抄的完整步骤和踩坑记录,适合刚接手数据库运维的新人,也适合想把手动备份正式切换到自动化、但一直没理顺密码配置细节的开发者。

1. 备份方案的核心逻辑与选型思路

1.1 为什么自动备份绕不开“密码”这个坎

做备份的人一般会先想“我要用什么方式备份”,但真正落地的时候,往往先死在“备份脚本怎么通过数据库认证”这一步。原因很简单:数据库默认不会允许你随便连,尤其是从另一台机器或通过 TCP 远程调用的时候,服务端要根据你的用户名、客户端地址、目标数据库,决定要不要让你进来,以及用什么方式验证你的身份。

这套验证逻辑全部写在 pg_hba.conf 里,官方说明把它定义得非常明确:条目从上到下逐个匹配,一旦匹配成功就按那条规则里的认证方法执行,不再往后看。这意味着,哪怕你的密码完全正确,只要命中的是一条trust规则,它连密码都不会问;反过来,如果命中的是一条scram-sha-256规则,而你又没提供密码,那服务端直接回你一句“no password supplied”。

所谓自动备份,本质是要在无人值守的情况下,让备份进程自己取得一个合法的“通行证”。通行证不能靠人工在键盘上输,只能写进某个能被脚本读取的地方。密码放在哪、用什么样的格式、权限怎么设,就变成了最现实的工程问题。官方手册在这个问题上给了两种很直接的答案:环境变量PGPASSWORD和密码文件.pgpass。但很多人把两种方式混在一起用,或者没理解两者与 pg_hba.conf 的关系,最后折腾半天,报错却始终是同一个。

1.2 备份工具选型与适用场景

既然是讲 PostgreSQL 自动备份,工具上绕不开三兄弟:pg_dump、pg_dumpall、pg_basebackup。它们解决的问题不一样,选错了场景,后面密码配置再完美也白搭。

  • pg_dump:逻辑备份,导出一个库的结构和数据,支持-Fc自定义压缩格式、-j并行导出,恢复灵活,单库全量、增量、定期导出选它最合适。
  • pg_dumpall:一次性导出整个实例的全局对象(角色、表空间)和所有数据库。如果要完整迁移一个实例,它是必要的补充,因为pg_dump不包含角色定义。
  • pg_basebackup:物理备份,备份整个数据目录,适合做恢复时间目标极短的主从重建或灾备基线。它要求用户具有REPLICATION权限,通常也得配一个专门的备份用户。

普通业务场景里,最常见的是用pg_dump+ crontab 做每日逻辑备份。本文后面的实操也是基于这个组合,因为它在密码配置这个问题上最有代表性,也最容易踩坑。物理备份的用户权限和 pg_hba.conf 规则会稍微特殊一点,但密码文件机制本身是共通的,看完这篇文章,切换到pg_basebackup也只是改一下命令和权限的问题。

1.3 方案选型背后的安全权衡

自动备份的密码配置,本质上是在做一道选择题:便利性 vs 安全性。

你想让脚本免密执行,最简单的方式是把密码直接写进脚本:

PGPASSWORD='你自己的密码' pg_dump -h 127.0.0.1 -U backup_user mydb > mydb.sql

这招对一次性脚本没问题,但长期放在服务器上风险不小:任何能读到脚本内容的用户、任何能被ps看到的进程参数,都可能把数据库密码泄露出去。这也是为什么官方说明虽然给出了 PGPASSWORD 这个环境变量,但并不建议在共享环境里这么玩。

官方的推荐路径是用.pgpass。这个文件同样要写明文密码,但它有两点好处:一是密码只写在一个独立文件里,不随脚本内容一起散落到日志、版本库或者聊天记录;二是它强约束了文件权限必须为0600,只有文件属主能读,目录层面可以锁得更死。另一个好处是,.pgpass不仅服务于备份脚本,psql、pg_dump、pg_basebackup等所有 libpq 系的工具都会自动读取它,日常手动连库也能省掉一遍遍输密码的麻烦。

所以我个人在服务器上做备份,永远优先.pgpass,只有在一次性容器环境里才用环境变量。这个选择不是因为我追求“复杂”,而是因为.pgpass把明文密码的暴露面收敛到了一个可控的文件上,后续配合权限管理系统也更容易交代。

2. 官方文档里关于“密码”的说明与约束

2.1 密码是“加密存储”而不是“明文存储”

如果去翻官方手册,会发现里面反复强调一个概念:数据库里角色的密码,是被转换成一个特定格式的哈希值存到系统表里的,而不是直接存明文字符串。这关系到你“设置密码”之后的存储形态,也决定了你在 .pgpass 里写的内容是否仍然有效。

PostgreSQL 10 以后默认的密码加密方式是scram-sha-256,早一点的版本可能还在用md5。密码设置语句很简单:

ALTER USER backup_user WITH PASSWORD '备份用户的密码';

这条命令执行后,服务端把密码按password_encryption参数指定的算法做哈希,存到pg_authid.rolpassword字段里。你无法从数据库里反查出明文是什么,即使看到那一长串哈希,也不能直接拿它当作登录密码来用。

这一点对备份脚本的意义在于:你在客户端配置的密码,必须与账户设置时的原始密码一致。如果你后来执行过ALTER USER ... PASSWORD改了密码,那 .pgpass 里的旧密码立刻失效,备份脚本就会从“正常执行”突然变成“认证失败”。这类问题很常见,而且系统日志里不会直接告诉你“你的密码被改过”,只会冷冰冰地说认证失败。

2.2 pg_hba.conf 决定“要不要密码”

密码设置好之后,真正决定备份脚本能不能连上的,是pg_hba.conf的规则顺序。官方说明里对这个文件的核心逻辑有三条,理解了这三条,你就理解了绝大多数密码认证问题:

  1. 按顺序从上到下匹配,第一条匹配生效。
  2. 匹配条件包括连接类型、数据库名、用户名、客户端地址。
  3. 匹配后按指定方法认证:trust(不验证)、md5(老式哈希)、scram-sha-256(推荐)、password(明文传输,不推荐)。

举个例子。文件里如果有这两条:

host all all 127.0.0.1/32 trust host all all 0.0.0.0/0 scram-sha-256

那么从本机连的时候,数据库完全不会要求密码,因为第一条直接匹配并放行了;从远程连的时候,因为第一条只匹配本机回环地址,所以会落到第二条,这时就必须提供密码。同一个账号,在不同来源地址下,行为完全不同。排查备份密码问题时,第一件事不是怀疑密码写错了,而是先确认 pg_hba.conf 里到底是哪条规则命中了你的备份请求。

改完 pg_hba.conf 之后,需要重载配置才能生效,执行:

su - postgres -c "psql -c 'SELECT pg_reload_conf();'"

还有一点要注意:pg_hba.conf 支持的是认证方法,它不负责“密码是什么”,所以即使把认证方式从scram-sha-256改成md5,你的 .pgpass 内容也不用变。反过来,如果客户端侧配置的密码本来就对,只是打不开认证方式,那就是另一回事了。

2.3 官方给出的两条非交互密码路径

官方说明在 libpq 文档里明确列了几种提供密码的方式,按优先级是这样的:

  • PGPASSWORD环境变量
  • ~/.pgpass密码文件
  • 连接服务文件
  • 交互式提示输入

先看PGPASSWORD。这个变量只要在环境里存在,psql、pg_dump等工具就会自动把它当作当前用户的密码。优点是配置简单,缺点是进程环境里存在密钥,而且有些系统监控工具可能把进程的完整环境打到日志里。官方文档的态度很明确:不建议在生产环境把它当长期方案。

再看.pgpass。官方给它定义了一种非常紧凑的格式:

主机名:端口:数据库:用户名:密码

五个字段,用冒号分隔。*可以当通配符,匹配任意内容。完整例子:

*:5432:mydb:backup_user:备份用户的密码

这行的意思是:不管从哪个主机来、端口是 5432、连的是 mydb、用户名是 backup_user,就使用后面的密码去认证。需要注意,如果数据库名写成*,表示匹配任意数据库,但认证逻辑仍由 pg_hba.conf 决定。

.pgpass文件的位置默认在用户主目录下($HOME/.pgpass),也可以通过环境变量PGPASSFILE指定其他位置。官方对它有两条铁律:一是文件权限必须是0600,不能给组和其他用户任何读权限;二是文件里的密码不需要转义,但整个文件内容必须以换行符结束,否则最后一行可能读不到。这两条就是“官方说明”里最容易出问题的地方,很多备份脚本一夜之间坏了,都是因为 .pgpass 权限被某个发布脚本改成 0644,或者最后一行没写换行。

3. 实操过程:配置密码自动备份的完整步骤

3.1 先理清环境,再做最小验证

在写备份脚本之前,我习惯先在一个最小环境里验证连接与认证,把问题边界切开。假设业务上有一台数据库服务器,已创建了备份专用账号,下面按这套走。

第一步,用 psql 手动连一次,确认账号密码本身没问题:

PGPASSWORD='备份用户的密码' psql -h 127.0.0.1 -U backup_user -d mydb -c 'SELECT 1;'

这一步如果直接过了,说明密码和认证方式没问题。如果在这就报错,需要回到第 2 章去看 pg_hba.conf 的命中规则,以及密码加密方式是否匹配。

第二步,确认备份目录存在且有足够空间:

sudo mkdir -p /backup/pgsql sudo chown postgres:postgres /backup/pgsql

备份目录属主,我一般直接给 postgres 系统用户,因为备份脚本由 postgres 用户执行,天然符合最小权限原则。如果你希望用独立的 backup 系统用户,那要把目录属主对应该用户。

第三步,针对密码文件做一次非交互测试。这里先看PGPASSWORD方式能不能在脚本里正常工作:

PGPASSWORD='备份用户的密码' pg_dump -h 127.0.0.1 -U backup_user -Fc mydb -f /tmp/test.dump

如果成功,说明备份命令本身没毛病。接下来要做的,是把密码从“环境变量”迁移到“.pgpass”,这是一次对安全性和稳定性的升级。

3.2 配置 .pgpass 文件并验证免密连接

.PGPASS 文件的配置过程,很多人喜欢手动编辑,但我更推荐在命令行里用一行完成写入,因为这样可以精确控制权限和内容格式:

touch /home/postgres/.pgpass echo '*:5432:mydb:backup_user:备份用户的密码' > /home/postgres/.pgpass chmod 600 /home/postgres/.pgpass chown postgres:postgres /home/postgres/.pgpass

注意chmod必须放在写入之后,不要先赋权限再写内容,防止写入瞬间文件处于可读状态。echo写完默认带换行,这正好符合官方对最后一行换行的要求。

接下来做免密验证:

psql -h 127.0.0.1 -U backup_user -d mydb -c 'SELECT 1;'

这时候如果回显一个?column?和数字 1,说明 .pgpass 已生效。如果没生效,先检查两个地方:文件属主和权限是不是0600,以及你执行命令的当前用户主目录下是不是真的有这个文件。很多人配置在/root下,结果脚本却用 postgres 用户跑,自然读不到。

如果你不想让备份用户的主目录太乱,也可以把密码文件放到专用目录,通过PGPASSFILE指定。比如:

mkdir -p /etc/postgresql/backup echo '*:5432:mydb:backup_user:备份用户的密码' > /etc/postgresql/backup/.pgpass chmod 600 /etc/postgresql/backup/.pgpass export PGPASSFILE=/etc/postgresql/backup/.pgpass

这种方式更便于集中管理密钥文件,后续做权限分离也更干净。执行备份的脚本开头统一加一句export PGPASSFILE=...即可。

3.3 封装备份脚本,把密码留在后台

密码文件配好以后,备份脚本本身就不应该再出现任何密码字符串。一个比较完整、可以直接放到 crontab 里的备份脚本大概长这样:

#!/usr/bin/env bash BACKUP_DIR="/backup/pgsql" BACKUP_DB="mydb" BACKUP_USER="backup_user" PGHOST="127.0.0.1" BACKUP_FILE="${BACKUP_DIR}/mydb_$(date +%Y%m%d_%H%M%S).dump" export PGPASSFILE="/home/postgres/.pgpass" LOG_FILE="${BACKUP_DIR}/backup.log" echo "$(date '+%Y-%m-%d %H:%M:%S') 开始备份 ${BACKUP_DB}" >> "${LOG_FILE}" pg_dump -h "${PGHOST}" -p 5432 -U "${BACKUP_USER}" -Fc -Z 5 -f "${BACKUP_FILE}" "${BACKUP_DB}" if [ $? -eq 0 ]; then echo "$(date '+%Y-%m-%d %H:%M:%S') 备份成功: ${BACKUP_FILE}" >> "${LOG_FILE}" else echo "$(date '+%Y-%m-%d %H:%M:%S') 备份失败: ${BACKUP_DB}" >> "${LOG_FILE}" exit 1 fi # 清理 7 天前的旧备份 find "${BACKUP_DIR}" -name "mydb_*.dump" -type f -mtime +7 -delete

脚本里几个关键点展开说一下:

  • -Fc使用自定义压缩格式,配合pg_restore可以做选择性恢复,比纯 SQL 文本灵活得多。
  • -Z 5指定压缩级别,介于 1-9 之间,5 是空间和速度比较均衡的值。
  • $?判断上一条命令的退出码,这是最简单的成功/失败判断方式,备份失败必须及时暴露到日志里。
  • find ... -delete清理旧备份,防止磁盘被历史备份填满。天数按业务保留策略调整。

脚本写完后要赋权:

chmod 700 /opt/scripts/backup_pgsql.sh chown postgres:postgres /opt/scripts/backup_pgsql.sh

700意味着只有属主能读和执行,备份脚本即便不含明文密码,也不该让无关账号读取里面的连接参数和日志路径。

3.4 配置 crontab 并验证整链路

crontab 的配置本身不复杂,但有几个隐藏坑。我用 postgres 用户身份编辑计划任务:

crontab -u postgres -e

写入一行:

30 2 * * * /opt/scripts/backup_pgsql.sh >/dev/null 2>&1

表示每天凌晨 2 点 30 分执行备份脚本。>/dev/null 2>&1把正常输出和错误输出都吞掉,因为脚本内部已把结果写进了日志,避免 crontab 邮件轰炸。

调度配好之后,不要干等第二天的凌晨 2 点,立即手动执行一遍:

sudo -u postgres /opt/scripts/backup_pgsql.sh

执行完查看日志:

cat /backup/pgsql/backup.log

然后确认备份文件生成:

ls -lh /backup/pgsql/

最后做一次恢复演练,确认备份文件不是“表面活着”:

pg_restore -l /backup/pgsql/mydb_最新时间.dump | head -20

pg_restore -l只列出备份内容,不做恢复,是验证备份文件完整性的低成本手段。这一步我觉得比备份本身更重要,只备份不验证,等于把安全托付给运气。

整个链路通了之后,密码配置的“问题”基本就翻篇了。后面再碰到备份失败,最大的可能只剩下三个:密码被动改过、磁盘空间满了、pg_hba.conf 被人动过顺序。这些都会体现在日志或数据库服务日志里,排查方向是固定的。

4. 常见问题与排查技巧实录

4.1 备份密码相关的经典报错

整理了一个速查表,全都是实战中频率最高的几类报错,按官方说明的逻辑去对照着解决,基本不会跑偏。

报错信息通常原因处理方式
no password supplied客户端没有提供任何密码,认证方式又要求密码检查 .pgpass 是否存在、权限是否为 0600、PGPASSFILE 是否指向正确
password authentication failed for user "..."密码本身不对,或密码文件里对应条目的密码已经过期/被改确认数据库密码是否被 ALTER USER 改过;测试时手动用 PGPASSWORD 连一次
could not change permissions of "/home/postgres/.pgpass".pgpass 权限不是 0600,libpq 拒绝读取执行chmod 600,不要把文件放到会被自动同步工具改写权限的共享目录
Peer authentication failed for user "backup_user"走 Unix Socket 连接,命中了 peer 认证规则备份脚本统一走-h 127.0.0.1,避免落入 peer 规则
fe_sendauth: no password supplied远程连接需要密码,但客户端没传确认 pg_hba.conf 对应条目是不是scram-sha-256或md5;确认 .pgpass 里有对应条目的 host 和数据库名
permission denied for table ...备份用户虽有连接权限,但没有对应表的读权限给备份用户授权,比如GRANT SELECT ON ALL TABLES IN SCHEMA public TO backup_user;

从表里可以看出,多数报错其实都能归到两类:客户端没有提供密码(文件或变量问题),或者服务端拒绝了所提供密码(认证规则或密码本身问题)。

4.2 一套高效的排查方法论

面对备份失败,别急着改密码。先按下面这个顺序来,大多数问题十分钟内能定位。

第一步,先看数据库日志。日志默认位置在数据目录下的log/,或者在postgresql.conf里logging_collector = on时指定的文件。日志里对每个认证失败几乎都有明确记录,会写明客户端 IP、用户、数据库名、认证方式,甚至具体是哪个环节失败。这一步能直接排除一半以上的“瞎猜”。

第二步,用最小命令隔离变量。先在命令行手动执行:

psql -h 127.0.0.1 -U backup_user -d mydb -c 'SELECT 1;'

如果这个交互命令要求输密码,说明 .pgpass 没生效;如果它直接进去了,说明密码文件生效正常,问题出在脚本环境,比如 crontab 里没有继承 PGPASSFILE 之类的环境变量。

第三步,检查当前命中的 pg_hba.conf 规则。在数据库服务器上执行:

su - postgres -c "psql -c 'SHOW hba_file;'"

打开对应文件,人工从头往下数一遍,看你的备份连接会命中最前面的哪一条。重点看是不是有trust在更前面“截胡”了,或者有一条更严格的规则在真正允许的规则前拦住了。

第四步,查看 .pgpass 的实际内容和环境值:

ls -l /home/postgres/.pgpass cat /home/postgres/.pgpass env | grep PGPASS

检查完这四步,如果问题还在,那基本就是密码被改或者中间有代理/连接池介入,需要回到业务侧确认。

4.3 几个容易忽略的细节

最后分享几个我实际踩过、且手册上不太显眼的细节。

第一点,.pgpass里字段为空也会被读取,但限定逻辑很严格。比如你想写“任意端口”但又不想写*,空字段反而不匹配。建议统一使用*,不要省。

第二点,密码包含冒号或反斜杠时,.pgpass官方不要求转义,但冒号会破坏五字段结构。极少数情况你会遇到那种密码带冒号的环境,实际测试中我发现这种密码会干扰解析。所以配置备份账号时,建号阶段就尽量避免使用冒号、换行这类特殊字符。不是不能处理,是没必要给自己找麻烦。

第三点,备份脚本里的主机地址如果写的是localhost,有些环境会走 Unix Socket,而不是 TCP。Socket 连接的默认认证方式可能跟 TCP 不同,导致 .pgpass 规则看似生效,实际上命中的是另一套认证逻辑。统一写127.0.0.1能少踩一个坑。

第四点,crontab 执行的环境变量与手动执行不同。手动命令行能连上数据库,不代表 cron 里能连上。脚本开头最好显式设置PGHOST、PGPORT、PGUSER、PGPASSFILE,不要依赖.bash_profile或.bashrc里的默认值。

4.4 定期轮换备份密码时的小提醒

如果业务策略要求定期改密码,别忘了同步更新 .pgpass 文件。更新时尽量不要直接编辑原文件,而是先写临时文件再原子替换,防止进程中途读到半个文件:

echo '*:5432:mydb:backup_user:新密码' > /tmp/.pgpass.tmp chmod 600 /tmp/.pgpass.tmp chown postgres:postgres /tmp/.pgpass.tmp mv /tmp/.pgpass.tmp /home/postgres/.pgpass

改完之后立刻手动跑一次备份脚本,做一次完整链路验证。等确认新的密码文件工作正常,再去更新你内部密码管理系统的记录。

我个人在实际操作中的体会是:PostgreSQL 官方这套密码认证体系其实设计得挺克制,它给了你灵活性,但要求你对 pg_hba.conf 和 libpq 的读取顺序有起码的尊重。备份密码问题翻来覆去就那几类,只要把.pgpass权限锁定、字段写全、脚本主机地址固定、日志路径留好,自动化备份的“密码这一关”基本一劳永逸地过去了。之后要担心的,就是磁盘容量和恢复演练这种更靠后的问题了。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/20 16:47:55

QuarkAudio深度拆解:实时音频处理引擎的架构设计与工程实践

QuarkAudio 技术报告这个标题,乍看像是某个音频库的发布说明,但真正把它读完你会发现,里面全是实打实的音频处理底层细节。做音频开发这些年,我翻过不少技术文档,能像这份报告一样把采集、预处理、特征提取、AI 推理整…

作者头像 李华
网站建设 2026/10/20 16:47:40

LobeChat接入DeepSeek R1:三种部署方式与避坑指南

简介:这是一份与 lobe-chat 及 DeepSeek R1 集成相关的完整前端工程资源包,适合具备一定前端基础、希望搭建智能对话界面并接入 DeepSeek R1 模型的开发者。包内以 tsx 与 ts 文件为绝对主体,对应 React 组件、状态逻辑与业务模块&#xff1b…

作者头像 李华
网站建设 2026/10/20 16:47:40

Coding Plan配额不是消耗品,而是可调度的算力信用体系

1. 先说结论:Coding Plan不是“过期食品”,而是被严重低估的弹性资源池 “Coding Plan每个月都用不完怎么办?”——这个问题在最近三个月里,我陆续收到二十七位不同背景的开发者私信,从刚转行的前端新人,到…

作者头像 李华
网站建设 2026/10/20 16:47:10

AI取代白领?五年后真相:从技术边界到任务重组的冷静拆解

1. 五年之期:这条预言为什么会反复刷屏每隔一阵子,总能在时间线上刷到类似的说法:"AI 将在 XX 年内取代 XX% 的工作岗位。"这一次,数字变成了 50%,对象是白领,期限是五年。转发区里有人焦虑&…

作者头像 李华
网站建设 2026/10/20 16:46:00

深度学习入门实战:用Python和神经网络实现计算机视觉图像识别

深度学习入门这件事,最怕的就是一上来被各种名词砸晕。计算机视觉、Python、机器学习、神经网络,这四个词随便拎一个出来都能写一本书,但把它们放在一起,其实指向的是一条非常具体的路径:用Python写代码,拿…

作者头像 李华
网站建设 2026/10/20 16:45:58

昆仑通泰触摸屏与变频器Modbus RTU通讯实战:接线、寄存器映射与调试

在项目现场摸爬滚打这些年,调过最多的就是触摸屏和变频器之间的通讯。前阵子给一条产线的变频器做集中监控,要求把运行频率、输出电流实时显示在昆仑通泰触摸屏上,还要能远程启停、调速度,通讯协议顺理成章选了 Modbus RTU。整套系…

作者头像 李华