PostgreSQL「lock file already exists(postmaster.pid)」の原因と安全な解決手順【完全版】

PostgreSQL「lock file already exists(postmaster.pid)」の原因と安全な解決手順【完全版】

このエラーは、データディレクトリ($PGDATA)直下にあるロックファイル「postmaster.pid」をPostgreSQLが検出し、「既に同じデータディレクトリでサーバが動いている」または「異常終了でロックが残っている」と判断したときに発生する。まずは本当に稼働中かを確認し、稼働中なら停止してから、稼働していないのにロックが残っている場合のみ安全に削除して再起動する。

症状と典型的なエラーメッセージ

よく見られる出力例:

FATAL:  lock file "postmaster.pid" already exists
HINT:  Is another postmaster (PID 1234) running in data directory "/var/lib/postgresql/16/main"?

ポイントは「postmaster.pid」というファイル名と、「別プロセス(PID)」や「data directory」の記載。

エラーの発生条件(よくある原因)

  • 同じデータディレクトリ($PGDATA)で既にPostgreSQLが稼働中
  • 異常終了(電源断・強制終了・OSクラッシュ)後にロックが孤児化(stale)
  • $PGDATAの取り違え(別バージョンや別環境のディレクトリを指定)
  • 所有権/権限の不整合により停止/起動がうまくいかずロックが残留
  • コンテナ/仮想環境で同一ボリュームを複数インスタンスが掴み合い
  • 自動起動スクリプトやサービス設定の重複起動

まず確認:PostgreSQLは既に稼働中か?

稼働中ならロックがあるのは正常。停止するまでロックは残る。

# 接続可否(ローカル)
pg_isready -h 127.0.0.1 -p 5432

# バージョン取得
psql -h 127.0.0.1 -p 5432 -U postgres -c "select version();"

# プロセス確認(Linux/macOS)
ps aux | grep '[p]ostgres'

# systemd
sudo systemctl status postgresql

# Homebrew (macOS)
brew services list

# Windows(管理者PowerShell)
sc query "postgresql-x64-16"

postmaster.pid の役割と中身(扱いの注意)

postmaster.pid はサーバ起動時に作成され、プロセスID(PID)やポート等のメタ情報が入る。実行中に削除してはいけない。削除するのは「サーバが確実に停止している」「ロックが孤児化している」場合のみ。

# ロックファイルの場所(例)
echo $PGDATA
ls -l "$PGDATA/postmaster.pid"

# PIDの確認(1行目)
head -1 "$PGDATA/postmaster.pid"

安全な停止と起動(基本手順)

# 1) サーバの状態確認
pg_ctl -D "$PGDATA" status

# 2) 稼働中なら安全に停止
pg_ctl -D "$PGDATA" stop -m fast

# 3) 停止を確認(必要なら数秒待つ)
pg_ctl -D "$PGDATA" status

# 4) 起動
pg_ctl -D "$PGDATA" start

systemdやHomebrew管理下なら、その方法で停止/起動するのが安全。

# systemd
sudo systemctl stop postgresql
sudo systemctl start postgresql

# Homebrew
brew services restart postgresql@16

ステール(孤児)ロックの見分け方と削除手順(Linux/macOS)

1) プロセス実在確認(PIDが生きていれば稼働中)

PID="$(head -1 "$PGDATA/postmaster.pid")"
if kill -0 "$PID" 2>/dev/null; then
  echo "PostgreSQLはPID $PIDで稼働中。削除禁止。"
else
  echo "PIDが存在しない → ロックは孤児の可能性。"
fi

2) ほかにpostgresプロセスがいないか再確認
3) バックアップを取ってから削除し起動

# 念のためバックアップ
cp -a "$PGDATA/postmaster.pid" "$PGDATA/postmaster.pid.bak.$(date +%s)"

# 削除(サーバ非稼働が前提)
rm -f "$PGDATA/postmaster.pid"

# 再起動
pg_ctl -D "$PGDATA" start

Windowsでの手順(サービス管理)

# 停止
net stop "postgresql-x64-16"

# もし停止済みなのに postmaster.pid が残っている場合
copy "C:\Program Files\PostgreSQL\16\data\postmaster.pid" "C:\Program Files\PostgreSQL\16\data\postmaster.pid.bak"
del  "C:\Program Files\PostgreSQL\16\data\postmaster.pid"

# 起動
net start "postgresql-x64-16"

pg_ctl.exe を使う場合:

"C:\Program Files\PostgreSQL\16\bin\pg_ctl.exe" -D "C:\Program Files\PostgreSQL\16\data" stop -m fast
"C:\Program Files\PostgreSQL\16\bin\pg_ctl.exe" -D "C:\Program Files\PostgreSQL\16\data" start

$PGDATA の取り違えチェック(最重要)

起動コマンドとロックファイルの $PGDATA が一致しているかを確認する。ディストリごとの例:

# Debian/Ubuntu系
/var/lib/postgresql/16/main

# RHEL/CentOS系
/var/lib/pgsql/16/data

# Homebrew (Apple Silicon)
 /opt/homebrew/var/postgres

# Windows 例
C:\Program Files\PostgreSQL\16\data

異なる $PGDATA を指す起動スクリプトが複数存在すると、重複起動と見なされる。

権限・所有権・ディスク容量の確認

権限不整合やディスク満杯は異常停止や起動失敗の典型的トリガ。

# 所有者と権限(Linux)
sudo chown -R postgres:postgres "$PGDATA"
sudo chmod 700 "$PGDATA"

# 空き容量
df -h

サービス管理ごとのコマンド早見表

# systemd(ユニット名は環境により postgresql@16-main 等)
sudo systemctl status postgresql
sudo systemctl stop postgresql
sudo systemctl start postgresql
sudo journalctl -u postgresql -e

# Homebrew
brew services list
brew services restart postgresql@16
tail -n 200 $(brew --prefix)/var/log/postgres.log

# Docker
docker ps | grep postgres
docker logs <container_id>
docker stop <container_id>; docker start <container_id>

# Kubernetes
kubectl get pods -n <ns> -l app=postgres
kubectl logs <pod> -n <ns>

コンテナ/仮想化環境での注意点

  • 同じボリューム(同じ$PGDATA)を複数インスタンスが同時にマウントしない
  • StatefulSetやComposeで再起動ポリシーが被って二重起動にならないようにする
  • ヘルスチェック(pg_isready)で実動前に二重起動をトリガしない設計

再発防止チェックリスト

  • 単一の公式な起動経路(systemd か pg_ctl か Homebrew か)に統一
  • サービスの自動起動設定を重複させない
  • 定期的にバックアップと正常停止手順(stop -m fast)の確認
  • 停電・クラッシュ対策(UPS、ファイルシステム/ストレージの堅牢化)
  • 監視で「異常終了→自動再起動」のループ検知

トラブルシューティングのクイックフロー

1) pg_isready/psqlで接続可否 → 接続可なら稼働中(手を入れない)
2) pg_ctl status / systemctl status で状態確認
3) postmaster.pid のPIDが生存しているか kill -0 で確認
4) 生存していなければバックアップのうえpostmaster.pidを削除 → 起動
5) 起動しない場合はログ確認(journalctl / postgres.log / Windowsイベントログ)
6) $PGDATA、権限、ディスク、ポート競合を再点検

サンプル:安全にステールロックを除去して再起動(Linux/macOS)

#!/usr/bin/env bash
set -euo pipefail

: "${PGDATA:=/var/lib/postgresql/16/main}"   # 環境に合わせて修正
: "${PGPORT:=5432}"

echo "[*] PGDATA = $PGDATA"
if pg_isready -h 127.0.0.1 -p "$PGPORT" >/dev/null 2>&1; then
  echo "[*] PostgreSQLは稼働中。処理を終了します。"
  exit 0
fi

if [ -f "$PGDATA/postmaster.pid" ]; then
  PID="$(head -1 "$PGDATA/postmaster.pid" || true)"
  if [ -n "${PID:-}" ] && kill -0 "$PID" 2>/dev/null; then
    echo "[!] PID $PID が生存。停止してから再実行してください。"
    exit 1
  fi
  echo "[*] ステールロックをバックアップし削除します。"
  cp -a "$PGDATA/postmaster.pid" "$PGDATA/postmaster.pid.bak.$(date +%s)"
  rm -f "$PGDATA/postmaster.pid"
fi

echo "[*] 所有権/権限の健全性を確認します。"
sudo chown -R postgres:postgres "$PGDATA"
sudo chmod 700 "$PGDATA"

echo "[*] PostgreSQLを起動します。"
sudo -u postgres pg_ctl -D "$PGDATA" -l "$PGDATA/server.log" start

echo "[*] 起動確認:"
pg_isready -h 127.0.0.1 -p "$PGPORT"

サンプル:Windows PowerShellワンライナー

$ServiceName = "postgresql-x64-16"
$DataDir     = "C:\Program Files\PostgreSQL\16\data"

sc query $ServiceName
if ((Get-Service $ServiceName).Status -eq "Running") {
  Write-Host "PostgreSQLは稼働中。処理を終了。"
  exit
}

$postmaster = Join-Path $DataDir "postmaster.pid"
if (Test-Path $postmaster) {
  $pid = Get-Content $postmaster | Select-Object -First 1
  try {
    $p = Get-Process -Id $pid -ErrorAction Stop
    Write-Host "PID $pid が稼働中。サービスを停止してから再実行してください。"
    exit
  } catch {
    Copy-Item $postmaster "$($postmaster).bak.$([int][double]::Parse((Get-Date -UFormat %s)))"
    Remove-Item $postmaster -Force
  }
}
Start-Service $ServiceName

補足:ポート競合は別エラー

ポート競合(例:5432が他プロセスで使用中)の場合は「Address already in use」の趣旨で失敗する。「lock file already exists」とは別系統。混同しないようにログを必ず確認する。