
Postgres Sync
- 1 installs
- 2 repo stars
- Updated July 9, 2026
- idenpin/ctc-fe-skills
Syncs a remote PostgreSQL database (test/staging) to a local instance using pg_dump custom format and pg_restore, with local DB drop and rebuild.
About
Mirrors a remote PostgreSQL database (test or staging) to a local instance by dropping and rebuilding the local DB, then dumping with pg_dump custom format and restoring with pg_restore. A developer uses it to refresh their local database from a shared server, with a warning that it destroys the local target.
- Drops and rebuilds the local DB, then dumps and restores from remote
- Uses pg_dump custom format with FORCE drop and template0/UTF8 rebuild
Postgres Sync by the numbers
- 1 all-time installs (skills.sh)
- Ranked #770 of 911 Databases skills by installs in the Skillselion catalog
- Data as of Jul 10, 2026 (Skillselion catalog sync)
npx skills add https://github.com/idenpin/ctc-fe-skills --skill postgres-syncAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 1 |
|---|---|
| repo stars | ★ 2 |
| Last updated | July 9, 2026 |
| Repository | idenpin/ctc-fe-skills ↗ |
What it does
Syncs a remote PostgreSQL database (test/staging) to a local instance using pg_dump custom format and pg_restore, with local DB drop and rebuild.
Files
PostgreSQL 数据库同步
把远程 PostgreSQL 数据库整库同步到本地:删除本地库 → 重建空库 → 远程 dump → 本地 restore → 校验。
何时使用
当用户提出以下需求时触发:
- 同步 / 复制 / 刷新 / 拉取 一个远程(测试 / 预发 / staging)PostgreSQL 数据库到本地
- 把测试服的 xx 数据库覆盖到本地
- 用 pg_dump / pg_restore 在两台 PG 间复制数据
前置条件
- 本机已安装
psql/pg_dump/pg_restore(macOS:brew install libpq && brew link --force libpq) - 本地 PostgreSQL 已启动且可连
- 本地账号具备
SUPERUSER或CREATEDB权限 - 远程 PG 的
pg_hba.conf允许本机访问,且监听了对外地址
必要参数
执行前请先与用户确认。优先从项目 Spring Boot 配置(如 application-dev.yaml、application-local-pg.yaml)读取。
| 参数 | 示例 |
|---|---|
| 远程主机 | 192.168.5.170 |
| 远程端口 | 5432 |
| 远程用户 | postgres |
| 远程密码 | jointsky@123.com |
| 远程库名 | ruoyi-vue-pro |
| 本地用户 | root |
| 本地密码 | com.ctcUniapp |
| 本地库名 | ruoyi-vue-pro |
推荐流程:直接调用脚本
脚本封装了完整流程,是首选方式:
bash .qoder/skills/postgres-sync/scripts/sync.sh \
--remote-host 192.168.5.170 \
--remote-port 5432 \
--remote-user postgres \
--remote-pass 'jointsky@123.com' \
--remote-db ruoyi-vue-pro \
--local-user root \
--local-pass 'com.ctcUniapp' \
--local-db ruoyi-vue-pro可选参数:
--local-host/--local-port(默认127.0.0.1/5432)--keep-dump:保留中间 dump 文件/tmp/<db>-<时间戳>.dump--verify-tables system_users,system_menu,system_role,system_dept:附加行数校验--dry-run:只打印命令不执行
手动流程(脚本不可用时)
按顺序执行:
# 1. 测试远程连通性
PGPASSWORD='<REMOTE_PASS>' psql -h <REMOTE_HOST> -p <REMOTE_PORT> -U <REMOTE_USER> -d <REMOTE_DB> -c "SELECT version();"
# 2. 强制删除并重建本地库
PGPASSWORD='<LOCAL_PASS>' psql -U <LOCAL_USER> -d postgres \
-c 'DROP DATABASE IF EXISTS "<LOCAL_DB>" WITH (FORCE);'
PGPASSWORD='<LOCAL_PASS>' psql -U <LOCAL_USER> -d postgres \
-c 'CREATE DATABASE "<LOCAL_DB>" TEMPLATE template0 ENCODING '\''UTF8'\'' OWNER <LOCAL_USER>;'
# 3. 从远程 dump(自定义格式,去掉 owner / acl)
PGPASSWORD='<REMOTE_PASS>' pg_dump \
-h <REMOTE_HOST> -p <REMOTE_PORT> -U <REMOTE_USER> -d <REMOTE_DB> \
-Fc --no-owner --no-acl -f /tmp/<REMOTE_DB>.dump
# 4. 导入本地
PGPASSWORD='<LOCAL_PASS>' pg_restore \
-U <LOCAL_USER> -d <LOCAL_DB> \
--no-owner --no-acl /tmp/<REMOTE_DB>.dump
# 5. 校验
PGPASSWORD='<LOCAL_PASS>' psql -U <LOCAL_USER> -d <LOCAL_DB> -c \
"SELECT count(*) FROM information_schema.tables WHERE table_schema='public';"关键决策(为什么这么做)
- `-Fc` 自定义格式:体积小,
pg_restore自动处理依赖顺序,支持并行恢复 - `--no-owner --no-acl`:避免本地缺少远程 owner 角色时报 "role does not exist"
- `DROP ... WITH (FORCE)`(PG 13+):自动踢掉占用会话,告别 "database is being accessed by other users"
- `TEMPLATE template0`:避免
template1的 locale / 编码不一致导致的CREATE DATABASE失败 - 不使用 `pg_dump | pg_restore` 管道:保留中间文件便于失败重试和
pg_restore -l检查内容
常见坑
| 现象 | 处理 |
|---|---|
本地报 role "postgres" does not exist | brew 装的 PG 没有 postgres 角色,使用 macOS 用户名或项目自建用户(如 root) |
database is being accessed by other users | 关闭 Spring Boot 应用 + DBeaver / IDEA 数据库工具后重试(脚本默认带 FORCE) |
数据-only dump restore 时报 relation "public.xxx" does not exist | 不要用 --data-only 倒入空库;改用全量 dump(默认即是) |
JVM 启动报 UnknownHostException: 127.0.0.1 | 系统 SOCKS 代理问题,与同步无关,参考 YudaoServerApplication 处理 |
macOS 提示 pg_restore: command not found | brew install libpq && echo 'export PATH="/opt/homebrew/opt/libpq/bin:$PATH"' >> ~/.zshrc |
同步完成后的回报模板
✅ 同步完成:<REMOTE_HOST>/<REMOTE_DB> → 本地/<LOCAL_DB>
总表数:<N>
system_users: <N> system_menu: <N>
system_role: <N> system_dept: <N>
Dump 文件:/tmp/<REMOTE_DB>.dump(<size>)安全注意
- 本 skill 会销毁本地数据库,执行前必须与用户确认本地库名
- 绝不把生产库设为本地目标
- 密码统一通过
PGPASSWORD环境变量传递,避免出现在ps进程列表里
#!/usr/bin/env bash
# postgres-sync: 把远程 PostgreSQL 数据库同步到本地
# 用法见上层 SKILL.md
set -euo pipefail
# ----- defaults -----
REMOTE_HOST=""
REMOTE_PORT="5432"
REMOTE_USER=""
REMOTE_PASS=""
REMOTE_DB=""
LOCAL_HOST="127.0.0.1"
LOCAL_PORT="5432"
LOCAL_USER=""
LOCAL_PASS=""
LOCAL_DB=""
KEEP_DUMP=false
DRY_RUN=false
VERIFY_TABLES="system_users,system_menu,system_role,system_dept"
# ----- arg parse -----
while [[ $# -gt 0 ]]; do
case "$1" in
--remote-host) REMOTE_HOST="$2"; shift 2 ;;
--remote-port) REMOTE_PORT="$2"; shift 2 ;;
--remote-user) REMOTE_USER="$2"; shift 2 ;;
--remote-pass) REMOTE_PASS="$2"; shift 2 ;;
--remote-db) REMOTE_DB="$2"; shift 2 ;;
--local-host) LOCAL_HOST="$2"; shift 2 ;;
--local-port) LOCAL_PORT="$2"; shift 2 ;;
--local-user) LOCAL_USER="$2"; shift 2 ;;
--local-pass) LOCAL_PASS="$2"; shift 2 ;;
--local-db) LOCAL_DB="$2"; shift 2 ;;
--verify-tables) VERIFY_TABLES="$2"; shift 2 ;;
--keep-dump) KEEP_DUMP=true; shift ;;
--dry-run) DRY_RUN=true; shift ;;
-h|--help)
grep -E '^# ' "$0" | sed 's/^# //'; exit 0 ;;
*) echo "❌ Unknown option: $1" >&2; exit 2 ;;
esac
done
# ----- validate -----
missing=()
for v in REMOTE_HOST REMOTE_USER REMOTE_PASS REMOTE_DB LOCAL_USER LOCAL_PASS LOCAL_DB; do
[[ -z "${!v}" ]] && missing+=("--${v,,}" ) # zsh-safe via bash
done
if [[ ${#missing[@]} -gt 0 ]]; then
echo "❌ Missing required args: ${missing[*]}" >&2
echo " Run with -h for help." >&2
exit 2
fi
for cmd in psql pg_dump pg_restore; do
command -v "$cmd" >/dev/null 2>&1 || { echo "❌ $cmd not found in PATH"; exit 1; }
done
TS="$(date +%Y%m%d-%H%M%S)"
DUMP_FILE="/tmp/${REMOTE_DB}-${TS}.dump"
run() {
if $DRY_RUN; then
echo "DRY-RUN> $*"
else
eval "$@"
fi
}
echo "🔧 Plan:"
echo " Source: ${REMOTE_USER}@${REMOTE_HOST}:${REMOTE_PORT}/${REMOTE_DB}"
echo " Target: ${LOCAL_USER}@${LOCAL_HOST}:${LOCAL_PORT}/${LOCAL_DB} (will be DROPPED)"
echo " Dump: ${DUMP_FILE}"
echo
# ----- 1. test remote -----
echo "▶ [1/5] Testing remote connection..."
run "PGPASSWORD='${REMOTE_PASS}' psql -h '${REMOTE_HOST}' -p '${REMOTE_PORT}' -U '${REMOTE_USER}' -d '${REMOTE_DB}' -c 'SELECT version();' >/dev/null"
echo " ✅ remote ok"
# ----- 2. drop & recreate local -----
echo "▶ [2/5] Dropping & recreating local database..."
run "PGPASSWORD='${LOCAL_PASS}' psql -h '${LOCAL_HOST}' -p '${LOCAL_PORT}' -U '${LOCAL_USER}' -d postgres -c 'DROP DATABASE IF EXISTS \"${LOCAL_DB}\" WITH (FORCE);'"
run "PGPASSWORD='${LOCAL_PASS}' psql -h '${LOCAL_HOST}' -p '${LOCAL_PORT}' -U '${LOCAL_USER}' -d postgres -c 'CREATE DATABASE \"${LOCAL_DB}\" TEMPLATE template0 ENCODING '\\''UTF8'\\'' OWNER \"${LOCAL_USER}\";'"
echo " ✅ local recreated"
# ----- 3. dump remote -----
echo "▶ [3/5] Dumping remote database (this may take a while)..."
run "PGPASSWORD='${REMOTE_PASS}' pg_dump -h '${REMOTE_HOST}' -p '${REMOTE_PORT}' -U '${REMOTE_USER}' -d '${REMOTE_DB}' -Fc --no-owner --no-acl -f '${DUMP_FILE}'"
if ! $DRY_RUN; then
echo " ✅ dump created: $(ls -lh "${DUMP_FILE}" | awk '{print $5}')"
fi
# ----- 4. restore -----
echo "▶ [4/5] Restoring into local database..."
# pg_restore returns non-zero on warnings (e.g. permissions); we tolerate but log.
if $DRY_RUN; then
echo "DRY-RUN> PGPASSWORD='***' pg_restore -h '${LOCAL_HOST}' -p '${LOCAL_PORT}' -U '${LOCAL_USER}' -d '${LOCAL_DB}' --no-owner --no-acl '${DUMP_FILE}'"
else
set +e
PGPASSWORD="${LOCAL_PASS}" pg_restore \
-h "${LOCAL_HOST}" -p "${LOCAL_PORT}" -U "${LOCAL_USER}" \
-d "${LOCAL_DB}" --no-owner --no-acl "${DUMP_FILE}" 2>/tmp/pg_restore.err
rc=$?
set -e
if [[ $rc -ne 0 ]]; then
err_count=$(grep -c '^pg_restore: error' /tmp/pg_restore.err || true)
warn_count=$(grep -c '^pg_restore: warning' /tmp/pg_restore.err || true)
echo " ⚠️ pg_restore exited $rc (errors=${err_count}, warnings=${warn_count}). See /tmp/pg_restore.err"
else
echo " ✅ restore completed without errors"
fi
fi
# ----- 5. verify -----
echo "▶ [5/5] Verifying..."
if $DRY_RUN; then
echo "DRY-RUN> verify queries"
else
TOTAL=$(PGPASSWORD="${LOCAL_PASS}" psql -h "${LOCAL_HOST}" -p "${LOCAL_PORT}" -U "${LOCAL_USER}" -d "${LOCAL_DB}" -tAc \
"SELECT count(*) FROM information_schema.tables WHERE table_schema='public';")
echo " Total tables: ${TOTAL}"
IFS=',' read -ra TBLS <<< "${VERIFY_TABLES}"
for t in "${TBLS[@]}"; do
t_trim="$(echo -n "$t" | tr -d '[:space:]')"
[[ -z "$t_trim" ]] && continue
cnt=$(PGPASSWORD="${LOCAL_PASS}" psql -h "${LOCAL_HOST}" -p "${LOCAL_PORT}" -U "${LOCAL_USER}" -d "${LOCAL_DB}" -tAc \
"SELECT count(*) FROM \"${t_trim}\";" 2>/dev/null || echo "N/A")
printf " %-30s %s\n" "${t_trim}:" "${cnt}"
done
fi
# ----- cleanup -----
if ! $KEEP_DUMP && ! $DRY_RUN; then
rm -f "${DUMP_FILE}"
echo " 🧹 dump file removed (use --keep-dump to retain)"
else
echo " 📁 dump kept at ${DUMP_FILE}"
fi
echo
echo "✅ Sync complete: ${REMOTE_HOST}/${REMOTE_DB} → local/${LOCAL_DB}"