Files
vps/runbooks/vaultwarden-sqlite-to-postgres.md
T
windyboy b0c01b2551 docs(runbooks): add runbook spec, template, index and 6 first-batch runbooks; light-enhance existing 10
- RUNBOOKS.md: repo-level spec (six-field model, naming, safety, maturity path)
- runbooks/_template.md + README.md: standard template and 16-entry routing index
- new: issue-to-merge, fix-ci, release, rollback, network-change, network-recovery
- light-enhance 10 existing runbooks with Purpose/Scope/Safety headers
- AGENTS.md: point step 3 at index/spec, add runbook execution rules
- docs/agent-runbook-guide.md: archive of Manus AI guide
2026-08-17 15:59:46 +08:00

2.8 KiB

Runbook: Vaultwarden SQLite → Postgres (us2)

Purpose

Replay checklist for the exceptional SQLite→Postgres migration on us2.

Scope

  • Applicable: us2.wsvc.info, /opt/vaultwarden.
  • Not applicable: routine reconcile — this is destructive and intentionally not automated.

Safety

  • Destructive: requires explicit approval; confirm backups exist before starting.
  • Do not commit .env or password-bearing files to git.
  • If any step diverges from this checklist, STOP and report.

Target: us2.wsvc.info
Upstream: Using the PostgreSQL Backend · docs/vaultwarden-upstream.md

Done on 2026-08-01 for this host. Keep this as the replay checklist. This is an exceptional, destructive migration procedure: it is intentionally not automated by the routine Ansible reconciliation playbook.

Summary of what we did

  1. Backup vw-data + .envbackups/pre-pg-migrate-<timestamp>/
  2. Baseline counts from SQLite (then: users=1, ciphers=1327)
  3. Drop/recreate empty Postgres DB; pin image vaultwarden/server:1.37.1
  4. Set compose DATABASE_URL=postgresql://${DB_USER}:${DB_PASS}@${DB_HOST}:${DB_PORT}/${DB_NAME}
  5. Start VW once → diesel schema → stop
  6. pgloader data only (exclude __diesel_schema_migrations) with CAST bigint→integer
  7. Remaining intentional bigints: attachments.file_size, twofactor.last_used, twofactor_duo_ctx.exp
  8. Align config.json SMTP 587/starttls, ip_header=X-Forwarded-For, DOMAIN without trailing slash
  9. Rotate DB_PASS / PGWEB_PASS; pgweb behind compose profiles: [debug]
  10. Move live sqlite file to backups/sqlite-cold/ (cold only)
  11. After VW recreate, restart Traefik so Docker provider picks up the router

Critical pitfalls

  • config.json overrides .env (admin wins). Fix SMTP there or via /admin.
  • SMTP password drift: if .env AUTH works but VW mail fails with 535, sync vw-data/config.json (+ .smtp-credentials) to the mailbox password — do not assume env alone is enough.
  • From this network, mx2:465 times out; use 587 + starttls.
  • After recreating vaultwarden, Traefik may 404 until docker restart traefik.
  • pgloader needs a writable directory for the sqlite file (CANTOPEN if mount is awkward).
  • After pgloader, wrong integer types → silent “wrong password”; see Discussion #7134.
  • Do not commit .env or load files with passwords into git.

Rollback

  1. docker compose stop vaultwarden
  2. Restore vw-data from backups/pre-pg-migrate-* (including sqlite)
  3. Remove DATABASE_URL from compose environment
  4. docker compose up -d vaultwarden && docker restart traefik