教程 搜索 / 发布与基础设施 / 在服务器上安装 Postgres 和 Redis
📝 文字 ● 中级 更新于 2026-05-13

在服务器上安装 Postgres 和 Redis

两者的默认安装配置在笔记本上很方便,但在公网服务器上却相当危险。本教程介绍安装流程、将其变得安全可用的身份验证与绑定调整,以及何时应该放弃自托管、转而选择托管服务的客观分析。

"自托管还是托管服务"是比敲安装命令更重要的前置问题。托管 Postgres(AWS RDS、Supabase、Neon、Crunchy)和托管 Redis(ElastiCache、Upstash)每 GB 的费用更高,但它们提供备份、故障转移、版本升级、监控以及与 IAM 集成的身份验证,无需你自己跑定时任务。在同一台 VPS 上自托管几乎不花钱,对个人项目来说完全够用;一旦宕机会造成损失,或者数据集大到你不希望丢失,这种方式就不再适合了。诚实的判断标准是:"数据库升级期间六小时的停机可以接受吗?"——如果不行,就选托管服务。

本教程涉及的两个服务在默认安全性上各有不同。Postgres 在 Ubuntu 上的默认配置绑定到 127.0.0.1,并使用 peer 认证(Unix 用户名必须与 Postgres 角色匹配),这在默认情况下确实是安全的。Redis 的默认配置同样绑定到本地,但如果你修改了绑定地址却没有设置密码,就会出现最糟糕的情况:一个暴露在公网 IP 上的 Redis 实例——这是互联网上被利用最多的错误配置之一。两者的配置步骤也因此有所不同:Postgres 只需为你的应用创建数据库和用户;Redis 则必须在触碰任何网络相关设置之前先配置好身份验证。

本教程假设你的服务器已经完成了基础安全加固清单——非 root 用户、sudo 权限、防火墙开启、仅 SSH 密钥登录。没有这些基础,再多的数据库配置也帮不上忙。完成本教程的所有步骤后,你将拥有:一个配有应用专属用户的 Postgres 数据库、一个启用了身份验证的 Redis 实例,两者均只绑定到本地(应用与数据库在同一台机器),以及一个用于备份重要数据的脚本。

你将学到什么

前置条件:一台经过安全加固的 Ubuntu 22.04 或 24.04 服务器(清单见此)、以具有 sudo 权限的非 root 用户访问的 shell、运行在同一台机器上的应用(或者你愿意完成可选章节中的网络绑定配置)。如果数据库需要与应用分开部署在不同机器上,安全配置会复杂很多——这种情况会在每个安装步骤的末尾单独说明。

步骤一:决策——自托管还是托管服务?

1

"六小时停机可以接受吗?"这个问题

适合自托管的情况:

  • 个人项目、兴趣网站,或早期阶段的低流量 SaaS。
  • 你宁愿每月花 $0 自己跑备份脚本,也不想每月多花 $20+ 用托管实例。
  • 数据集在几 GB 以内,即使发生灾难性情况也能从备份重建。

适合选择托管服务的情况:

  • 你有付费用户,他们的数据必须能经受单机故障的考验。
  • 你不想在凌晨三点跑时间点恢复脚本。
  • 你希望主版本升级不需要自己安排维护窗口。
  • 你已经在 AWS / GCP 上,同区域托管实例的延迟与本地实例完全相当。

小规模的大概费用:Supabase 免费套餐(Postgres)支持最多 500 MB 的项目;Upstash 免费 Redis 支持 256 MB。超出后,最便宜的托管 Postgres 约 $10–25/月;最便宜的托管 Redis 约 $5–10/月。在已有的 VPS 上自托管则是免费的。

步骤二:安装 Postgres

2

一条 apt 命令,然后验证

sudo apt update
sudo apt install -y postgresql postgresql-contrib

# Postgres 会自动启动。验证:
sudo systemctl status postgresql
# Active: active (exited) 是正常现象——包装单元显示 "exited",
# 但实际集群(postgresql@<ver>-main.service)正在运行。

# 检查安装的版本:
psql --version

Ubuntu 22.04 附带 Postgres 14;Ubuntu 24.04 附带 Postgres 16。如需更新版本,可添加官方 Postgres apt 源(apt.postgresql.org)并改为安装 postgresql-17。以下配置步骤适用于任何 >= 12 的版本。

步骤三:创建应用角色和数据库

3

每个应用一个角色、一个数据库;永远不要使用 postgres 超级用户

Postgres 自带一个 Unix 用户 postgres,通过 "peer" 认证拥有完整的数据库超级用户权限——你的 Unix 用户名必须匹配。切换到该用户以管理集群:

sudo -u postgres psql

你现在以超级用户身份进入了 psql 提示符。为你的应用创建一个角色和数据库:

-- 替换 mybrand_app 和密码。
CREATE ROLE mybrand_app WITH LOGIN PASSWORD 'long-random-password-from-pwgen';
CREATE DATABASE mybrand_prod OWNER mybrand_app;

-- 验证:
\du
\l
\q

使用 pwgen -s 32 1openssl rand -base64 24 在本地生成密码——要长、要随机,存入你的密码管理器。应用将通过环境变量使用它,绝不手动输入。

步骤四:在 pg_hba.conf 中配置身份验证

4

Postgres 用什么方式验证"是谁在发请求"

Ubuntu 上默认的 pg_hba.conf 有以下几条关键规则(已简化):

# Unix socket(本地管道,非网络)——peer 认证(Unix 用户必须与角色匹配)
local   all             postgres                                peer
local   all             all                                     peer

# 来自 127.0.0.1 的 TCP——scram-sha-256(密码认证)
host    all             all             127.0.0.1/32            scram-sha-256
host    all             all             ::1/128                 scram-sha-256

对于在同一台机器上通过 127.0.0.1:5432 连接的应用,这已经是正确的配置——应用使用步骤三中的密码进行认证,无需修改任何内容。

如果你的应用通过 Unix socket(无密码)连接,则将 local all all peer 这行改为:

local   all             mybrand_app                             scram-sha-256
local   all             all                                     peer

这表示:应用角色即使在 Unix socket 上也使用密码认证;其他人使用 peer 认证。修改此文件后需重新加载 Postgres:sudo systemctl reload postgresql

应用连接字符串: postgresql://mybrand_app:<password>@127.0.0.1:5432/mybrand_prod。将其放入应用读取的 .env 文件,不要提交到代码仓库。在 Node 中:process.env.DATABASE_URLpg 驱动会自动解析该 URL。

步骤五:设置定时备份

5

没有备份的数据库,就是你根本没有的生产数据库

通过 pg_dump 进行每日快照,压缩后传输到 S3(或任何对象存储)。保存为 /usr/local/bin/pg-backup.sh

#!/usr/bin/env bash
set -e
DB=mybrand_prod
TIMESTAMP=$(date +%Y%m%d-%H%M%S)
BACKUP_DIR=/var/backups/postgres
S3_BUCKET=s3://mybrand-backups/postgres

mkdir -p "$BACKUP_DIR"
sudo -u postgres pg_dump -Fc "$DB" | gzip > "$BACKUP_DIR/$DB-$TIMESTAMP.dump.gz"

# 传输到 S3。请先以部署用户身份配置 AWS CLI(aws configure)。
aws s3 cp "$BACKUP_DIR/$DB-$TIMESTAMP.dump.gz" "$S3_BUCKET/$DB-$TIMESTAMP.dump.gz"

# 本地保留 14 天;S3 保留策略通过存储桶的生命周期规则设置。
find "$BACKUP_DIR" -type f -mtime +14 -delete

对脚本执行 chmod +x。以部署用户身份添加 cron 任务:

crontab -e
# 添加:
30 3 * * * /usr/local/bin/pg-backup.sh >> /var/log/pg-backup.log 2>&1

更重要也更难的第二步:实际演练一次恢复流程。在另一台机器上,用 aws s3 cp 下载一份最近的备份,gunzip 解压,pg_restore 还原,验证行数是否正确。从未实际恢复过的备份只是一种期望,而不是真正的备份。

步骤六:安装 Redis

6

安装的同时立即设置身份验证

sudo apt install -y redis-server
# 服务会自动启动,默认绑定到 127.0.0.1。很好。

# 在做任何网络相关操作之前,先设置密码。
# 生成一个密码:
REDIS_PASSWORD=$(openssl rand -base64 24)
echo "$REDIS_PASSWORD"
# 保存到你的密码管理器。稍后也需要将其设置为应用的环境变量。

编辑 Redis 配置:

sudo nano /etc/redis/redis.conf

# 找到并设置:
requirepass <THE_PASSWORD_FROM_ABOVE>
# (确保这行开头没有 #。)

# 确认 bind 行为:
bind 127.0.0.1 -::1
# (这是默认值。除非你绝对确定要将 Redis 暴露到网络,
#  并且防火墙和身份验证都已正确配置,否则不要改为 0.0.0.0。)

# 在生产环境中禁用 COMMAND 命令(可选,偏执级别):
rename-command CONFIG ""
rename-command FLUSHALL ""

重新加载:

sudo systemctl restart redis-server
redis-cli
# 在 redis-cli 中:
AUTH <the password>
PING    # → PONG
exit

应用的连接字符串:redis://default:<password>@127.0.0.1:6379。(只设置 requirepass 时,default 是隐式用户名;如果你使用 Redis ACL 并配置了命名用户,用户名就很重要了。)

不要在没有 TLS + ACL 的情况下将 Redis 暴露到公网。公网 IP 上只有 requirepass 保护的 Redis 会被迅速暴力破解。如果应用和 Redis 在不同机器上,请使用私有 VPC IP、建立 SSH 隧道,或使用带客户端证书的 TLS(Redis 6+)。对大多数项目来说:直接将 Redis 和应用放在同一台机器上,并绑定到 127.0.0.1,是最简单可靠的方案。

步骤七:选择 Redis 持久化模式

7

RDB 做快照,AOF 做写日志,或者纯缓存模式不持久化

Redis 有三种使用方式,各有不同的持久化方案:

  • 纯缓存模式——会话、查询缓存、可以重新生成的临时数据。禁用持久化以避免磁盘开销:在 redis.conf 中设置 save "",并设置 appendonly no。重启后 Redis 从空状态开始,由应用重新填充数据。
  • 快照持久化(RDB,默认)——Redis 按计划将二进制快照写入磁盘。默认的 save 3600 1 300 100 60 10000 含义是:"在一小时内有 1 次变更、5 分钟内有 100 次变更,或 1 分钟内有 10000 次变更时写入磁盘。"恢复时会丢失最后一次快照之后的数据。适合"希望数据能在重启后保留,但可以接受丢失几分钟数据"的场景。
  • 追加日志(AOF)——Redis 将每条命令写入日志;重启时重放日志。在 redis.conf 中设置 appendonly yes。恢复时最多丢失 1 秒的命令(默认 fsync 策略为 everysec)。当 Redis 是主要数据存储而非缓存时使用。

对大多数应用缓存:RDB 默认设置就够了,无需修改。对于会话存储或队列:同时设置 appendonly yes。重新加载:sudo systemctl restart redis-server

步骤八:端对端验证整个链路

8

从应用端到数据库端,全链路验证

# 验证 Postgres 连接(替换为实际的凭据):
psql "postgresql://mybrand_app:[email protected]:5432/mybrand_prod" -c "SELECT version();"

# 验证 Redis 连接:
redis-cli -a "REDIS_PASSWORD" ping    # → PONG

# 验证防火墙没有泄漏(从你的笔记本执行,而不是服务器):
nc -zv <SERVER_IP> 5432    # → Connection refused(正常——Postgres 未对外开放)
nc -zv <SERVER_IP> 6379    # → Connection refused(正常——Redis 未对外开放)

如果从笔记本执行 nc成功连接到其中某个端口,说明有问题——该服务绑定到了 0.0.0.0,或者防火墙缺少拒绝规则。请立即修复;暴露的 Redis 会在数小时内被利用。

何时该放弃自托管

数据库已达到多 GB 规模且还在增长。备份开始无法在夜间时间窗口内完成。PITR(时间点恢复)开始变得重要。将 Postgres 迁移到 RDS(AWS)、Supabase(Postgres 风格的开发体验)或 Neon(无服务器,低流量时费用很低)。

你需要复制或故障转移能力。手动配置 Postgres 流复制或 Redis Sentinel 是一个真实的工程项目。托管实例只需一个配置开关。

同一个数据库上跑着多个应用。当多个服务争抢连接时,单 VPS 的模式就不够用了。迁移到专用的托管实例,让你的应用服务器保持简单。

运维负担令人痛苦。如果"Postgres 在凌晨两点磁盘满了"发生过一次,它还会再发生。托管实例能自动扩展磁盘容量,并主动发出告警。

下一步