# Backup and Restore Guide

Complete guide for backing up and restoring your Nextcloud Stack.

## Automated Backups

### What Gets Backed Up

- **Daily (3:00 AM)**:
  - PostgreSQL database dump
  
- **Weekly (Sundays, 3:00 AM)**:
  - Nextcloud data volume
  - Configuration files

- **Retention**: 30 days

### Backup Locations

```
/opt/nextcloud-stack/backups/
├── database/
│   └── nextcloud_db_YYYYMMDD_HHMMSS.sql.gz
├── volumes/
│   ├── nextcloud_data_YYYYMMDD_HHMMSS.tar.gz
│   └── configs_YYYYMMDD_HHMMSS.tar.gz
└── backup.log
```

### Manual Backup

```bash
ssh user@server
/opt/nextcloud-stack/backup.sh
```

### Check Backup Status

```bash
# View backup log
tail -f /opt/nextcloud-stack/backups/backup.log

# List backups
ls -lh /opt/nextcloud-stack/backups/database/
ls -lh /opt/nextcloud-stack/backups/volumes/

# Check disk usage
du -sh /opt/nextcloud-stack/backups/
```

---

## Restore Procedures

### 1. Restore Database Only

**When to use:**
- Database corruption
- Accidental data deletion
- Need to revert to earlier state

**Steps:**

```bash
# 1. Stop Nextcloud (keep database running)
cd /opt/nextcloud-stack
docker compose stop next

# 2. List available backups
ls -lh backups/database/

# 3. Choose backup to restore
BACKUP_FILE="backups/database/nextcloud_db_20260216_030000.sql.gz"

# 4. Drop existing database (CAUTION!)
docker exec next-db psql -U nextcloud -c "DROP DATABASE nextcloud;"
docker exec next-db psql -U nextcloud -c "CREATE DATABASE nextcloud;"

# 5. Restore from backup
gunzip < $BACKUP_FILE | docker exec -i next-db psql -U nextcloud -d nextcloud

# 6. Restart Nextcloud
docker compose start next

# 7. Run Nextcloud upgrade (if needed)
docker exec -u www-data next php occ upgrade

# 8. Disable maintenance mode
docker exec -u www-data next php occ maintenance:mode --off
```

### 2. Restore Data Volumes

**When to use:**
- Lost files
- Corrupted data directory
- Complete system failure

**Steps:**

```bash
# 1. Stop all services
cd /opt/nextcloud-stack
docker compose down

# 2. List available backups
ls -lh backups/volumes/

# 3. Choose backup
BACKUP_FILE="backups/volumes/nextcloud_data_20260216_030000.tar.gz"

# 4. Restore volume
# Note: This requires accessing the Docker volume directory
sudo tar -xzf $BACKUP_FILE -C /var/lib/docker/volumes/nextcloud_nextcloud_data/_data/

# 5. Restore configs (optional)
CONFIG_BACKUP="backups/volumes/configs_20260216_030000.tar.gz"
sudo tar -xzf $CONFIG_BACKUP -C /opt/nextcloud-stack/configs/

# 6. Fix permissions
sudo chown -R www-data:www-data /var/lib/docker/volumes/nextcloud_nextcloud_data/_data/

# 7. Start services
docker compose up -d

# 8. Run Nextcloud file scan
docker exec -u www-data next php occ files:scan --all
```

### 3. Complete System Restore

**When to use:**
- Disaster recovery
- Migration to new server
- Complete system failure

**Steps:**

```bash
# 1. On new server, install base system
# Run Ansible playbooks 01-04 (up to Tailscale)
ansible-playbook playbooks/01-preflight-checks.yml --ask-vault-pass
ansible-playbook playbooks/02-system-setup.yml --ask-vault-pass
ansible-playbook playbooks/03-docker-setup.yml --ask-vault-pass
ansible-playbook playbooks/04-tailscale-setup.yml --ask-vault-pass

# 2. Create deployment directory structure
ssh user@new-server
sudo mkdir -p /opt/nextcloud-stack/backups/{database,volumes}

# 3. Copy backups from old server to new server
# On your local machine:
scp -r old-server:/opt/nextcloud-stack/backups/* user@new-server:/opt/nextcloud-stack/backups/

# 4. Deploy stack (creates volumes and containers)
ansible-playbook playbooks/05-deploy-stack.yml --ask-vault-pass

# 5. Stop services on new server
ssh user@new-server
cd /opt/nextcloud-stack
docker compose down

# 6. Restore database
BACKUP_FILE="backups/database/nextcloud_db_20260216_030000.sql.gz"
docker compose up -d next-db next-redis
sleep 10
gunzip < $BACKUP_FILE | docker exec -i next-db psql -U nextcloud -d nextcloud

# 7. Restore data volumes
VOLUME_BACKUP="backups/volumes/nextcloud_data_20260216_030000.tar.gz"
sudo tar -xzf $VOLUME_BACKUP -C /var/lib/docker/volumes/nextcloud_nextcloud_data/_data/

# 8. Restore configs
CONFIG_BACKUP="backups/volumes/configs_20260216_030000.tar.gz"
sudo tar -xzf $CONFIG_BACKUP -C /opt/nextcloud-stack/configs/

# 9. Fix permissions
sudo chown -R www-data:www-data /var/lib/docker/volumes/nextcloud_nextcloud_data/_data/

# 10. Start all services
docker compose up -d

# 11. Run Nextcloud maintenance
docker exec -u www-data next php occ maintenance:mode --off
docker exec -u www-data next php occ files:scan --all
docker exec -u www-data next php occ db:add-missing-indices

# 12. Continue with remaining playbooks
exit
ansible-playbook playbooks/06-configure-caddy.yml --ask-vault-pass
ansible-playbook playbooks/07-setup-backups.yml --ask-vault-pass
ansible-playbook playbooks/08-post-deployment.yml --ask-vault-pass
```

---

## Off-Site Backups with rclone

### Initial Setup

```bash
ssh user@server

# Configure rclone
rclone config

# Example: Setup Backblaze B2
# Follow prompts:
# - Choose: New remote
# - Name: b2backup
# - Storage: Backblaze B2
# - Enter account ID and application key
```

### Sync Backups to Remote

```bash
# Test sync (dry run)
rclone sync /opt/nextcloud-stack/backups/ b2backup:nextcloud-backups --dry-run

# Actual sync
rclone sync /opt/nextcloud-stack/backups/ b2backup:nextcloud-backups
```

### Automated Off-Site Backups

Add to `/opt/nextcloud-stack/backup.sh` (after the cleanup section):

```bash
# Off-site backup (if rclone configured)
if command -v rclone &> /dev/null && rclone listremotes | grep -q "b2backup"; then
    log "Syncing backups to off-site storage..."
    rclone sync $BACKUP_DIR b2backup:nextcloud-backups --log-file=$LOG_FILE
    log "Off-site sync completed"
fi
```

### Restore from Off-Site Backup

```bash
# List remote backups
rclone ls b2backup:nextcloud-backups/database/

# Download specific backup
rclone copy b2backup:nextcloud-backups/database/nextcloud_db_20260216_030000.sql.gz /opt/nextcloud-stack/backups/database/

# Download all backups
rclone sync b2backup:nextcloud-backups /opt/nextcloud-stack/backups/
```

---

## Backup Verification

### Test Database Backup Integrity

```bash
# 1. Extract backup
gunzip -c backups/database/nextcloud_db_20260216_030000.sql.gz > /tmp/test_restore.sql

# 2. Check for errors
grep -i "error" /tmp/test_restore.sql

# 3. Verify size (should be reasonable)
ls -lh /tmp/test_restore.sql

# 4. Cleanup
rm /tmp/test_restore.sql
```

### Test Volume Backup Integrity

```bash
# Test tar archive
tar -tzf backups/volumes/nextcloud_data_20260216_030000.tar.gz | head -20

# Check for errors
tar -tzf backups/volumes/nextcloud_data_20260216_030000.tar.gz > /dev/null && echo "Archive OK"
```

---

## Migration to New Server

**Complete migration guide:**

1. **Prepare new server:**
   - Same or newer Ubuntu version
   - Run Ansible setup playbooks (01-04)

2. **Backup old server:**
   ```bash
   /opt/nextcloud-stack/backup.sh
   ```

3. **Copy backups to new server:**
   ```bash
   rsync -avz old-server:/opt/nextcloud-stack/backups/ new-server:/opt/nextcloud-stack/backups/
   ```

4. **Update DNS:**
   - Point all domains to new server IP
   - Wait for propagation

5. **Restore on new server:**
   - Follow "Complete System Restore" steps above

6. **Verify:**
   - Test all services
   - Check Nextcloud functionality
   - Verify SSL certificates

7. **Decommission old server:**
   - Only after confirming new server works
   - Keep final backup from old server

---

## Backup Best Practices

### DO:
- ✅ Test restores regularly (monthly)
- ✅ Keep backups off-site (rclone to cloud storage)
- ✅ Verify backup integrity
- ✅ Monitor backup log for errors
- ✅ Document restore procedures
- ✅ Keep multiple backup generations

### DON'T:
- ❌ Store backups only on same server
- ❌ Assume backups work without testing
- ❌ Delete old backups without verification
- ❌ Skip database backups
- ❌ Ignore backup failure notifications

---

## Backup Schedule Customization

Edit `/opt/nextcloud-stack/backup.sh` to change:

- Backup frequency
- Retention period
- What gets backed up
- Compression settings

Edit cron job:
```bash
sudo crontab -e

# Change from 3:00 AM to 2:00 AM
0 2 * * * /opt/nextcloud-stack/backup.sh >> /opt/nextcloud-stack/backups/backup.log 2>&1
```

---

## Troubleshooting Backups

### Backup script fails

**Check logs:**
```bash
tail -100 /opt/nextcloud-stack/backups/backup.log
```

**Common issues:**
- Disk space full
- Docker container not running
- Permission denied

**Solutions:**
```bash
# Check disk space
df -h

# Check containers
docker ps

# Fix permissions
sudo chown -R root:root /opt/nextcloud-stack/backup.sh
sudo chmod +x /opt/nextcloud-stack/backup.sh
```

### Database backup empty

**Verify database is running:**
```bash
docker exec next-db pg_isready -U nextcloud
```

**Test manual backup:**
```bash
docker exec next-db pg_dump -U nextcloud nextcloud > /tmp/test.sql
ls -lh /tmp/test.sql
```

---

## Emergency Recovery

If all backups are lost and you need to start fresh:

```bash
# 1. Remove everything
ansible-playbook playbooks/99-rollback.yml --ask-vault-pass
cd /opt/nextcloud-stack
docker compose down -v

# 2. Redeploy from scratch
ansible-playbook playbooks/site.yml --ask-vault-pass

# 3. Reconfigure Nextcloud manually
# Login and set up users, apps, etc.
```

---

**Remember:** Backups are worthless unless you've tested restoring from them!

**Last Updated:** 2026-02-16
