`pg_hardstorage`是一款基于 PostgreSQL **复制协议的持续 WAL 流式**传输备份工具。在生产环境中,需要并行运行两个进程:一个进程`wal stream` 持续接收来自 PostgreSQL 的 WAL 日志,并将每个已完成的 16 MiB 数据段提交到存储库;`backup`另一个进程则按计划(例如每晚)创建基础备份,后续的 WAL 流式传输会以此为基础进行滚动。每日备份 + 持续运行的 WAL 流式传输 = PITR(基于数据段的备份恢复)至任何数据段对齐点。

它通过物理复制协议备份您自己运行的 PostgreSQL 数据库(裸机、虚拟机、容器、Patroni 集群以及 CloudNativePG 等运维工具),对内容寻址数据块进行去重和加密,并使用 PITR 进行恢复。支持 PG 15+ 和 Apache 2.0。

以下从零开始搭建pg_hardstorage备份。

------

## 1. 安装

### Pre-built binary

发布版本以静态文件`linux/{amd64,arm64}`和tar 包的形式提供(Windows 仅支持命令行界面)。从[github.com/cybertec-postgresql/pg_hardstorage/releases](https://github.com/cybertec-postgresql/pg_hardstorage/releases)`darwin/arm64` 获取匹配的版本 ,验证签名,然后将二进制文件放到您的目录中:`$PATH`

```
VERSION=1.0.4   # latest release: https://github.com/cybertec-postgresql/pg_hardstorage/releases/latest
curl -LO "https://github.com/cybertec-postgresql/pg_hardstorage/releases/download/v${VERSION}/pg_hardstorage_${VERSION}_linux_amd64.tar.gz"
tar xzf "pg_hardstorage_${VERSION}_linux_amd64.tar.gz"
sudo install -m 0755 pg_hardstorage /usr/local/bin/
pg_hardstorage version
```

### `.deb`(Debian / Ubuntu)

```
VERSION=1.0.4   # the release you downloaded
sudo dpkg -i "pg-hardstorage_${VERSION}_amd64.deb"
```

该软件包在 处安装二进制文件`/usr/bin/pg_hardstorage`,在 处放置一个 systemd 单元`/lib/systemd/system/pg_hardstorage.service`,并创建`/etc/pg_hardstorage/`,,其模式为 0750 `/var/lib/pg_hardstorage/`, `/var/log/pg_hardstorage/`所有者为`pg-hardstorage`。

### `.rpm`(Fedora / RHEL / Rocky / Alma)

```
VERSION=1.0.4   # the release you downloaded
sudo rpm -i "pg-hardstorage-${VERSION}-1.x86_64.rpm"
```

### Container image

```
VERSION=1.0.4   # latest release tag
docker pull "ghcr.io/cybertec-postgresql/pg_hardstorage:v${VERSION}"
docker run --rm "ghcr.io/cybertec-postgresql/pg_hardstorage:v${VERSION}" version
```

该镜像不包含发行版。请在指定位置挂载一个配置目录`/etc/pg_hardstorage` ,并在指定位置挂载一个状态目录`/var/lib/pg_hardstorage`;这两个目录都必须对用户 ID 65532 具有写入权限。

### From source

```
git clone https://github.com/cybertec-postgresql/pg_hardstorage
cd pg_hardstorage
make                       # produces bin/pg_hardstorage
sudo install -m 0755 bin/pg_hardstorage /usr/local/bin/
```

需要 Go 1.26+.`make test`在竞争检测器下运行完整的单元测试套件;`make test-integration`通过 testcontainers-go 测试真正的 PostgreSQL 17 容器(需要 Docker)。

$ pg_hardstorage version
pg_hardstorage 1.0.4 (6bf20a6, built 2026-06-24T20:10:58Z)

------

## 2. 五分钟快速入门

### 2.1 在 PostgreSQL 上配置复制用户

```
CREATE ROLE pgbackup REPLICATION LOGIN PASSWORD 'pgbackup';
```

添加一行`pg_hba.conf`代码,允许代理主机以该角色进行复制:

```
host  replication  pgbackup  192.192.103.117/32  scram-sha-256
或者
host  replication  pgbackup  0.0.0.0/0  scram-sha-256
```

重新加载 PG(`SELECT pg_reload_conf()`)。

### 2.2 创建存储库

```
pg_hardstorage repo init file:///postgresql/backup/
```

#提前创建本地目录 /postgresql/backup,postgres用户需要赋予目录读写权限,chmod 700

仓库是一个目录(或 S3 对象存储),其中包含代码块、清单和 WAL。一个仓库可以包含多个部署。该命令`repo init`对 URL 是幂等的——针对现有仓库重新运行命令会返回 `conflict.repo_exists`(exit 7)。

S3 的工作原理相同:

```
pg_hardstorage repo init 's3://acme-backups/?region=eu-central-1'
```

其他后端使用相同的格式——选择与存储相匹配的 URL 方案:

| 后端                         | 示例网址                                            |
| ---------------------------- | --------------------------------------------------- |
| 本地文件系统                 | `file:///postgresql/backup`                         |
| AWS S3 / MinIO / R2 / B2     | `s3://acme-backups/?region=eu-central-1`            |
| Google Cloud Storage         | `gcs://acme-backups/`                               |
| Azure Blob                   | `azblob://account.blob.core.windows.net/container/` |
| 通过 SSH(SFTP)远程主机     | `sftp://backup@nas.example.com/srv/backups`         |
| 通过 SSH (ssh-exec) 远程主机 | `scp://backup@nas.example.com/srv/backups`          |

`sftp://`两者`scp://`都使用 SSH;`sftp://`默认情况下选择 SSH,`scp://`当远程服务器禁用 SFTP 子系统时选择 SSH。有关身份验证/已知主机/额外映射设置的相关信息,请参阅 [“添加 SFTP 存储库”](https://github.com/cybertec-postgresql/pg_hardstorage/blob/main/docs/how-to/adding/repository-sftp.md) 和[“添加 SCP 存储库”](https://github.com/cybertec-postgresql/pg_hardstorage/blob/main/docs/how-to/adding/repository-scp.md) 。

### 2.3 验证 PG 是否已准备好进行备份恢复(一次性预检)

`wal stream`每次启动时都会自动运行预检程序,但您可以先单独运行它,以确认源 PostgreSQL 满足复制要求,然后再配置 systemd:

```
pg_hardstorage wal pg_hardstorage wal preflight db1  --pg-connection \ 'postgres://pgbackup:pgbackup@192.192.103.117:5432/postgres'
输出如下:
{
  "deployment": "db1",
  "role": "pgbackup",
  "pg_version_num": 180003,
  "findings": [
    {
      "severity": "info",
      "code": "max_slot_wal_keep_size.unbounded",
      "message": "max_slot_wal_keep_size = -1 (PG default); the slot will retain WAL until the streamer reconnects, even if pg_wal/ fills the partition",
      "suggestion": "pair with a disk-free alert on the partition holding pg_wal/ AND a streamer-lag alert; or set max_slot_wal_keep_size to bound the slot — see docs/how-to/operating/slot-disk-safety.md for the full trade-off",
      "observed": "-1"
    },
    {
      "severity": "info",
      "code": "wal_keep_size",
      "message": "wal_keep_size = 0 (informational; the slot is the primary retention guarantee)",
      "observed": "0"
    }
  ]
}
```

致命错误(`wal_level.too_low`,,,)会导致命令以非零值退出,并且每个错误都会阻塞程序。警告(, 在 PG 17 及以上级别)会显示出来`max_replication_slots.full`, 但不会阻塞程序。`max_wal_senders.saturated``role.no_replication``suggestion:``max_slot_wal_keep_size.set``idle_replication_slot_timeout.set`

### 2.4 启动 WAL 流式传输(这是始终开启的核心)

完成预检清理后,启动 WAL 流。这是其主要功能,`pg_hardstorage`也是需要 24*7 全天候运行的进程:

```
pg_hardstorage wal stream db1 \
    --pg-connection 'postgres://pgbackup:pgbackup@192.192.103.117:5432/postgres' \
    --repo file:///postgresql/backup
输出如下:
08:26:18 [INFO ] wal.preflight.max_slot_wal_keep_size.unbounded
  body: {
          "message": "max_slot_wal_keep_size = -1 (PG default); the slot will retain WAL until the streamer reconnects, even if pg_wal/ fills the partition",
          "observed": "-1",
          "required": "",
          "suggestion": "pair with a disk-free alert on the partition holding pg_wal/ AND a streamer-lag alert; or set max_slot_wal_keep_size to bound the slot — see docs/how-to/operating/slot-disk-safety.md for the full trade-off"
        }
08:26:18 [INFO ] wal.preflight.wal_keep_size
  body: {
          "message": "wal_keep_size = 0 (informational; the slot is the primary retention guarantee)",
          "observed": "0",
          "required": "",
          "suggestion": ""
        }
08:26:19 [INFO ] wal.stream.starting  deployment=db1 timeline=1
  body: {
          "attempt": 1,
          "compression": "",
          "encryption": false,
          "resume_strategy": "fresh-slot-restart-lsn",
          "slot": "pg_hardstorage_db1",
          "start_lsn": "0/85000000",
          "status_interval": "10s",
          "system_id": "7639282777750685031"
        }

08:43:04 [WARNING] wal.stream.reconnecting
  body: {
          "attempt": 1,
          "backoff": "1s",
          "error": "streaming: inactivity timeout (no message from server) (after 5m0s)",
          "reason": "stream_break",
          "synced_lsn": "0/87000000"
        }
08:43:05 [INFO ] wal.stream.starting  deployment=db1 timeline=1
  body: {
          "attempt": 2,
          "compression": "",
          "encryption": false,
          "last_lsn_in_repo": "0/87000000",
          "resume_strategy": "resume-from-repo",
          "slot": "pg_hardstorage_db1",
          "start_lsn": "0/87000000",
          "status_interval": "10s",
          "system_id": "7639282777750685031"
        }
08:48:05 [WARNING] wal.stream.reconnecting
  body: {
          "attempt": 2,
          "backoff": "1s",
          "error": "streaming: inactivity timeout (no message from server) (after 5m0s)",
          "reason": "stream_break",
          "synced_lsn": "0/0"
        }
08:48:06 [INFO ] wal.stream.starting  deployment=db1 timeline=1
  body: {
          "attempt": 3,
          "compression": "",
          "encryption": false,
          "last_lsn_in_repo": "0/87000000",
          "resume_strategy": "resume-from-repo",
          "slot": "pg_hardstorage_db1",
          "start_lsn": "0/87000000",
          "status_interval": "10s",
          "system_id": "7639282777750685031"
        }
...................

当前该进程是前台运行,如果终止则服务停止,可以转为后台运行,如下:
方法1:使用nohup(临时/轻量级)
nohup pg_hardstorage wal stream db1 \
     --pg-connection 'postgres://pgbackup:pgbackup@192.192.103.117:5432/postgres' \
     --repo file:///postgresql/backup \
     >> /postgresql/backup/wal_stream.log 2>&1 &

方法2:创建systemd服务(生产环境最佳实践)
1. 创建服务文件
使用 root 用户或 sudo 权限创建 /etc/systemd/system/pg_hardstorage-wal.service:
[Unit]
Description=PostgreSQL Hardstorage WAL Stream for db1
After=network.target postgresql.service

[Service]
Type=simple
User=postgres
Group=postgres
ExecStart=/usr/bin/pg_hardstorage wal stream db1 \
    --pg-connection 'postgres://pgbackup:pgbackup@192.192.103.117:5432/postgres' \
    --repo file:///postgresql/backup
Restart=always
RestartSec=5
# 限制日志输出,防止占满系统盘
StandardOutput=journal
StandardError=journal
[Install]
WantedBy=multi-user.target

2. 启动并启用服务
# 重新加载 systemd 配置
sudo systemctl daemon-reload

# 启动服务
sudo systemctl start pg_hardstorage-wal

# 设置开机自启
sudo systemctl enable pg_hardstorage-wal
3. 查看日志与状态
# 查看运行状态
sudo systemctl status pg_hardstorage-wal

# 实时查看流传输日志
journalctl -u pg_hardstorage-wal -f
```

`CREATE_REPLICATION_SLOT pg_hardstorage_db1 PHYSICAL RESERVE_WAL`如果槽位不存在,代理会发出请求——在创建时立即`RESERVE_WAL`锁定槽位`restart_lsn`,因此 PG 从那时起就保留 WAL 日志。然后它会 `START_REPLICATION SLOT pg_hardstorage_db1 PHYSICAL`针对该槽位发出请求。代理重启后,数据流不会中断。可以使用 systemd或容器调度器来监控它。

`--skip-preflight`如果已经审核过 PG,则此方法是显式覆盖;`--no-slot`对于仅用于归档的部署,此方法是显式逃生舱口,它通过另一种机制保证 WAL 保留(两者都会发出响亮的警告——使用其中任何一种都是有意为之)。

让它继续运行。剩余步骤将在另一个终端或单独的调度程序下运行。

### 2.5 采取第一次基础备份

在streamer进程并发运行的情况下,进行基础备份。这两个进程共享存储库 URL,但除此之外没有其他协调——`backup streamer进程`BASE_BACKUP`通过自身的复制连接传输数据,同时`wal stream`继续发送 WAL 日志。

向导程序会探测 PG,生成签名密钥对和 KEK,写入数据 `pg_hardstorage.yaml`,并(默认情况下)进行第一次备份:

```
pg_hardstorage init \
     --pg-connection 'postgres://pgbackup:pgbackup@192.192.103.117:5432/postgres' \
     --repo file:///postgresql/backup \
     --deployment db1 \
     --yes
输出内容如下:
08:30:38 [INFO ] init.probe.starting
  body: {
          "deployment": "db1"
        }
08:30:38 [NOTICE] init.probe.ok
  body: {
          "pg_version": 18,
          "system_id": "7639282777750685031",
          "timeline": 1
        }
08:30:38 [NOTICE] init.repo.ready
  body: {
          "repo_id": "9bc618d1f252be3ba8f3c6422d3c3c55",
          "url": "file:///postgresql/backup"
        }
08:30:39 [NOTICE] init.kek.ready
  body: {
          "generated": true,
          "path": "/home/postgres/.config/pg_hardstorage/keyring/kek.bin"
        }
08:30:39 [NOTICE] init.config.written
  body: {
          "deployment": "db1"
        }
08:30:39 [INFO ] init.backup.starting
  body: {
          "deployment": "db1"
        }
08:43:58 [NOTICE] init.backup.ok
  body: {
          "backup_id": "db1.full.20260625T083040Z.68ac",
          "duration_ms": 799160
        }
✓ pg_hardstorage initialized
  Deployment:   db1
  PostgreSQL:   18
  Cluster ID:   7639282777750685031
  Timeline:     1
  Repository:   file:///postgresql/backup
  Config:       /home/postgres/.config/pg_hardstorage/pg_hardstorage.yaml
  Keyring:      /home/postgres/.config/pg_hardstorage/keyring
  Encryption:   AES-256-GCM (KEK generated at /home/postgres/.config/pg_hardstorage/keyring/kek.bin)

  First backup: db1.full.20260625T083040Z.68ac
    logical:    1.3 GiB
    duration:   799160ms

Next steps:
  1. Start the agent (drives scheduled backups + retention):
       pg_hardstorage agent
  2. Continuously archive WAL for PITR:
       pg_hardstorage wal stream db1 --pg-connection ... --repo file:///postgresql/backup
  3. Inspect deployment health:
       pg_hardstorage doctor db1
```

要稍后进行备份而无需通过向导(这是调度程序每晚运行的命令):

```
pg_hardstorage backup db1 \
     --pg-connection 'postgres://pgbackup:pgbackup@192.192.103.117:5432/postgres' \
     --repo file:///postgresql/backup
输出内容如下:
08:48:41 [INFO ] backup.dedup_hints_loaded  deployment=db1
  body: {
          "hint_chunks": 20698
        }
08:48:42 [INFO ] backup.pg_probed
  body: {
          "pg_version": 18,
          "raw": "18.3"
        }
08:48:42 [INFO ] backup.identified
  body: {
          "system_id": "7639282777750685031",
          "timeline": 1,
          "xlogpos": "0/870001A0"
        }
08:48:42 [INFO ] backup.started  tenant=default deployment=db1 backup_id=db1.full.20260625T084842Z.2dd5
08:48:57 [INFO ] backup.stream_complete
  body: {
          "bytes_received": 1422521710,
          "messages": 46080,
          "tablespaces": 1
        }
08:49:03 [WARNING] backup.essential_files_unchecked  deployment=db1 backup_id=db1.full.20260625T084842Z.2dd5
  body: {
          "error": "backup: probe config locations: pg: read data_directory: ERROR: permission denied to examine \"data_directory\" (SQLSTATE 42501)",
          "hint": "could not query data_directory / config_file from source PG; skipping the pre-commit essential-files check"
        }
08:49:05 [INFO ] backup.committed  tenant=default deployment=db1 backup_id=db1.full.20260625T084842Z.2dd5 timeline=1 lsn=0/88000158
  body: {
          "chunks_deduped": 21160,
          "chunks_written": 2,
          "dedup_hit_rate": 0.9999054909743881,
          "duration_ms": 24044,
          "file_count": 1782,
          "logical_bytes": 1421272834,
          "primary_key": "manifests/db1/backups/db1.full.20260625T084842Z.2dd5/manifest.json",
          "total_chunk_refs": 21162,
          "unique_chunk_bytes": 1409386943,
          "unique_chunk_count": 20698
        }
✓ Backup committed
  ID:               db1.full.20260625T084842Z.2dd5
  Deployment:       db1
  PostgreSQL:       18
  Cluster ID:       7639282777750685031
  Stop LSN / TLI:   0/88000158 / 1
  Files:            1782 in 1 tablespace(s)
  Logical bytes:    1.3 GiB
  Unique chunks:    20698 (1.3 GiB after dedup)
  Dedup ratio:      1.01x
  Duration:         24044 ms
  Encryption:       AES-256-GCM (per-backup DEK, wrapped under local KEK)
  Manifest:         manifests/db1/backups/db1.full.20260625T084842Z.2dd5/manifest.json
```

生产环境中:调度`backup`(cron / systemd 定时器 / Kubernetes CronJob),监控`wal stream`(systemd / Kubernetes Deployment)。基础备份是周期性锚点;流式传输器确保 PITR 在锚点之间达到字节级精确同步。

### 2.6 恢复    

```
pg_hardstorage restore db1 latest \
     --target /postgresql/restored \
     --repo file:///postgresql/backup
输出内容如下:
08:50:18 [INFO ] restore.manifest_loaded  tenant=default deployment=db1 backup_id=db1.full.20260625T084842Z.2dd5 timeline=1 lsn=0/88000158
  body: {
          "file_count": 1782,
          "pg_version": 18,
          "tablespaces": 1
        }
08:50:18 [INFO ] restore.started  deployment=db1 backup_id=db1.full.20260625T084842Z.2dd5
  body: {
          "resumed": false,
          "target": "/postgresql/restored"
        }
09:00:47 [INFO ] restore.verifybackup_ok  deployment=db1 backup_id=db1.full.20260625T084842Z.2dd5
  body: {
          "algorithm": "CRC32C",
          "bytes_hashed": 1421273066,
          "files_checked": 1783
        }
09:00:49 [INFO ] restore.auto_recovery_armed  deployment=db1 backup_id=db1.full.20260625T084842Z.2dd5
  body: {
          "recovery_target": "immediate",
          "restore_command": "wired",
          "signal": "standby.signal"
        }
```

PITR 通过自然语言时间:

--恢复预览:

  [postgres@pg117 ~]$ pg_hardstorage restore db1 latest     --target /postgresql/restored     --repo file:///postgresql/backup     --to "5 minutes ago"  --preview                                                                           

Restore plan (preview only — no files written)                                                                                                                                                                                          
    Backup:           db1.full.20260626T071735Z.b2be                                                                                                                                                                                    
    Deployment:       db1                                                                                                                                                                                                                    
    Target:           /postgresql/restored                                                                                                                                                                                                   
    PostgreSQL:       18                                                                                                                                                                                                                     
    Cluster ID:       7639282777750685031                                                                                                                                                                                                    
    Backup stop LSN:  0/A2000158 (TLI 1)                                                                                                                                                                                                     
    Recovery target:  time 2026-06-29T07:40:33.510854269Z (inclusive=true)   
    On target reached: pause                                                                                                                                                                                                                 
    Recovery TLI:     latest                                                                                                                                                                                                                 
    Files:            1787                                                                                                                                                                                                                   
    Total bytes:      1.3 GiB                                                                                                                                                                                                                
    Chunk refs:       21166 (20702 unique, 1.3 GiB after dedup) 
    backup_label:     232 bytes                                                                                                                                                                                                              
    Estimated RTO:    13554 ms (assuming 100.0 MiB/s)   
    Pre-flight:       ✗ 1 issue(s)                                                                                                                                                                                                           

--开始恢复:

pg_hardstorage restore db1 latest \
     --target /postgresql/restored5 \
     --repo file:///postgresql/backup \
     --to "5 minutes ago
输出内容如下:
09:03:36 [INFO ] restore.manifest_loaded  tenant=default deployment=db1 backup_id=db1.full.20260625T084842Z.2dd5 timeline=1 lsn=0/88000158
  body: {
          "file_count": 1782,
          "pg_version": 18,
          "tablespaces": 1
        }
09:03:36 [INFO ] restore.started  deployment=db1 backup_id=db1.full.20260625T084842Z.2dd5
  body: {
          "resumed": false,
          "target": "/postgresql/restored5"
        }
09:14:19 [INFO ] restore.verifybackup_ok  deployment=db1 backup_id=db1.full.20260625T084842Z.2dd5
  body: {
          "algorithm": "CRC32C",
          "bytes_hashed": 1421273066,
          "files_checked": 1783
        }
09:14:20 [INFO ] restore.recovery_armed  deployment=db1 backup_id=db1.full.20260625T084842Z.2dd5
  body: {
          "action": "pause",
          "inclusive": true,
          "target_lsn": "",
          "target_name": "",
          "target_time": "2026-06-25T08:58:36Z",
          "timeline": "latest"
        }

或者恢复到特定的 LSN:。`--to-lsn 0/3000028`或者恢复到指定的还原点:`--to-name pre_release`。

恢复操作会写入一个托管块`recovery.signal`,并在 `postgresql.auto.conf`其中`restore_command`调用 `pg_hardstorage wal fetch <deployment> %f %p --repo ...`。启动 PG;恢复过程继续进行。

`pg_verifybackup`在恢复操作声明成功之前,会先对数据目录进行检查。只有在您清楚自己在做什么的情况下才跳过此检查——`--verify=skip`退出代码,表示验证程序判定为“否”。

### 2.7 备份状态查看

[postgres@pg117 ~]$ pg_hardstorage status db1  

DEPLOYMENT  BACKUPS  HEALTH  LATEST                          AGE  STOP LSN    TLI  STREAMS  

db1         5        ✓       db1.full.20260626T071735Z.b2be  3d   0/A2000158  1    -         

  Audit anchor: ⚠ none (6 event(s) un-anchored — run `pg_hardstorage audit anchor`)  
  Pending approvals: 0                                                                                                                                                                                                                     
                                                                                                                                                                                                                                           
[postgres@pg117 ~]$ pg_hardstorage list db1                                                                                           

Backups for db1 (5):                                                                                                                                                                                                                       
  BACKUP ID                       TYPE  WHEN              FILES  SIZE     DEDUP  DURATION

  db1.full.20260626T071735Z.b2be  full  2026-06-26 07:17  1787   1.3 GiB  1.01x  10805ms                                                                                                                                                    db1.full.20260625T094450Z.9826  full  2026-06-25 09:45  1785   1.3 GiB  1.01x  18790ms                                                                                                                                                   db1.full.20260625T092448Z.25f6  full  2026-06-25 09:25  1782   1.3 GiB  1.01x  13424ms                                                                                                                                                   db1.full.20260625T084842Z.2dd5  full  2026-06-25 08:48  1782   1.3 GiB  1.01x  15683ms                                                                                                                                                   db1.full.20260625T083040Z.68ac  full  2026-06-25 08:31  1782   1.3 GiB  1.01x  27398ms 

[postgres@pg117 ~]$ pg_hardstorage show  db1 db1.full.20260625T084842Z.2dd5
Backup db1.full.20260625T084842Z.2dd5                                                                                                                                                                                                       Deployment     db1                                                                                                                                                                                                                       
  Type           full                                                                                                                                                                                                                      
  PostgreSQL     18                                                                                                                                                                                                                        
  Cluster ID     7639282777750685031                                                                                                                                                                                                       
  Start LSN      0/88000028                                                                                                                                                                                                                
  Stop LSN       0/88000158                                                                                                                                                                                                                
  Timeline       1                                                                                                                                                                                                                         
  Started        2026-06-25 08:48:42 UTC                                                                                                                                                                                                   
  Stopped        2026-06-25 08:48:57 UTC                                                                                                                                                                                                   
  Duration       15683 ms                                                                                                                                                                                                                  
  Compression    zstd                                                                                                                                                                                                                      
  Tablespaces    1                                                                                                                                                                                                                         
    oid=0                                                                                                                                                                                                                                  
  Files          1782                                                                                                                                                                                                                      
  Logical bytes  1.3 GiB                                                                                                                                                                                                                   
  Unique chunks  20698 (1.3 GiB)                                                                                                                                                                                                           
  Dedup ratio    1.01x                                                                                                                                                                                                                     
  backup_label   232 bytes                                                                                                                                                                                                                 
  Signature      ed25519 / fingerprint 25aeb62195860b7b

`status`返回 RPO(当前时间 - 最近一次备份完成时间)、WAL 延迟和下次计划运行时间。`show`转储完整清单,包括 LSN 范围、时间线、去重率、加密信封详细信息以及已写入的验证记录。

### 2.8 留存率

默认策略:

deployments:
  db1:
    retention:
      policy: gfs
      keep_daily: 7
      keep_weekly: 4
      keep_monthly: 12
      keep_yearly: 5

[postgres@pg117 ~]$ pg_hardstorage rotate db1 

Rotation plan (dry-run — no manifests soft-deleted)   

  Policy: gfs  

  db1                                                                                                                                                                                                                                      
    keep:    2                                                                                                                                                                                                                             
    delete:  3                                                                                                                                                                                                                             
      [keep] db1.full.20260626T071735Z.b2be @ 2026-06-26T07:17:46Z  (daily-1,weekly-1,monthly-1,yearly-1)   

​      [keep] db1.full.20260625T094450Z.9826 @ 2026-06-25T09:45:08Z  (daily-2)   

​      [del ] db1.full.20260625T092448Z.25f6 @ 2026-06-25T09:25:02Z    

​      [del ] db1.full.20260625T084842Z.2dd5 @ 2026-06-25T08:48:57Z    

​      [del ] db1.full.20260625T083040Z.68ac @ 2026-06-25T08:31:07Z 

------

## 3. 验证安装

`doctor`是单命令,检查“是否存在任何问题”:

$ pg_hardstorage doctor
Mode: user

PATHS
  Config         /home/postgres/.config/pg_hardstorage              [xdg] exists drwx------
    drop-in      /home/postgres/.config/pg_hardstorage/conf.d       [derived] missing
    deployments  /home/postgres/.config/pg_hardstorage/deployments  [derived] missing
    sinks        /home/postgres/.config/pg_hardstorage/sinks        [derived] missing
    skills       /home/postgres/.config/pg_hardstorage/skills       [derived] missing
    keyring      /home/postgres/.config/pg_hardstorage/keyring      [derived] exists drwx------
  State          /home/postgres/.local/share/pg_hardstorage         [xdg] missing
    bookkeeping  /home/postgres/.local/share/pg_hardstorage/bookkeeping [derived] missing
    inflight     /home/postgres/.local/share/pg_hardstorage/inflight [derived] missing
    crashes      /home/postgres/.local/share/pg_hardstorage/crashes [derived] missing
  Cache          /home/postgres/.cache/pg_hardstorage               [xdg] missing
  Logs           /home/postgres/.local/state/pg_hardstorage         [xdg] missing
  Runtime        /run/user/1001/pg_hardstorage                      [xdg] missing
  Shared data    /home/postgres/.local/share/pg_hardstorage/share   [xdg] missing

CONFIG
  Status:        configured
  Schema:        pg_hardstorage.config.v1
  Deployments:   1 (db1)
    db1                  class: internal
  Sinks:         0
  Source files:
    [loaded ] /home/postgres/.config/pg_hardstorage/pg_hardstorage.yaml

KEYSTORE
  Dir:           /home/postgres/.config/pg_hardstorage/keyring
  Signing key:   ✓ present
  KEK:           ✓ present (encryption ON by default for new backups)

AIRGAP
  Mode:          off

REPOS
  file:///postgresql/backup — reachable
    audit chain: 6 event(s)
    anchor: ✗ NONE (chain has events, no transparency anchor)

ISSUES
  [WARNING] audit.anchor_missing: doctor: 6 audit event(s) at file:///postgresql/backup but no transparency-log anchor; run `pg_hardstorage audit anchor`
    hint: run `pg_hardstorage audit anchor --repo <url>` (or wire periodic anchoring via the agent's audit_anchor schedule)

任何一行`✗`都是一个需要修正的建议。运行 `pg_hardstorage doctor -o json`生成机器可读格式。

`doctor`正常时退出代码为 0,`--exit-on-issues`发现问题时退出代码为 10 — 如果想要一个硬性失败信号,可将其连接到警报系统中。

更多推荐