Upgradeとデータ保持
既存環境の更新にはこの手順を使います。fresh installには保持対象の データがないため、はじめにを使ってください。下記の post-upgrade確認が終わるまで、更新成功とは報告しません。
契約はfail-closedです。backup不足、PostgreSQL停止、schema非互換、migration 失敗のどれかがあれば更新を停止します。volumeを削除したり、不完全なcontrol planeを起動したりして続行しません。
更新前の確認
現在のrelease、checkout SHA、Compose file、.envの参照先、選択したZ.AI 環境file、docker compose psの結果を記録します。secret値は記録しません。 3つのPostgreSQL dump、volume archive、隔離restore用の空き容量を確認します。 確認期間中は旧checkoutと旧image tagを残します。
更新はimageと追跡済み設定を変更する作業です。docker compose down --volumes は不要です。通常のdocker compose downはnamed volumeを保持しますが、 --volumesはvolumeのデータを永久削除するため、更新手順では使いません。
Backup契約
checkout外のtimestamp付き、アクセス制限済みdirectoryを作ります。次のDBを 必ずbackupします。
| Database | 保持するデータ |
|---|---|
forgejo | repository、user、organization、Issue、PR、comment、Actions metadata、LFS参照 |
openface_metrics | 実測view、download、時系列event、like、agent identity |
openface_maintenance | webhook deliveryとmaintenance job状態 |
v0.5.0 migrationが終わっていないv0.4.0環境では、pipeline audit/historyとreconcile stateは まだlegacy SQLiteの/data/agents/pipelines/pipeline-audit.dbに保存され、 openface_agent-metrics-data volumeに含まれます。openface_metrics dumpにはこのlegacy stateは含まれないため、上記volume archiveもbackup契約に含めます。migration後のauthoritative stateはopenface_metricsのopenface_pipeline schemaに入り、このdumpで保持されます。
v0.6.0ではrunner startupがopenface_metrics内にmetric_events ledgerとindexを作成し、 既存のview/like counterを安定したidempotency keyでbackfillします。これは手動data migration ではない自動かつ非破壊の初期化ですが、ledgerは自動pruneされないため、database dumpとrestore evidenceを保持してください。
dumpまたはarchiveを作る前にmaintenance windowを開き、archive対象volumeへ writeできる稼働中のCompose serviceをquiesceします。pg_dumpのために postgresだけを残し、下記のdumpとarchiveがすべて終わるまで停止したserviceを 起動しません。これでForgejo、Spaces runner、maintenance、Actions、MCP (有効な場合)のwriteを一貫した操作として停止できます。
set -euo pipefail
mapfile -t running_services < <(docker compose ps --services --status running)
for service in "${running_services[@]}"; do
if [[ "$service" != "postgres" ]]; then
docker compose stop "$service"
fi
doneLXC restore helperは、.env、選択したZ.AI環境file、gateway-certs.zipの保護済み copyも必要とします。database dumpの前に同じaccess-controlled backupへcopyし、 内容はmanifestへ記録しません。
下記のbackup snippetを実行する前にOPENFACE_BACKUP_DIRを一度設定します。 各snippetはfail-fastを有効にし、同じdirectoryを再利用します。directoryを設定せず 別shellでsnippetだけを実行した場合は直ちに失敗します。
set -euo pipefail
export OPENFACE_BACKUP_DIR="${OPENFACE_BACKUP_DIR:-/secure/openface-backups/$(date -u +%Y%m%dT%H%M%SZ)}"
backup_dir="$OPENFACE_BACKUP_DIR"
umask 077
mkdir -p "$backup_dir"
test -f .env
zai_config="${ZAI_AGENT_CONFIG:-$(sed -n 's/^ZAI_AGENT_CONFIG=//p' .env | head -n 1)}"
zai_config="${zai_config:-./maintenance-agent/zai.example.env}"
test -f "$zai_config"
test -d gateway/certs
install -m 600 .env "$backup_dir/openface.env"
install -m 600 "$zai_config" "$backup_dir/zai.env"
python3 - "$backup_dir/gateway-certs.zip" <<'PY'
from pathlib import Path
import sys
from zipfile import ZIP_DEFLATED, ZipFile
root = Path("gateway/certs")
with ZipFile(sys.argv[1], "w", compression=ZIP_DEFLATED) as archive:
for path in root.rglob("*"):
if path.is_file():
archive.write(path, path.relative_to(root))
PYset -euo pipefail
backup_dir="${OPENFACE_BACKUP_DIR:?Set OPENFACE_BACKUP_DIR before running the backup snippets}"
umask 077
mkdir -p "$backup_dir"
docker compose exec -T postgres sh -c 'pg_dump -U "${POSTGRES_USER:-openface}" -Fc forgejo' > "$backup_dir/forgejo.dump"
docker compose exec -T postgres sh -c 'pg_dump -U "${POSTGRES_USER:-openface}" -Fc openface_metrics' > "$backup_dir/openface_metrics.dump"
docker compose exec -T postgres sh -c 'pg_dump -U "${POSTGRES_USER:-openface}" -Fc openface_maintenance' > "$backup_dir/openface_maintenance.dump"deploymentで使っているnamed volumeをarchiveします。実際の名前は docker volume lsで確認し、広すぎるhost directoryを指定しません。 次のpreflightとarchive blockはnamed volume、bind archive、credential fileに 同じMCP検出を使います。archiveを作る前にMCP一式を検査するため、部分的なMCP backupはfail closedになります。
set -euo pipefail
backup_dir="${OPENFACE_BACKUP_DIR:?Set OPENFACE_BACKUP_DIR before running the backup snippets}"
mcp_enabled="${OPENFACE_MCP_ENABLED:-0}"
compose_profiles="${COMPOSE_PROFILES:-$(sed -n 's/^COMPOSE_PROFILES=//p' .env | head -n 1)}"
case ",${compose_profiles}," in
*,mcp,*) mcp_enabled=1 ;;
esac
if [[ "$mcp_enabled" != "1" ]] && docker volume inspect openface_mcp-state >/dev/null 2>&1; then
mcp_enabled=1
fi
if [[ "$mcp_enabled" == "1" ]]; then
mcp_state_dir="${OPENFACE_MCP_STATE_DIR:-$(sed -n 's/^OPENFACE_MCP_STATE_DIR=//p' .env | head -n 1)}"
mcp_state_dir="${mcp_state_dir:-./secrets/openface-mcp}"
mcp_forgejo_token_file="${OPENFACE_MCP_FORGEJO_USER_TOKEN_FILE:-$(sed -n 's/^OPENFACE_MCP_FORGEJO_USER_TOKEN_FILE=//p' .env | head -n 1)}"
mcp_forgejo_token_file="${mcp_forgejo_token_file:-./secrets/openface-mcp-forgejo-user-token}"
mcp_admin_token_file="${OPENFACE_MCP_ADMIN_INTERNAL_TOKEN_FILE:-$(sed -n 's/^OPENFACE_MCP_ADMIN_INTERNAL_TOKEN_FILE=//p' .env | head -n 1)}"
mcp_admin_token_file="${mcp_admin_token_file:-./secrets/openface-mcp-admin-internal-token}"
if ! docker volume inspect openface_mcp-state >/dev/null 2>&1; then
echo "MCP is enabled but openface_mcp-state is missing." >&2
exit 1
fi
if [[ ! -d "$mcp_state_dir" || ! -f "$mcp_forgejo_token_file" || ! -f "$mcp_admin_token_file" ]]; then
echo "MCP is enabled but its bind state or credential source file is missing." >&2
exit 1
fi
fi
volumes=(
openface_forgejo-data \
openface_agent-metrics-data \
openface_maintenance-agent-data \
openface_shared-token \
openface_forgejo-runner-data
)
if [[ "$mcp_enabled" == "1" ]]; then
volumes+=(openface_mcp-state)
fi
for volume in "${volumes[@]}"; do
if ! docker volume inspect "$volume" >/dev/null 2>&1; then
echo "Required Docker volume is missing: $volume" >&2
exit 1
fi
docker run --rm -v "${volume}:/source:ro" -v "$backup_dir:/backup" alpine \
tar czf "/backup/${volume}.tgz" -C /source .
done
# MCP profileのbind stateと保護されたcredential sourceはnamed volume loopに
# 含まれないため、同じmaintenance windowでarchiveします。
if [[ "$mcp_enabled" == "1" ]]; then
tar czf "$backup_dir/openface-mcp-state-dir.tgz" -C "$mcp_state_dir" .
install -m 600 "$mcp_forgejo_token_file" "$backup_dir/mcp-forgejo-user-token"
install -m 600 "$mcp_admin_token_file" "$backup_dir/mcp-admin-internal-token"
fi上記の.tgz、openface.env、zai.env、gateway-certs.zip、保護されたMCP source filenameを使います。 OPENFACE_MCP_ENABLED=1を指定したscripts/restore_lxc_deployment.shは、MCP named volume、bind archive、Forgejo service-account PAT、admin bridge credential を必須としてrestoreし、復元した.envへCOMPOSE_PROFILES=mcpも保存します。 復元先hostでpathを変える場合は対応するOPENFACE_MCP_*_FILE変数を渡してください。 registry token、HMAC key、その他secretの内容はmanifestへ記録しません。
manifestにはfilename、byte size、SHA-256、現在のrelease/SHA、health結果を 記録します。.envの内容、tokenの内容、password、credential入りURL、secret値は manifestへ出さず、source fileはaccess-controlledなbackup内に保管します。 backupを別の保護場所へコピーし、restore rehearsalを行ってから運用backupと みなします。
更新手順
旧checkoutで
docker compose ps、gateway health、Forgejo login、代表 repository、Issue/PR、Space、metrics、maintenance、pipeline historyを 確認し、件数と代表IDをprivate manifestへ保存します。target tagまたはreview済みrelease commitを別checkoutへ取得します。
docker compose config --quietを実行し、environment、image、database、 named volume、seedの差分を確認します。backupと旧checkoutを残したまま、
docker compose up -d --build postgresを実行し、3つのPostgreSQL databaseがhealthyになるまで待ちます。migration pathを選ぶ間は
spaces-runnerを停止したままにします。fresh install、または legacy SQLite sourceがないtargetでは、PostgreSQLがhealthyになった後にrunnerを起動して/healthzを確認できます。v0.4.0 sourceがある場合は、次の明示的migrationが成功するまで 起動しません。reviewed targetにPostgreSQL pipeline schemaと
pipeline_migration.pyが含まれる場合、既存のpipeline-audit.dbを次の明示的commandで移行します。fresh installまたはlegacy sourceがない 場合は実行しません。v0.5.0の通常startupはlegacy SQLiteを自動で検索・importしないため、 migrationと比較確認が成功するまで新しいpipeline writeを受け付けません。bashdocker compose run --rm --no-deps --build spaces-runner \ python pipeline_migration.py \ --source /data/agents/pipelines/pipeline-audit.db --verify-only docker compose run --rm --no-deps --build spaces-runner \ python pipeline_migration.py \ --source /data/agents/pipelines/pipeline-audit.dbこのcommandはSQLite integrityとcolumnを検査し、source digestを
openface_pipeline.sqlite_migrationsへ保存します。再実行は冪等で、競合行や 不完全なsourceではnon-zeroになります。比較が終わるまでsource fileを残します。long-running serviceだけを起動し、one-shotの
seedは通常のrestartから除外 します。--no-depsで、maintenance-agentやActions runnerのdependencyとして Composeがseedを起動することも防ぎます。bashdocker compose up -d --build --no-deps \ postgres gateway frontend forgejo spaces-runner maintenance-agent \ forgejo-actions-dind forgejo-actions-runnermaintenance windowの前からMCPを有効にしていた場合、またはtarget releaseが MCPを明示的に必要とする場合は、このblockへ
OPENFACE_MCP_ENABLED=1を渡すか、.envにCOMPOSE_PROFILES=mcpを残してprofile serviceを起動します。bashset -euo pipefail mcp_enabled="${OPENFACE_MCP_ENABLED:-0}" compose_profiles="${COMPOSE_PROFILES:-$(sed -n 's/^COMPOSE_PROFILES=//p' .env | head -n 1)}" case ",${compose_profiles}," in *,mcp,*) mcp_enabled=1 ;; esac if [[ "$mcp_enabled" == "1" ]]; then docker compose --profile mcp up -d --build --no-deps mcp-admin openface-mcp fiseed再実行はreleaseが明示した場合だけ行います。seedは冪等で、既存repository、 Issue、like、audit historyを削除してはいけません。
更新後の確認
毎releaseで同じ順に確認し、secretを除いたrecordをprivateなdeployment changeへ 添付します。
| 領域 | 必須確認 |
|---|---|
| Compose | docker compose config --quiet、期待するserviceがhealthy |
| PostgreSQL | 3 DBへ接続でき、target releaseが要求するschema versionとmigration markerだけを確認する(pipeline markerは#163を含む場合だけ) |
| Forgejo | login、repository/Files、LFS、Issue、PR、comment、historyのID/件数 |
| Space | public/private境界、start/status、代表artifact、environment control |
| Metrics | 代表的なview/download/時系列件数、active like、agent identity |
| Maintenance | webhook/job historyが読め、意図しない再実行がない |
| Pipeline | audit、retry/cancel/rollback/reconcile、cursor、run number |
| Runtime | gateway、frontend、spaces-runner/healthz、maintenance health、log |
更新前後でrow count、stable ID、created timestamp、Issue/PR番号、repository history、LFS object、pipeline run numberを比較します。比較証跡がない項目は unknown passではなく検証失敗です。
失敗時とrollback
healthcheck、migration、互換性検査、比較のいずれかが失敗したら、対象serviceを 停止し、secretを含まないlogだけを保存します。named volumeを削除せず、破壊的 migrationを無計画に再試行しません。image/configだけが変わりschema互換なら旧 checkout/image tagへ戻します。schema/data migration後はin-place downgradeせず、 PostgreSQL dumpとnamed volumeを隔離projectまたは承認済みmaintenance windowへ restoreし、旧releaseの比較確認後に切り戻します。
旧checkoutはdatabase backupではありません。検証済みdumpとvolume archiveがない rollbackはblockedです。
Release notesの必須欄
英語/日本語の各release noteに、target versionと対応する旧version、breaking change、schema/data migration(automaticかexplicitか)、backup対象DB/volume、 environment/image/Compose/volume変更、rollback条件、post-upgrade確認、known issues、このrunbookへのlinkを記載します。現在の実例は v0.6.0 release pageで、前回のpipeline migrationは v0.5.0 release pageを参照してください。
