Skip to content

Disaster Recovery Runbook

ProjectPTX-CM
Last Updated2026-02-17

Quick Reference

ScenarioTime to recoverProcedure
API crash~1 minAutomatic (Docker restart + replicas)
Host reboot~2-3 minAutomatic (Docker restart policies)
Bad migration~15 minRestore from backup
Disk failure~30-60 minFull server rebuild
Data corruption~15 minRestore from backup

Prerequisites

Before disaster strikes, ensure:

  • [ ] scripts/backup.sh runs daily via cron
  • [ ] Backups are copied offsite (S3/GDrive/rclone)
  • [ ] Better Stack heartbeat confirms backups are running
  • [ ] .env file is stored in a secure vault (1Password, Bitwarden, etc.)
  • [ ] This document is accessible outside the server (printed/cloud copy)

Restore from Backup

Use when: bad migration, accidental data deletion, data corruption.

Step 1: Find the latest backup

bash
ls -lht backups/*.sql.gz | head -5

If local backups are gone, download from offsite:

bash
# rclone copy remote:ptx-cm-backups ./backups --max-age 7d
# aws s3 sync s3://ptx-cm-backups/ ./backups/

Step 2: Restore

bash
./scripts/restore.sh backups/ptx_cm_YYYYMMDD_HHMMSS.sql.gz

The script will:

  1. Stop API containers (prevent writes)
  2. Drop and recreate the database
  3. Restore from the backup file
  4. Restart API containers

Step 3: Verify

bash
# Check API health
curl http://localhost/health

# Check readiness (DB + Redis)
curl http://localhost/health/ready

# Check containers
docker compose ps

Full Server Rebuild

Use when: server hardware failure, need to migrate to new server.

Step 1: Provision new server

  • Ubuntu 22.04+ LTS (or your preferred Linux)
  • Docker Engine + Docker Compose v2
  • Git
  • Minimum: 2 CPU, 4 GB RAM, 40 GB SSD
  • Open ports: 80 (HTTP), 443 (HTTPS)
  • SSH access configured
bash
# Install Docker
curl -fsSL https://get.docker.com | sh
sudo usermod -aG docker $USER

# Install Docker Compose plugin
sudo apt install docker-compose-plugin

Step 2: Clone repository

bash
git clone <your-repo-url> /opt/ptx-cm
cd /opt/ptx-cm

Step 3: Restore secrets

Retrieve .env from your secure vault and place it in the project root:

bash
# Required secrets:
# DATABASE_URL, JWT_SECRET, JWT_REFRESH_SECRET,
# ENCRYPTION_KEY, REDIS_PASSWORD, SMTP credentials
cp /path/to/vault/.env .env

Step 4: Restore database backup

bash
# Start only postgres first
docker compose up -d postgres
# Wait for healthy
docker compose exec postgres pg_isready -U ptx_cm

# Download latest backup from offsite
# rclone copy remote:ptx-cm-backups ./backups --max-age 1d

# Restore
./scripts/restore.sh backups/ptx_cm_LATEST.sql.gz

Step 5: Start all services

bash
docker compose up -d

Step 6: Verify

bash
# All containers running?
docker compose ps

# API healthy?
curl http://localhost/health

# DB + Redis connected?
curl http://localhost/health/ready

# Web accessible?
curl -I http://localhost/

Step 7: DNS / Proxy

Update DNS or load balancer to point to the new server's IP.

Step 8: Restore cron

bash
# Add backup cron
crontab -e
# 0 2 * * * /opt/ptx-cm/scripts/backup.sh >> /var/log/ptx-cm-backup.log 2>&1

Secrets to Recover

SecretWhere to find it
.env fileSecure vault (1Password/Bitwarden)
JWT_SECRETIn .env
JWT_REFRESH_SECRETIn .env
ENCRYPTION_KEYIn .envcritical, decrypts OTA credentials
REDIS_PASSWORDIn .env
SMTP credentialsIn .env
Better Stack heartbeat URLBetter Stack dashboard
Git repo credentialsGit hosting platform

⚠️ CRITICAL: If ENCRYPTION_KEY is lost, all encrypted OTA credentials become unrecoverable. Ensure this is backed up in your vault.


Post-Recovery Checklist

  • [ ] All containers healthy (docker compose ps)
  • [ ] /health returns 200
  • [ ] /health/ready returns 200 (DB + Redis OK)
  • [ ] Login works in browser
  • [ ] Dashboard loads with data
  • [ ] Booking sync is running (check sync jobs page)
  • [ ] Better Stack monitors are green
  • [ ] Backup cron is scheduled
  • [ ] Better Stack heartbeat is receiving pings

PTX Channel Manager — Internal Documentation