home-server-config/DISCOURSE_RECOVERY.md

3.3 KiB

Discourse State and Database Recovery

This restores Discourse from the BorgBase backup configured in discourse.nix. It replaces the current Discourse state directory and PostgreSQL database with the selected Borg archive.

The backup contains:

  • /var/lib/discourse
  • /var/backup/discourse/discourse.dump

Requirements

  • BorgBase repo URL: ssh://oas17j8p@oas17j8p.repo.borgbase.com/./repo
  • Decrypted Borg SSH private key from the flash drive
  • Borg repository passphrase from the flash drive
  • A target server that has already been switched to this NixOS config
  • A shell with borg, openssh, postgresql, and rsync

On NixOS or another machine with Nix:

nix-shell -p borgbackup openssh postgresql rsync

Prepare Borg Access

Copy the Borg SSH key into a local recovery directory:

mkdir -p ~/borg-recovery/discourse
cp /path/to/flash/borg-private-key ~/borg-recovery/discourse/borg-private-key
chmod 600 ~/borg-recovery/discourse/borg-private-key

Set the Borg connection environment:

export BORG_REPO='ssh://oas17j8p@oas17j8p.repo.borgbase.com/./repo'
export BORG_RSH='ssh -i ~/borg-recovery/discourse/borg-private-key -o IdentitiesOnly=yes -o StrictHostKeyChecking=accept-new'

Read the Borg passphrase without showing it on screen:

read -rsp 'Borg passphrase: ' BORG_PASSPHRASE
export BORG_PASSPHRASE
echo

Extract a Backup

List available archives:

borg list

Choose an archive name from the list, then extract only the Discourse state and database dump into a temporary directory. Do not extract directly into /.

export ARCHIVE='ARCHIVE_NAME_FROM_BORG_LIST'

mkdir -p ~/borg-recovery/discourse/extract
cd ~/borg-recovery/discourse/extract

borg extract ::$ARCHIVE var/lib/discourse var/backup/discourse/discourse.dump

Restore on the Server

Run these commands from ~/borg-recovery/discourse/extract on the target server. They assume the NixOS config has already created the discourse user, postgresql.service, and discourse-postgresql.service.

Capture absolute tool paths so they still work through sudo:

RSYNC="$(command -v rsync)"
PSQL="$(command -v psql)"
DROPDB="$(command -v dropdb)"
PG_RESTORE="$(command -v pg_restore)"

Stop Discourse and make sure PostgreSQL is running:

sudo systemctl stop discourse.service
sudo systemctl start postgresql.service

Restore /var/lib/discourse:

sudo "$RSYNC" -a --delete ./var/lib/discourse/ /var/lib/discourse/
sudo chown -R discourse:discourse /var/lib/discourse

Replace the discourse PostgreSQL database:

sudo -u postgres "$PSQL" -d postgres -v ON_ERROR_STOP=1 \
  -c "SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname = 'discourse';"

sudo -u postgres "$DROPDB" --if-exists discourse
sudo systemctl restart discourse-postgresql.service

sudo -u postgres "$PG_RESTORE" \
  --exit-on-error \
  --no-owner \
  --role=discourse \
  --dbname=discourse \
  ./var/backup/discourse/discourse.dump

sudo systemctl restart discourse-postgresql.service

Start Discourse:

sudo systemctl start discourse.service

Verify

Check the service and recent logs:

sudo systemctl status discourse.service --no-pager
sudo journalctl -u discourse.service -b --no-pager -n 100

If networking and DNS are already restored, open:

https://discourse.torahimderecheretz.com