Files
siftlode/scripts/restore.ps1
T
peter ca4e559cc7 fix(ops): make backup/restore work, and give upgrades a pin and a rollback
DX-13: both shell scripts ran bare 'docker compose', which has no config
file to find in the layout docs/self-hosting.md tells people to use (only
docker-compose.selfhost.yml is downloaded) — they now fall back to it, while
a source checkout keeps resolving docker-compose.yml as before. The two
PowerShell scripts moved the dump through the PS pipeline, which corrupts
binary on 5.1 and cannot express -Encoding Byte at all on 7 ('Byte' is not a
supported encoding name), so they simply failed there; they now write inside
the container and 'docker compose cp' it across. backup.sh also wrote its
output straight into backups/, leaving a truncated file that looks like a
real backup when pg_dump fails — it writes a .part and promotes on success.
Both restores report a non-zero pg_restore rather than printing 'Restored'
over it. Docs now state that a backup is the dump PLUS .env: without
TOKEN_ENCRYPTION_KEY every encrypted secret in the restored database is
unreadable, which the old one-line 'back up the volume' never said.

DX-14: 'latest' moves with every release and migrations run on every boot,
so self-hosters had no way to choose when they move and no way back. Docs
gain IMAGE_TAG pinning, a rollback recipe (image-only vs schema-bearing) and
the migration-failed-at-boot case; CHANGELOG.md is generated from
releaseNotes.ts so the file cannot drift from what the app shows, and
'siftlode release' refuses on a stale one rather than regenerating it into
the clean tree it just demanded.

Verified: dump + restore round-trip on both platforms against localdev into
a scratch database (8 users, 262822 videos, schema 0082_invite_lang), and
IMAGE_TAG=0.80.0 resolves on both services in 'compose config'.
2026-09-05 00:50:07 +02:00

36 lines
1.9 KiB
PowerShell

# Restores a Siftlode Postgres dump created by backup.ps1 into the running db container.
# Usage: .\scripts\restore.ps1 backups\siftlode-YYYYMMDD-HHMMSS.dump
#
# Restoring the database does NOT restore the secrets that decrypt it. If this dump is from an
# instance with a different TOKEN_ENCRYPTION_KEY than the current .env, the encrypted sysconfig
# values and refresh tokens will not be readable. See docs/self-hosting.md § Backups.
param([Parameter(Mandatory = $true)][string]$DumpFile)
$ErrorActionPreference = "Stop"
# See backup.ps1: the dump travels with `docker compose cp`, never through the PowerShell pipeline,
# which corrupts binary on 5.1 and cannot express `-Encoding Byte` at all on 7.
$remote = "/tmp/siftlode-restore.dump"
if (-not $env:COMPOSE_FILE -and -not (Test-Path "docker-compose.yml") -and (Test-Path "docker-compose.selfhost.yml")) {
$env:COMPOSE_FILE = "docker-compose.selfhost.yml"
}
$user = if ($env:POSTGRES_USER) { $env:POSTGRES_USER } else { "siftlode" }
$dbname = if ($env:POSTGRES_DB) { $env:POSTGRES_DB } else { "siftlode" }
if (-not (Test-Path $DumpFile)) { throw "Dump file not found: $DumpFile" }
docker compose cp $DumpFile "db:$remote"
if ($LASTEXITCODE -ne 0) { throw "RESTORE FAILED — could not copy the dump into the container (exit $LASTEXITCODE)." }
docker compose exec -T db pg_restore -U $user -d $dbname --clean --if-exists $remote
$restoreCode = $LASTEXITCODE
docker compose exec -T db rm -f $remote 2>$null
# pg_restore exits non-zero for ignorable warnings too (--clean --if-exists on a fresh database
# reports every missing object it tried to drop), so a non-zero code is reported as "check this",
# not swallowed and not dressed up as success.
if ($restoreCode -ne 0) {
throw "pg_restore reported errors while restoring $DumpFile (exit $restoreCode) — read the output above before treating this instance as restored."
}
Write-Output "Restored $DumpFile"