# Backup & restore

## Schedule

Host cron, daily 02:00, 7-day retention. Script: [`scripts/backup.sh`](../scripts/backup.sh) (deployed to `/opt/docker/backup.sh` on host). Output: `/opt/backups/YYYYMMDD_HHMMSS/`.

Backs up the whole nullstone Docker stack; the Minecraft block (lines 80-117 of backup.sh) handles MC.

## Strategy

| Server state | Backup type | Method |
|--------------|-------------|--------|
| Running | World snapshot + configs | `docker exec` tars worlds inside container (consistent), then `docker cp` out. Configs tarred from host. |
| Stopped | Full archive | Single tar of `/opt/docker/minecraft/` (worlds + plugins + configs). |

## Manual backup

```bash
ssh user@192.168.0.100 /opt/docker/backup.sh
```

## Manual archive (full server, while stopped)

```bash
ssh user@192.168.0.100 'cd /opt/docker/minecraft && docker compose down'
ssh user@192.168.0.100 'tar czf - -C /opt/docker minecraft' > minecraft-backup-$(date +%F).tar.gz
ssh user@192.168.0.100 'cd /opt/docker/minecraft && docker compose up -d'
```

## Restore

```bash
# Stop running server
ssh user@192.168.0.100 'cd /opt/docker/minecraft && docker compose down'

# Move current dir aside
ssh user@192.168.0.100 'mv /opt/docker/minecraft /opt/docker/minecraft.broken'

# Extract backup
scp minecraft-backup-YYYY-MM-DD.tar.gz user@192.168.0.100:/tmp/
ssh user@192.168.0.100 'cd /opt/docker && tar xzf /tmp/minecraft-backup-YYYY-MM-DD.tar.gz'

# Re-apply perms (userns-remap)
ssh user@192.168.0.100 'chmod -R 777 /opt/docker/minecraft'

# Boot
ssh user@192.168.0.100 'cd /opt/docker/minecraft && docker compose up -d'
```

## What's NOT in repo backups

- Plugin jars: re-downloaded fresh on each boot via `MODRINTH_PROJECTS` / `SPIGET_RESOURCES`.
- World data: only in `/opt/backups/` archives, never committed to git (binary, large, frequently changing).
- Player data (`usercache.json`, `ops.json`, `whitelist.json`): committed to repo *as snapshots only*; live state lives on host.
