文章目录
- [Rocky Linux 9.8 安装 PostgreSQL 16:自定义数据目录 + 远程访问完整记录](#Rocky Linux 9.8 安装 PostgreSQL 16:自定义数据目录 + 远程访问完整记录)
- 环境
- [一、装 PostgreSQL 16](#一、装 PostgreSQL 16)
- 二、建数据目录
- 三、初始化数据库
- [四、SELinux 和防火墙:这次关着,但别默认别人也关](#四、SELinux 和防火墙:这次关着,但别默认别人也关)
- [五、交给 systemd 管理](#五、交给 systemd 管理)
- 六、确认端口和数据目录都对上了
- [七、给 postgres 设密码](#七、给 postgres 设密码)
- 八、开放远程访问
-
- [8.1 改监听地址](#8.1 改监听地址)
- [8.2 配 pg_hba.conf](#8.2 配 pg_hba.conf)
- [8.3 放行防火墙](#8.3 放行防火墙)
- [8.4 让配置生效](#8.4 让配置生效)
- 九、客户端测试,以及三种报错怎么区分
- [十、业务程序别用 postgres 超级用户](#十、业务程序别用 postgres 超级用户)
- 十一、几个常用参数
- 十二、日常会用到的几条命令
- 十三、最终的文件布局
- 十四、验收顺序
- 十五、几个容易踩的坑
Rocky Linux 9.8 安装 PostgreSQL 16:自定义数据目录 + 远程访问完整记录
内网一台 Rocky 9.8 要装 PostgreSQL 16 给业务系统用。系统盘不大,数据目录定在 /data/app/postgresql,没走默认的 /var/lib/pgsql/16/data。
一开始以为只是把 initdb -D 的路径换一下的事,实际动手才发现自定义目录会额外带出两个问题:systemd 的 PGDATA 得覆盖,pg_hba.conf 和 postgresql.conf 也跟着搬了家。下面按实际操作顺序记一遍,中途碰到的报错原文一起贴上。
环境
| 项 | 值 |
|---|---|
| 操作系统 | Rocky Linux 9.8 (Blue Onyx) |
| PostgreSQL | 16(PGDG 官方源) |
| 数据目录 | /data/app/postgresql(/data 是单独挂的数据盘) |
| 端口 | 5432 |
| 客户端服务器 | 192.168.1.2 |
| SELinux | Disabled |
| firewalld | 未启用 |
Rocky 9.2 到 9.8 在这一套流程里没有差别,命令可以直接照搬,PGDG 仓库那个包选 EL-9 就行,整个 9.x 系列共用。
SELinux 和 firewalld 这两项单独列出来,是因为它们直接决定了后面的步骤能不能省。这台机器上两者都是关着的,所以没踩到;但换一台默认配置的机器,这两处是绕不过去的,第四节会说明。
一、装 PostgreSQL 16
Rocky 9 自带的 appstream 仓库里有 postgresql 模块,但版本停在 13,要 16 得加 PGDG 官方源:
bash
dnf -y install https://download.postgresql.org/pub/repos/yum/reporpms/EL-9-x86_64/pgdg-redhat-repo-latest.noarch.rpm
装完先别急着装包,把系统自带的 postgresql 模块禁掉:
bash
dnf -qy module disable postgresql
这一步漏掉的话,dnf install 有可能被 module 拦下来,或者给你装出一套来源混杂的包,后面查问题很烦。
然后装服务端和客户端:
bash
dnf -y install postgresql16-server postgresql16
PGDG 的包装完之后不会往 /usr/bin 里塞东西,二进制全在 /usr/pgsql-16/bin/ 下面,所以后面 initdb、pg_ctl 都得写全路径,别用系统里可能存在的同名命令。
bash
ls /usr/pgsql-16/bin/
顺手提一句,postgresql16 这个包不含 contrib 扩展。如果要用 pg_stat_statements、pg_trgm 之类的东西,记得把 postgresql16-contrib 一起装上,装完再补也行。
二、建数据目录
bash
mkdir -p /data/app/postgresql
chown -R postgres:postgres /data/app/postgresql
chmod 700 /data/app/postgresql
chmod 700 不是可选项。PostgreSQL 启动时会检查数据目录的权限,只要有同组或其他用户的可读权限就直接拒绝启动,日志里只留一句 permission denied,看着像别的问题,实际就是权限给宽了。这个目录里装的是整个数据库的全部内容,收紧就对了。
postgres 这个系统用户在上面装包的时候已经自动创建了,chown 不会报错。
三、初始化数据库
指定 -D 到我们的目录:
bash
sudo -u postgres /usr/pgsql-16/bin/initdb -D /data/app/postgresql/
初始化完会打印这么一行:
text
成功。您现在可以用下面的命令开启数据库服务器:
/usr/pgsql-16/bin/pg_ctl -D /data/app/postgresql/ -l 日志文件 start
先别照它说的用 pg_ctl 起,我们后面用 systemd 托管。
初始化过程中有两段输出看着像报错,其实都不用管。
第一段:
text
initdb: 无法为本地化语言环境"zh_CN.UTF-8"找到合适的文本搜索配置
缺省的文本搜索配置将会被设置到"simple"
这只是说没有配套的中文全文检索配置,跟数据库能不能用完全无关。真要做中文分词检索,一般也是外挂 zhparser 或者 pg_jieba,不会指望自带的。数据类型、字符集、排序规则都不受影响。
第二段:
text
initdb: 警告: 为本地连接启用"trust"身份验证
local(Unix socket)连接默认配的是 trust,也就是本机通过 socket 连进来不校验密码,这是 initdb 的默认行为,不是错误。
要注意这里的"本地"指的是 socket,127.0.0.1 那条走的是 TCP,认证方式另算。PG16 默认给的是 scram-sha-256,所以等一下用 -h 127.0.0.1 测试的时候反而会要密码------同一个"本地",两种认证方式,第一次见挺容易懵。
想一开始就收紧,可以在 initdb 时加 --auth-local=scram-sha-256,或者事后再改 pg_hba.conf。
四、SELinux 和防火墙:这次关着,但别默认别人也关
这一节先说结论:本次环境 SELinux 是 Disabled、firewalld 也没启用,所以这两处没遇到过问题,下面的步骤都没有执行。
但为什么还要写?因为如果数据目录留在 /var/lib/pgsql/16/data,SELinux 的上下文是发行版配好的,什么都不用管;一旦挪到 /data/app 下面,就是另一回事了------文件属主是对的,但 SELinux 标签还是默认的 default_t,postgres 进程(postgresql_t)读不了,服务会直接起不来。默认配置的 Rocky 9 上这是大概率会中的一枪。
检查状态:
bash
getenforce # SELinux
systemctl is-active firewalld
getenforce 返回 Enforcing 的话,启动失败时日志里一般是这类信息:
text
FATAL: could not open directory "pg_tblspc": Permission denied
或者 systemd 那边直接给个非零退出码、状态 failed。
给自定义目录打 SELinux 上下文:
bash
dnf -y install policycoreutils-python-utils
semanage fcontext -a -t postgresql_db_t "/data/app/postgresql(/.*)?"
restorecon -Rv /data/app/postgresql
restorecon 不能省,semanage fcontext 只是写规则,不执行不生效。如果做完还是起不来,把 /data/app 甚至 /data 也按同样方式标一遍,父目录缺 search 权限同样过不去。
临时验证是不是标签问题:
bash
setenforce 0
如果 setenforce 0 之后服务立刻就起来了,基本可以确认就是这个原因,回去把上面两步做完,再把 setenforce 1 打开复测一遍。生产环境不建议为了省事永久关掉 SELinux------同一台机器上跑的别的服务也在受它保护。
firewalld 的部分放到第八节,跟着远程访问一起说更顺。
五、交给 systemd 管理
PGDG 的 postgresql-16.service 里写死了 PGDATA=/var/lib/pgsql/16/data,我们要覆盖它。
不要直接改 /usr/lib/systemd/system/postgresql-16.service,包升级的时候会被覆盖回去,用 override:
bash
mkdir -p /etc/systemd/system/postgresql-16.service.d
cat > /etc/systemd/system/postgresql-16.service.d/override.conf <<'EOF'
[Service]
Environment=PGDATA=/data/app/postgresql
EOF
让 systemd 重新读一遍配置:
bash
systemctl daemon-reload
设置开机自启并启动:
bash
systemctl enable --now postgresql-16
看状态:
bash
systemctl status postgresql-16

到这一步 Active: active (running) 就基本成了。如果状态是 active 但很快变成 failed,回去看第四节。
六、确认端口和数据目录都对上了
查监听端口:
bash
ss -nltp | grep 5432
Rocky 9 最小化安装是不带 netstat 的(net-tools 要单独装),用 ss 更省事。
刚装完只会监听本地:
text
LISTEN 0 244 127.0.0.1:5432 0.0.0.0:* users:(("postgres",pid=xxxx,fd=5))
LISTEN 0 244 [::1]:5432 [::]:* users:(("postgres",pid=xxxx,fd=5))
说明服务起来了,但还没对外,这是预期的。
确认数据目录是不是真的用上了:
bash
sudo -u postgres psql -c "show data_directory;"
返回 /data/app/postgresql 才算对。如果这里还是 /var/lib/pgsql/16/data,说明 override 没生效,回去检查文件路径和 daemon-reload。
再瞄一眼进程,确认 -D 参数:
bash
ps -ef | grep '[p]ostgres' | head -1
应该能看到类似:
text
/usr/pgsql-16/bin/postgres -D /data/app/postgresql
这两条命令看着重复,但其实查的是两件不同的事------前者是 PostgreSQL 自己认为数据目录在哪,后者是进程实际启动时用的参数。真出问题的时候,两者不一致是最常见的情况。
七、给 postgres 设密码
bash
sudo -u postgres psql
sql
ALTER USER postgres WITH PASSWORD '你的密码';
\q
本机走 TCP 测一下(这一步才会真正校验密码):
bash
psql -h 127.0.0.1 -U postgres -d postgres -W

能进去,说明 scram-sha-256 这条链路是通的。
八、开放远程访问
要让别的机器连过来,三件事都得做:监听地址、pg_hba.conf、防火墙。少任何一样都连不上,而且报错长得都不一样,第九节会对照着说。
8.1 改监听地址
bash
vi /data/app/postgresql/postgresql.conf
注意路径,不是 /var/lib/pgsql/16/data/postgresql.conf,自定义数据目录之后配置文件也跟着搬家了。
找到这一行:
ini
#listen_addresses = 'localhost'
改成:
ini
listen_addresses = '*'
如果这台机器有多块网卡,只希望业务网卡被监听,那就写具体 IP,比 '*' 更保险。
8.2 配 pg_hba.conf
同样在 /data/app/postgresql/ 下面:
bash
vi /data/app/postgresql/pg_hba.conf
只放通 192.168.1.2 这一台:
ini
host all postgres 192.168.1.2/32 scram-sha-256
要放整个网段:
ini
host all postgres 192.168.1.0/24 scram-sha-256
能写 /32 就别写 /24。这条规则是放行语义,范围开多大,库就暴露多大。要是客户端网段不固定、随手写了 0.0.0.0/0,那基本等于把 5432 挂出去了。
另外,pg_hba.conf 的匹配规则是从上往下、第一条命中就生效 ,所以别随手把它塞在文件末尾,最好紧跟在已有的 host 规则后面,顺序自己心里有数。
8.3 放行防火墙
这台机器 firewalld 没启用,所以这一步跳过了。如果目标机器是默认配置(firewalld 开着),就算 PostgreSQL 已经监听在 0.0.0.0:5432,客户端那边还是 Connection timed out。
只给业务服务器放行:
bash
firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="192.168.1.2/32" port protocol="tcp" port="5432" accept'
firewall-cmd --reload
firewall-cmd --list-all
嫌 rich rule 麻烦,也可以 firewall-cmd --permanent --add-port=5432/tcp,但那是无差别放通,内网里也不建议。
8.4 让配置生效
改了 postgresql.conf 里的 listen_addresses,必须重启 ;只改 pg_hba.conf 的话重载就够了:
bash
systemctl reload postgresql-16
重载也可以走 SQL:
bash
sudo -u postgres psql -c "SELECT pg_reload_conf();"
两个都改了就直接:
bash
systemctl restart postgresql-16
九、客户端测试,以及三种报错怎么区分
在 192.168.1.2 上:
bash
psql -h 192.168.1.x -U postgres -d postgres -W
报错 A:
text
致命错误: 没有用于主机 "192.168.1.2", 用户 "postgres",
数据库 "postgres", no encryption 的 pg_hba.conf 记录
看到这句其实是个好消息------数据库已经收到你的连接请求了。防火墙、listen_addresses 都没问题,纯粹是 pg_hba.conf 里没有匹配的规则。
顺便注意里面的 no encryption。PG16 的客户端默认会去协商 SSL,服务端没配 SSL 就回落成明文,日志里就带上这个标记。host 类型的规则同时匹配加密和非加密连接,所以不会因此不匹配;但如果写成 hostssl,明文连接永远命不中,就会一直报这句------这是个挺隐蔽的坑。
补上对应的 host 行,systemctl reload postgresql-16,再连即可。

报错 B:
text
could not connect to server: Connection timed out
这个跟 pg_hba.conf 一点关系都没有,连接根本没到数据库。回去查两件事:firewalld 有没有放行 5432、listen_addresses 是不是还停在 localhost。
报错 C:
text
FATAL: password authentication failed for user "postgres"
规则匹配上了,是密码或者认证方式的问题。pg_hba.conf 里写的是 scram-sha-256,但密码是用 md5 方式存的老密码,或者密码本身输错了,都会是这一条。
这三种报错分别对应网络层、HBA 层、认证层,定位的时候先看是哪一个,能省不少时间。
十、业务程序别用 postgres 超级用户
postgres 是超级用户,业务程序拿它连库,等于给了应用侧删库跑路的能力。建个业务用户和业务库:
bash
sudo -u postgres psql
sql
CREATE USER appuser WITH PASSWORD '业务用户密码';
CREATE DATABASE appdb OWNER appuser ENCODING 'UTF8';
\q
应用侧拿到这些信息就够了:
text
host:192.168.1.x
port:5432
db: appdb
user:appuser
再在 pg_hba.conf 里给它单独一条,只允许业务服务器连业务库:
ini
host appdb appuser 192.168.1.2/32 scram-sha-256
这样就算 appuser 的密码泄了,也连不上 postgres 库,更动不了别的库。数据库名和用户名都写具体值,比 all all 保险得多。
bash
systemctl reload postgresql-16
十一、几个常用参数
装完默认配置能跑,但有几个值是明显偏保守的。以 8GB 内存的机器为例,按下面这套调一下基本够用:
ini
max_connections = 200
shared_buffers = 2GB
work_mem = 8MB
maintenance_work_mem = 512MB
effective_cache_size = 6GB
| 参数 | 默认值 | 建议取值 | 说明 |
|---|---|---|---|
max_connections |
100 | 200 | 按应用连接池的上限给,不是越大越好,每个连接都有固定开销 |
shared_buffers |
128MB | 物理内存的 25% 左右 | 改完必须重启 |
work_mem |
4MB | 8~16MB | 注意这是每个排序/哈希操作能用的内存,不是全局,给太大容易 OOM |
maintenance_work_mem |
64MB | 256~512MB | 建索引、VACUUM 时用,改完重载即可 |
effective_cache_size |
4GB | 物理内存的 50%~75% | 只是给优化器的估算值,不实际占内存 |
改完看情况重启或者重载:
bash
systemctl restart postgresql-16
这块不用一次调到位,上业务之后看实际情况再动更靠谱。
十二、日常会用到的几条命令
服务状态和开机自启:
bash
systemctl status postgresql-16
systemctl is-enabled postgresql-16
端口:
bash
ss -nltp | grep 5432
日志,启动失败的原因都在这儿:
bash
journalctl -u postgresql-16 -n 100 --no-pager
实时跟:
bash
journalctl -u postgresql-16 -f
几个常用查询:
bash
sudo -u postgres psql -c "show data_directory;" # 数据目录
sudo -u postgres psql -c "show listen_addresses;" # 监听地址
sudo -u postgres psql -c "select version();" # 版本
sudo -u postgres psql -l # 库列表
十三、最终的文件布局
数据目录:
text
/data/app/postgresql/
├── base/
├── global/
├── pg_wal/
├── pg_tblspc/
├── postgresql.conf
├── pg_hba.conf
└── ...
systemd override:
text
/etc/systemd/system/postgresql-16.service.d/override.conf
内容:
ini
[Service]
Environment=PGDATA=/data/app/postgresql
十四、验收顺序
改完配置之后建议按这个顺序过一遍,出问题好定位:
bash
systemctl status postgresql-16
ss -nltp | grep 5432
sudo -u postgres psql -c "show data_directory;"
sudo -u postgres psql -c "select version();"
如果开了远程访问,在客户端上再加一步:
bash
psql -h 192.168.1.x -U appuser -d appdb -W
这几条都正常,就说明安装、初始化、自定义数据目录、开机自启、远程访问这一整套都通了。
十五、几个容易踩的坑
- 忘了
dnf -qy module disable postgresql,装出来的包版本不对。 - 数据目录权限给成 755,PostgreSQL 直接拒绝启动,日志只有一句 permission denied。
- 只改了 systemd 的 override 但忘了
daemon-reload,PGDATA没生效,数据跑到默认目录去了。 pg_hba.conf里写成hostssl,明文连接永远匹配不上,一直报 no encryption 那条。- SELinux 处于 Enforcing 且数据目录在
/data下------本次环境已关闭没验证到,但默认配置的机器上大概率会中。 - firewalld 没放行 5432------本次同样未启用,默认配置的机器要留意。
/data如果是单独挂的盘,记得写进/etc/fstab,不然下次重启机器 PGDATA 直接是空的,这个代价最大。