2026年8月13日
2026年8月13日
PostgreSQLのpg_dumpでバックアップと復元を行う方法(形式の選び方・pg_restore・自動化)
PostgreSQLのデータベース単位のバックアップは pg_dump で取り、復元はプレーン形式なら psql、それ以外の形式なら pg_restore で行います。運用では「カスタム形式(-Fc)で取る」「ロールなどのグローバルオブジェクトは pg_dumpall --globals-only で別に取る」「パスワードは .pgpass に置く」の3点を押さえると、復元で困る場面が大きく減ります。この記事では、形式の選び方から復元手順、cronでの自動化と復元テストまでを順に説明します(2026年10月現在、サポート期間内のPostgreSQLで共通の内容です。バージョンで差がある箇所は個別に注記します)。
pg_dumpで取れるもの・取れないもの
pg_dump は1つのデータベースの中身(テーブル定義、データ、インデックス、ビュー、関数、権限設定など)を論理的に書き出すツールです。ダンプ開始時点のスナップショットで一貫性のある内容を取得するため、稼働中のデータベースに対して実行しても、途中で書き込まれた行が混ざることはありません。通常の読み書きも止めません。ただし、ダンプ中は各テーブルに弱いロックを取るので、ALTER TABLE のような強いロックを必要とする操作はダンプが終わるまで待たされます。
一方で、次のものは pg_dump の出力に含まれません。
- ロール(ユーザー)とそのパスワード
- テーブルスペースの定義
postgresql.confやpg_hba.confなどの設定ファイル
これらはクラスタ全体で共有される「グローバルオブジェクト」で、後述の pg_dumpall --globals-only で別に取得します。新しいサーバーに復元したとき「所有者のロールが存在しない」というエラーが大量に出るのは、たいていこれを忘れたケースです。
出力形式の違いと選び方
pg_dump は -F オプションで出力形式を選べます。復元方法と機能が形式ごとに異なるので、ここは比較表で整理します。
| 形式 | オプション | 復元方法 | 圧縮 | 並列ダンプ | 並列リストア・部分復元 |
|---|---|---|---|---|---|
| プレーンSQL | -Fp(既定) |
psql |
外部でgzip等 | 不可 | 不可 |
| カスタム | -Fc |
pg_restore |
既定で圧縮 | 不可 | 可 |
| ディレクトリ | -Fd |
pg_restore |
既定で圧縮 | -j で可 |
可 |
| tar | -Ft |
pg_restore |
なし | 不可 | 部分復元は可 |
普段の定期バックアップには カスタム形式(-Fc) が扱いやすい選択です。1ファイルにまとまり、圧縮され、復元時に特定のテーブルだけを取り出したり、pg_restore -j で並列に復元したりできます。
数十GB以上の大きなデータベースで、ダンプにかかる時間を短くしたいときは ディレクトリ形式(-Fd) を使います。テーブルごとにファイルが分かれ、-j で複数のテーブルを同時に書き出せます。並列数のぶんだけデータベースへの接続を使うので、max_connections に余裕があるか確認してから増やしてください。
プレーン形式(-Fp) は中身がSQL文なので、目で確認したり、別のツールに読ませたりしたいときに向いています。ただし復元は先頭から順に流すしかなく、一部だけ取り出すのは手間がかかります。
基本のバックアップコマンド
Debian/Ubuntu の標準構成では、OSの postgres ユーザーがUNIXソケット経由でパスワードなしに接続できるため、次のように実行するのが手早い方法です。
# カスタム形式でダンプ(日付入りのファイル名)
sudo -u postgres pg_dump -Fc -f /var/backups/pg/mydb_$(date +%Y%m%d).dump mydb
# ディレクトリ形式で4並列ダンプ
sudo -u postgres pg_dump -Fd -j 4 -f /var/backups/pg/mydb_$(date +%Y%m%d) mydb
# プレーン形式をgzipで圧縮
sudo -u postgres pg_dump -Fp mydb | gzip > /var/backups/pg/mydb_$(date +%Y%m%d).sql.gz
出力先ディレクトリは postgres ユーザーが書き込める場所にしておく必要があります。権限エラーで止まる場合は、sudo mkdir -p /var/backups/pg && sudo chown postgres:postgres /var/backups/pg のように用意します。
よく使うオプションは次のとおりです。
-t テーブル名/-n スキーマ名:対象を絞る(-T・-Nで除外)-a:データのみ、-s:定義(スキーマ)のみ-Z:圧縮レベル。PostgreSQL 16 以降は gzip に加えて lz4・zstd も指定できます-C:ダンプにCREATE DATABASEを含める
グローバルオブジェクトをpg_dumpall --globals-onlyで取る
ロールとテーブルスペースは次のコマンドで取得します。出力はプレーンSQLです。
sudo -u postgres pg_dumpall --globals-only -f /var/backups/pg/globals_$(date +%Y%m%d).sql
このファイルにはロールのパスワードハッシュが含まれます。データ本体と同じくらい厳重に扱い、ファイルの権限を 600 にしておいてください。パスワードを含めたくない場合は --no-role-passwords を付けます。
別ホストからの実行と.pgpass
バックアップ専用サーバーから -h でリモート接続してダンプを取る場合、cronから実行するとパスワード入力ができません。コマンドラインにパスワードを書くのは、プロセス一覧やシェル履歴から見えてしまうので避けます。代わりに、実行ユーザーのホームに .pgpass を置きます。
# ~/.pgpass の書式: ホスト名:ポート:データベース名:ユーザー名:パスワード
echo 'db.example.internal:5432:mydb:backup_user:your-db-password' >> ~/.pgpass
chmod 600 ~/.pgpass
pg_dump -h db.example.internal -U backup_user -Fc -f mydb.dump mydb
つまずきやすいのはパーミッションです。.pgpass が他のユーザーから読める状態だと、PostgreSQLのクライアントはこのファイルを無視します。「設定したのにパスワードを聞かれる」ときは、まず chmod 600 になっているかを確認してください。ファイルの場所を変えたい場合は環境変数 PGPASSFILE で指定できます。
バックアップ用のロールには、対象テーブルを読める権限があれば足ります。PostgreSQL 14 以降なら pg_read_all_data ロールを付与する方法が簡単です。
pg_restoreとpsqlで復元する
復元の前に、上書きされる側のデータベースのバックアップも取っておいてください。 誤って本番に復元したときに戻せなくなります。
新しいサーバーへ移す場合の基本の流れは、グローバルオブジェクト → データベース作成 → 中身の復元、の順です。
# 1. ロールとテーブルスペースを先に作る
sudo -u postgres psql -f globals_20261017.sql
# 2. 空のデータベースを作り、カスタム形式のダンプを4並列で復元
sudo -u postgres createdb -O app_owner mydb
sudo -u postgres pg_restore -d mydb -j 4 mydb_20261017.dump
# プレーン形式の場合はpsqlで流す(エラーで止め、1トランザクションで実行)
gunzip -c mydb_20261017.sql.gz | sudo -u postgres psql -v ON_ERROR_STOP=1 --single-transaction -d mydb
pg_restore の主なオプションは次のとおりです。
-C -d postgres:ダンプ内のCREATE DATABASEを使ってデータベースごと作成する(接続先はいったんpostgresなど別のDB)--clean --if-exists:既存のオブジェクトを削除してから作り直す-O(--no-owner):所有者を変更せず、接続ユーザーの所有にする。ロール構成が違う環境へ移すときに使う-t/-n:特定のテーブルやスキーマだけを復元する-l:ダンプの目次を表示する。-lの出力を編集して-Lに渡すと、復元する項目を細かく選べる
プレーン形式で ON_ERROR_STOP を付けないと、psql はエラーが出ても最後まで流し続けます。途中で失敗したまま「完了した」ように見えるので、付けておくのが安全です。
バージョンの違いに注意する
pg_dump は自分より古いメジャーバージョンのサーバーからはダンプできますが、自分より新しいサーバーからのダンプは拒否します。また、新しいバージョンで作ったダンプを古いサーバーに復元できる保証はありません。PostgreSQL のバージョンアップを兼ねて移行する場合は、**移行先(新しい側)の pg_dump を使って旧サーバーからダンプを取る**のが基本です。
cronで自動化し、復元テストまで行う
定期バックアップの最小構成は、ダンプ・保存期間を過ぎたファイルの削除・失敗時の検知の3つです。
#!/bin/bash
# /usr/local/bin/pg_backup.sh(postgresユーザーで実行)
set -euo pipefail
DEST=/var/backups/pg
DATE=$(date +%Y%m%d)
KEEP_DAYS=7
pg_dump -Fc -f "$DEST/mydb_$DATE.dump" mydb
pg_dumpall --globals-only -f "$DEST/globals_$DATE.sql"
chmod 600 "$DEST/mydb_$DATE.dump" "$DEST/globals_$DATE.sql"
# ダンプが読めるか目次を出して確認(壊れていればここで失敗する)
pg_restore -l "$DEST/mydb_$DATE.dump" > /dev/null
# 7日より古いファイルを削除
find "$DEST" -name 'mydb_*.dump' -mtime +"$KEEP_DAYS" -delete
find "$DEST" -name 'globals_*.sql' -mtime +"$KEEP_DAYS" -delete
# sudo crontab -u postgres -e で登録(毎日3時15分)
15 3 * * * /usr/local/bin/pg_backup.sh >> /var/log/pg_backup.log 2>&1
set -euo pipefail を入れておくと、どこかのコマンドが失敗した時点でスクリプトが止まり、終了コードが0以外になります。パイプでgzipに渡している場合も、pipefail がないと pg_dump の失敗が見逃されます。crontabに直接コマンドを書く場合、% は改行として解釈されるので \% とエスケープが必要です。スクリプトに分けておけばこの問題は起きません。
pg_restore -l で確認できるのは「ファイルとして読めること」までです。月に一度程度は、検証用のデータベースへ実際に復元し、主要テーブルの件数を本番と比べておくと、いざというときに手順で迷いません。
sudo -u postgres createdb mydb_restore_test
sudo -u postgres pg_restore -d mydb_restore_test -j 4 /var/backups/pg/mydb_20261017.dump
sudo -u postgres psql -d mydb_restore_test -c "SELECT count(*) FROM orders;"
sudo -u postgres dropdb mydb_restore_test
同じサーバー内にしかバックアップがないと、ディスク障害やサーバーの消失で一緒に失われます。rsync やオブジェクトストレージへの転送で、別の場所にもコピーを置いてください。
なお、pg_dump はある時点の状態を取る方式なので、ダンプとダンプの間の変更は失われます。数分単位で巻き戻したい、あるいはデータベースが大きすぎてダンプが終わらないといった場合は、pg_basebackup とWALアーカイブを組み合わせた物理バックアップ(PITR)を検討します。
まとめ
pg_dumpはデータベース1つ分の論理バックアップ。稼働中でも一貫した内容を取れる- 定期バックアップはカスタム形式(
-Fc)、大きなDBの時間短縮にはディレクトリ形式(-Fd -j) - ロール・テーブルスペースは
pg_dumpall --globals-onlyで別に取り、復元時は先に流す - リモート実行のパスワードは
.pgpass(権限600)に置く - 復元は
pg_restore(プレーン形式はpsql -v ON_ERROR_STOP=1)。バージョン移行では新しい側のpg_dumpを使う - cronで自動化し、
set -euo pipefailと定期的な復元テスト、別の場所へのコピーまで含めて運用する