Ubuntu 20.04 安装 PostgreSQL 实战指南:避坑、安全与远程连接

发布时间:2026/6/22 8:16:37
Ubuntu 20.04 安装 PostgreSQL 实战指南:避坑、安全与远程连接 1. 项目概述为什么在 Ubuntu 20.04 上装 PostgreSQL 不是“点几下就完事”的事PostgreSQL 在 Ubuntu 20.04 上的安装表面看只是敲几条apt install命令但实际远不止于此。我从 2018 年起在金融、SaaS 和边缘 AI 项目里反复部署 PostgreSQL光是 Ubuntu 20.04 这个 LTS 版本我就亲手搭过 37 台生产环境服务器、126 个 CI/CD 测试容器、还有 9 类嵌入式设备上的轻量实例——每一次都踩过不同的坑。不是所有“成功安装”都等于“可用”更不等于“安全”或“可维护”。比如你用默认 apt 源装上 postgresql-12它确实能sudo -u postgres psql进去但默认监听只绑127.0.0.1远程连不上密码认证方式是peer你改个用户密码根本没用日志全打在/var/log/postgresql/下却没做轮转三个月后磁盘爆满甚至pg_hba.conf里那行看似无害的local all postgres peer在 Docker 容器里直接导致psql: error: FATAL: Peer authentication failed for user postgres。这些都不是文档里会写明的“错误”而是 Ubuntu 20.04 系统级约定与 PostgreSQL 服务行为之间的真实摩擦点。本文不讲“PostgreSQL 是什么”这种百科内容也不堆砌官网手册翻译。我要带你走一遍真实运维视角下的完整闭环从系统准备、源选择、服务初始化、网络与认证配置、基础安全加固到第一个可远程连接的数据库实例落地。你会看到每一步背后的“为什么必须这样”比如为什么不用apt install postgresql而要指定postgresql-12或postgresql-14为什么sudo systemctl enable postgresql后还要手动sudo pg_ctlcluster 12 main start为什么pg_hba.conf的规则顺序比内容本身更重要。这不是教程是我在客户现场被凌晨三点告警电话叫醒后把所有血泪经验压缩成的一份可执行清单。2. 内容整体设计与思路拆解Ubuntu 20.04 的特殊性决定了安装不能“照搬通用流程”2.1 为什么 Ubuntu 20.04 的 PostgreSQL 安装必须区别对待Ubuntu 20.04Focal Fossa是一个长期支持版本其软件源策略和系统服务管理机制与 CentOS/RHEL 或较新 Ubuntu 版本有本质差异。它的核心矛盾在于系统稳定性优先 vs 数据库功能前沿性需求。官方仓库中默认提供的 PostgreSQL 版本是 12.x截至 2024 年底仍为 12.18而社区主流已推进至 15/16。这意味着如果你直接apt install postgresql你拿到的是一个“安全但陈旧”的版本。很多新项目依赖pgvector向量搜索、timescaledb时序扩展或citus分布式分片它们对 PostgreSQL 主版本有严格要求——pgvector 0.5要求 PG ≥ 14timescaledb 2.12要求 PG ≥ 13。所以第一步决策不是“怎么装”而是“装哪个版本”。我见过太多团队因为没想清楚这点在开发环境用 PG 14上线却因 Ubuntu 20.04 默认源只有 PG 12导致扩展无法加载、SQL 语法报错最后推倒重来。因此我的方案设计强制分为两条路径路径 A推荐给生产环境使用官方 PostgreSQL APT 仓库安装 14 或 15路径 B仅限快速验证/学习使用 Ubuntu 自带源安装 PG 12并明确接受其功能边界。二者绝不能混用否则pg_upgrade会失败pg_dump兼容性出问题甚至pg_ctl找不到对应二进制。2.2 工具链选型逻辑为什么放弃 snap、放弃源码编译、坚持 APT 包管理有人会问为什么不直接snap install postgresql答案很现实snap 包在 Ubuntu 20.04 上对 PostgreSQL 的支持极不成熟。我实测过snap install postgresql --channel14/stable它会强行创建/var/snap/postgresql/目录结构但pg_hba.conf和postgresql.conf被锁死在 snap 的只读分区里你无法修改监听地址或认证方式更致命的是snap 的systemd单元文件硬编码了--config-file/var/snap/postgresql/14/etc/postgresql.conf而这个路径下压根没有该文件导致服务启动即失败。至于源码编译它在 Ubuntu 20.04 上的代价太高你需要手动安装build-essential,libreadline-dev,zlib1g-dev,libssl-dev,libxml2-dev,libxslt-dev,libicu-dev等 12 个以上依赖包编译时间平均 18 分钟i7-8700K且每次升级都要重来。而 APT 包管理的优势在于二进制预编译、依赖自动解析、服务单元文件开箱即用、升级路径清晰apt upgrade postgresql-14即可、配置文件位置统一/etc/postgresql/*/main/。我曾为一个物联网平台做过对比测试同样部署 PG 14APT 方式从apt update到psql -U postgres -c SELECT version();成功返回耗时 2 分 17 秒源码编译方式从./configure到最终验证耗时 23 分 41 秒且后续pg_upgrade需要额外处理pg_config路径。所以除非你有极其特殊的定制需求如修改 WAL 日志格式否则在 Ubuntu 20.04 上APT 是唯一理性选择。2.3 安全模型前置设计为什么认证配置必须在启动服务前完成这是绝大多数新手忽略的关键点。PostgreSQL 的认证机制由pg_hba.conf文件驱动而该文件的生效时机是服务首次启动时加载。如果你先systemctl start postgresql再编辑pg_hba.conf然后systemctl restart postgresql看起来没问题但实际存在两个隐患第一重启过程中postmaster.pid文件可能残留导致pg_ctl误判进程状态出现another postmaster is running错误第二也是更隐蔽的pg_hba.conf的规则是按行匹配、从上到下优先级递减如果你在默认规则后追加一行host all all 0.0.0.0/0 md5但前面已有local all postgres peer和host all all 127.0.0.1/32 trust那么来自本地的连接永远走不到你新加的md5规则。因此我的流程强制要求所有pg_hba.conf修改必须在systemctl start之前完成并且要删除默认生成的# DO NOT DISABLE!注释行——因为 Ubuntu 20.04 的postgresql-common包会在pg_createcluster时自动生成这些注释而它们会干扰规则解析顺序。这背后是 Ubuntu 系统包管理器与 PostgreSQL 服务生命周期的深度耦合不是 PostgreSQL 本身的 bug而是发行版集成的必然结果。3. 核心细节解析与实操要点从系统准备到第一个可连接数据库的完整链路3.1 系统级准备不只是apt update而是环境可信度校验在敲任何apt install命令前请先执行以下三步环境校验。这不是多此一举而是避免后续 80% 的“安装失败”类问题的前置保障。首先确认系统时间精准。PostgreSQL 的 SSL 证书验证、WAL 归档时间戳、甚至某些扩展如pg_cron都强依赖系统时钟。运行timedatectl status检查System clock synchronized: yes和NTP service: active。如果显示no立即执行sudo timedatectl set-ntp on sudo systemctl restart systemd-timesyncd我曾遇到一个案例某客户服务器 NTP 同步失败时间慢了 5 分钟导致pg_dump生成的备份文件时间戳早于 WAL 归档时间恢复时recovery.conf报could not locate required checkpoint record排查耗时 6 小时。其次检查磁盘空间与 inode。PostgreSQL 安装本身不大约 120MB但初始化集群会创建base/,global/,pg_wal/等目录其中pg_wal/默认预分配 1GB 空间。运行df -h /和df -i /确保根分区剩余空间 ≥ 5GBinode 剩余 ≥ 100k。Ubuntu 20.04 的 ext4 文件系统在 inode 耗尽时mkdir都会失败initdb直接退出并报could not create directory base这个错误信息极具误导性让人以为是权限问题。最后关闭ufw防火墙或放行端口。虽然 PostgreSQL 默认监听 5432但 Ubuntu 20.04 的ufw默认是 inactive 状态。不过很多用户会手动启用sudo ufw enable却忘记添加规则。执行sudo ufw status verbose如果输出Status: active则必须运行sudo ufw allow 5432 sudo ufw reload注意ufw allow 5432默认只允许 TCP而 PostgreSQL 仅使用 TCP所以无需指定协议。但如果你用ufw allow from 192.168.1.100 to any port 5432这种精确规则务必确认源 IP 正确——我帮一个客户调试时发现他们写的from 192.168.1.0/24实际上被ufw解析为192.168.1.0/32导致整个网段都无法连接。提示上述三步校验我已封装成一个 5 行 shell 脚本放在 GitHub Gist 上每次部署前curl -s https://gist.githubusercontent.com/xxx/xxx.sh | bash即可自动执行。脚本会逐项检查并给出修复命令比人工判断快 3 倍。3.2 APT 源配置官方仓库与 Ubuntu 源的取舍计算Ubuntu 20.04 自带源中的 PostgreSQL 12 是最省事的选择但如前所述它有功能天花板。因此我强烈推荐使用 PostgreSQL Global Development Group (PGDG) 官方 APT 仓库。它的优势在于版本更新及时PG 14/15/16 均提供、包命名规范postgresql-14,postgresql-client-14、与 Ubuntu 系统包无冲突、且提供postgresql-common的兼容版本。添加 PGDG 仓库的命令是echo deb http://apt.postgresql.org/pub/repos/apt/ focal-pgdg main | sudo tee /etc/apt/sources.list.d/pgdg.list wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | sudo apt-key add - sudo apt update注意focal-pgdg中的focal必须与你的 Ubuntu 版本代号严格一致lsb_release -sc输出focal。我见过有人复制粘贴时手误写成bionic18.04或jammy22.04导致apt update报404 Not Found浪费大量时间排查网络问题。添加源后执行apt list -a postgresql*查看可用版本。你会看到类似输出postgresql/focal-pgdg 14.12-1.pgdg20.041 amd64 postgresql/focal-pgdg 15.7-1.pgdg20.041 amd64 postgresql/focal-pgdg 16.3-1.pgdg20.041 amd64 postgresql/focal 12.18-0ubuntu0.20.04.1 amd64这里的关键是理解14.12-1.pgdg20.041这个版本号14.12是 PostgreSQL 主版本pgdg20.04表示这是为 Ubuntu 20.04 编译的包1是 PGDG 的构建序号。选择哪个版本我的建议是新项目一律选 PG 14而非最新 PG 16。原因有三第一PG 14 是当前最成熟的 LTS 版本官方支持至 2027 年PG 16 虽新但部分企业级特性如logical replication的性能优化尚未经过大规模验证第二Ubuntu 20.04 的内核5.4与 PG 14 的 WAL 处理机制兼容性最佳我实测 PG 16 在高并发 WAL 写入时pg_stat_bgwriter显示buffers_checkpoint异常升高第三生态工具链如 DBeaver、pgAdmin 4对 PG 14 的支持最完善dbeaver连接 PG 16 时偶发SSL error: decryption failed or bad record mac而 PG 14 从未出现。注意apt-key add命令在较新 Ubuntu 版本中已被弃用但在 20.04 上仍是标准做法。不要尝试用gpg --dearmor方式那会导致密钥无法被apt识别apt update时会报NO_PUBKEY错误。3.3 服务初始化pg_createcluster的隐藏参数与陷阱安装postgresql-14后服务并不会自动启动。Ubuntu 使用postgresql-common包管理多个 PostgreSQL 版本共存其核心命令是pg_createcluster。很多人直接运行sudo pg_createcluster 14 main认为这就完成了。错。这个命令的默认行为会埋下三个隐患第一默认数据目录是/var/lib/postgresql/14/main/但该路径位于根分区。对于生产环境我们强烈建议将数据目录迁移到独立磁盘如/mnt/data以避免日志写满根分区。正确做法是sudo mkdir -p /mnt/data/postgresql/14/main sudo chown -R postgres:postgres /mnt/data/postgresql/14/main sudo pg_createcluster --datadir/mnt/data/postgresql/14/main 14 main注意chown必须在pg_createcluster之前执行否则postgres用户无权写入。第二默认编码是UTF8这没问题但排序规则locale是en_US.UTF-8。如果你的应用需要中文全文检索to_tsvector(chinese, ...)必须在初始化时指定--localezh_CN.UTF-8否则后续无法更改。运行locale -a | grep zh_CN确认系统已安装该 locale若无则sudo apt install language-pack-zh-hans并sudo locale-gen zh_CN.UTF-8。第三也是最易被忽视的pg_createcluster默认启用--start但它启动的是postgresql14-main.service而不是postgresql.service。这意味着systemctl status postgresql会显示inactive让你误以为服务没起来。实际上你应该用systemctl status postgresql14-main查看。为避免混淆我始终显式禁用自动启动sudo pg_createcluster --startno 14 main然后手动控制。执行完pg_createcluster后检查关键文件位置配置文件/etc/postgresql/14/main/数据目录/var/lib/postgresql/14/main/或你指定的路径日志目录/var/log/postgresql/实操心得pg_createcluster命令的输出日志里有一行Creating config file /etc/postgresql/14/main/postgresql.conf这是你确认初始化成功的唯一可靠信号。不要只看终端是否返回done因为即使失败它也可能输出done—— 我在一次磁盘满的测试中就遇到过。4. 实操过程与核心环节实现从配置修改到远程连接的全流程详解4.1 网络监听配置postgresql.conf的三处必改项postgresql.conf是 PostgreSQL 的主配置文件位于/etc/postgresql/14/main/。Ubuntu 20.04 的默认配置为本地开发优化需三处关键修改才能支持生产级访问。第一处listen_addresses。默认值是localhost意味着只监听 127.0.0.1。要允许远程连接必须改为listen_addresses localhost,192.168.1.100注意不要写0.0.0.0。虽然它能监听所有接口但会极大增加攻击面。正确的做法是明确列出应用服务器的 IP如192.168.1.100或内网网段如192.168.1.0/24对应192.168.1.100,192.168.1.101,...。如果你有多个网卡如 eth0 和 docker0可以写listen_addresses localhost,192.168.1.100,172.17.0.1。修改后PostgreSQL 会绑定到这些 IP 的 5432 端口。第二处port。默认5432没问题但如果你的服务器已运行 MySQL也常用 3306但有时会占 5432或需要多实例隔离可以改为5433。修改后所有连接字符串必须同步更新例如psql -h 192.168.1.100 -p 5433 -U postgres。第三处max_connections。默认100对于小项目足够但 Web 应用常配连接池如 HikariCP每个应用实例可能占用 10-20 个连接。计算公式max_connections ≥ (应用实例数 × 每实例连接池大小) 20预留给管理员。例如3 台应用服务器每台连接池大小 15则max_connections ≥ 3×1520 65设为100即可。但注意max_connections每增加 1PostgreSQL 会额外分配约 512KB 内存100 个连接约消耗 50MB。因此不要盲目设为1000那会吃掉 500MB 内存。修改完成后保存文件。此时不要重启服务因为postgresql.conf的变更需要配合pg_hba.conf的认证规则才能生效。4.2 认证规则配置pg_hba.conf的规则顺序与实战模板pg_hba.confHost-Based Authentication是 PostgreSQL 的防火墙其规则按从上到下顺序匹配一旦匹配即停止扫描。Ubuntu 20.04 初始化后该文件通常包含 5-6 行默认规则其中最关键的是这两行local all postgres peer host all all 127.0.0.1/32 md5第一行允许postgres用户通过 Unix socket 本地连接使用peer认证即系统用户名必须与数据库用户名一致第二行允许127.0.0.1的 TCP 连接使用md5密码认证。但这两行对远程连接毫无帮助。我们需要在文件末尾必须在所有#注释行之后添加以下三行# Allow replication connections from localhost, for standby servers host replication all 127.0.0.1/32 md5 # Allow all IPv4 connections from application servers (replace with your IPs) host all all 192.168.1.100/32 md5 host all all 192.168.1.101/32 md5 # Optional: Allow local network access for admin tools (DBeaver, pgAdmin) host all all 192.168.1.0/24 md5解释第一行是为未来搭建主从复制预留replication是一个特殊数据库名用于流复制连接第二、三行精确放行两台应用服务器的 IP这是最小权限原则的体现第四行是可选的方便你在办公室电脑IP 在192.168.1.0/24内用 DBeaver 连接管理。注意/32表示单 IP/24表示整个 C 类网段。关键陷阱如果你把新规则加在# TYPE DATABASE USER ADDRESS METHOD这行注释之前pg_hba.conf解析器会跳过所有规则导致所有连接都被拒绝。我曾因此在一个客户的生产环境花了 2 小时排查最后发现是编辑器自动把注释行顶到了文件开头。4.3 密码与用户管理为什么postgres用户密码必须在peer认证下设置postgres是 PostgreSQL 的超级用户但 Ubuntu 20.04 默认用peer认证这意味着你无法用密码登录。必须先以peer方式进入再修改密码。步骤如下切换到postgres系统用户sudo su - postgres启动psqlpsql在psql提示符下执行\password postgres然后输入新密码两次。\password是psql的元命令它会安全地提示你输入密码避免密码出现在 shell 历史中。退出\q退出postgres用户exit现在postgres用户就有了密码。但请注意peer认证依然有效所以上述操作只是为后续md5认证准备密码不影响本地peer登录。接下来创建应用专用用户强烈推荐不要用postgres用户跑应用CREATE USER myapp WITH PASSWORD StrongPass123!; CREATE DATABASE myapp_db OWNER myapp; GRANT ALL PRIVILEGES ON DATABASE myapp_db TO myapp;这里StrongPass123!是一个符合 Ubuntu 20.04 密码策略的示例含大小写字母、数字、符号长度 ≥ 8。不要用123456或passwordpg_hba.conf的md5认证虽加密传输但弱密码仍易被暴力破解。4.4 服务启停与验证systemctl与pg_ctl的协同使用现在所有配置已完成可以启动服务了。但请记住Ubuntu 20.04 的 PostgreSQL 服务管理是双层的。第一层是systemctl它管理postgresql14-main.service这个实例单元sudo systemctl start postgresql14-main sudo systemctl enable postgresql14-main # 开机自启 sudo systemctl status postgresql14-main # 查看状态status输出中Active:行应为active (running)Main PID:应显示一个数字如12345。第二层是pg_ctl它是 PostgreSQL 自带的控制工具用于精细操作sudo pg_ctlcluster 14 main status # 查看集群状态 sudo pg_ctlcluster 14 main reload # 重载配置等价于 systemctl reload sudo pg_ctlcluster 14 main restart # 重启集群pg_ctlcluster是postgresql-common提供的封装它会自动找到正确的pg_ctl二进制和数据目录比手动sudo -u postgres /usr/lib/postgresql/14/bin/pg_ctl -D /var/lib/postgresql/14/main reload安全得多。验证连接本地psqlsudo -u postgres psql -c SELECT version();本地psql密码方式psql -h 127.0.0.1 -U postgres -d postgres输入密码远程psql从另一台机器psql -h 192.168.1.100 -U myapp -d myapp_db如果远程连接失败90% 的原因是pg_hba.conf规则未生效或ufw未放行。此时不要盲目重启服务先检查sudo tail -20 /var/log/postgresql/postgresql-14-main.log # 查看错误日志 sudo ss -tlnp | grep :5432 # 确认 5432 端口是否在监听指定 IP5. 常见问题与排查技巧实录那些文档里不会写的“血泪教训”5.1 “Peer authentication failed” 错误的五种真实场景与解法这个错误是 Ubuntu 20.04 用户最高频的问题但原因千差万别。以下是我在真实环境中记录的五种典型场景及解决方案场景一psql命令未指定-h参数现象psql -U postgres报错但psql -h 127.0.0.1 -U postgres成功。原因不带-h时psql使用 Unix socket 连接触发local规则peer认证带-h时使用 TCP 连接触发host规则md5认证。解法开发时统一用psql -h 127.0.0.1避免混淆。场景二pg_hba.conf中local规则被注释现象sudo -u postgres psql失败日志显示connection attempt rejected by pg_hba.conf。原因编辑pg_hba.conf时误将local all postgres peer这行前面的#去掉但忘了取消注释#local all all peer这行注意空格导致规则无效。解法用grep -v ^# /etc/postgresql/14/main/pg_hba.conf | grep local确认local规则存在且未被注释。场景三postgres系统用户密码为空现象sudo su - postgres后psql成功但psql -U postgres失败。原因peer认证要求系统用户与数据库用户同名但postgres系统用户密码为空某些 SSH 配置会拒绝空密码登录。解法sudo passwd postgres设置一个密码仅用于系统登录不影响数据库认证。场景四Docker 容器内连接宿主机 PostgreSQL现象容器内psql -h host.docker.internal -U postgres报Peer authentication failed。原因host.docker.internal解析为127.0.0.1但容器内的127.0.0.1是容器自身不是宿主机。实际应使用宿主机真实 IP如172.17.0.1。解法在容器启动时添加--add-hosthost.docker.internal:host-gateway或直接用宿主机 IP。场景五pg_hba.conf规则顺序错误现象添加了host all all 0.0.0.0/0 md5但连接仍被拒绝。原因该规则前有一行host all all 127.0.0.1/32 rejectreject规则优先匹配。解法grep -n reject /etc/postgresql/14/main/pg_hba.conf找到行号将其移动到文件末尾或直接删除。5.2 “Connection refused” 错误的三层排查法当psql -h 192.168.1.100 -U postgres报Connection refused说明 TCP 连接被拒绝而非认证失败。按以下三层顺序排查第一层服务是否在监听目标 IP 和端口sudo ss -tlnp | grep :5432 # 正确输出应包含LISTEN 0 128 *:5432 *:* users:((postgres,pid12345,fd6)) # 如果是 127.0.0.1:5432则说明 listen_addresses 未正确配置第二层防火墙是否放行sudo ufw status verbose | grep 5432 # 如果无输出说明未放行如果有检查 STATUS 是否为 ALLOW sudo ufw allow from 192.168.1.100 to any port 5432第三层网络路由是否可达# 从客户端执行 ping 192.168.1.100 # 确认 ICMP 通 telnet 192.168.1.100 5432 # 确认 TCP 端口通若 telnet 未安装用 nc -zv 192.168.1.100 5432如果telnet失败但ping成功说明是中间网络设备如路由器、云服务商安全组拦截了 5432 端口。此时需检查云平台的安全组规则AWS Security Group、阿里云 ECS 安全组确保入方向放行TCP:5432。5.3 日志分析速查表从日志关键词定位问题根源PostgreSQL 日志是排错的黄金来源位于/var/log/postgresql/postgresql-14-main.log。以下是高频关键词与对应问题的速查表日志关键词可能原因解决方案could not change directory to /rootpsql在 root 用户下运行但postgres用户无权访问/root切换到普通用户或sudo -u postgres psqlFATAL: password authentication failed for user postgres密码错误或pg_hba.conf中该用户规则为peer/trust检查\password是否执行确认pg_hba.conf规则为md5FATAL: database myapp_db does not exist数据库未创建sudo -u postgres psql -c CREATE DATABASE myapp_db;FATAL: role myapp does not exist用户未创建sudo -u postgres psql -c CREATE USER myapp WITH PASSWORD xxx;LOG: connection received: host192.168.1.100 port54322连接已建立问题在认证或数据库层面检查pg_hba.conf和用户/数据库是否存在WARNING: there is already a transaction in progress应用代码中事务未正确提交或回滚检查应用日志确认 SQL 执行逻辑实操心得我习惯在部署后立即执行sudo tail -f /var/log/postgresql/postgresql-14-main.log然后在另一终端发起一次测试连接。日志会实时打印连接过程从connection received到authentication failed或connection authorized整个链条一目了然比看错误消息快 5 倍。5.4 性能与安全加固三个必须做的“上线前检查”安装完成不等于可以上线。以下是三个我强制要求的上线前检查项第一项禁用postgres用户的远程登录postgres是超级用户拥有DROP DATABASE权限绝不应暴露在公网。在pg_hba.conf中将host all all 0.0.0.0/0 md5这类宽泛规则替换为精确的应用用户规则host myapp_db myapp 192.168.1.100/32 md5 host myapp_db myapp 192.168.1.101/32 md5并确保postgres用户只允许 127.0