Azure Blob Offsite Backup Setup
Azure Blob Offsite Backup Setup
Overview
Purpose: Offsite backup for ALAI system databases and git bundles
Region: North Europe (Dublin) — geographic separation from primary Sweden Central VM
Retention: 365 days with lifecycle policies (Hot → Cool → Archive → Delete)
Recovery Time Objective: 4 hours (manual restore)
Azure Resources
| Resource Type | Name | Purpose |
|---|---|---|
| Resource Group | alai-backups-rg | Isolation boundary for backup storage |
| Storage Account | alaibackups0ebb | Blob storage (LRS, Standard tier) |
| Container | system-db-backups | SQLite databases (hivemind.db, mission-control.db, etc.) |
| Container | system-git-bundles | Git repository bundles |
| Service Principal | alai-backup-writer | Scoped write-only access (Storage Blob Data Contributor) |
Service Principal Setup
# Create service principal
az ad sp create-for-rbac --name alai-backup-writer --skip-assignment
# Assign Storage Blob Data Contributor to SA only (not subscription)
STORAGE_ID=$(az storage account show --name alaibackups0ebb --query id -o tsv)
az role assignment create \
--assignee <service-principal-app-id> \
--role "Storage Blob Data Contributor" \
--scope "$STORAGE_ID"
# Store credentials in ~/system/config/azure-backup.env
cat > ~/system/config/azure-backup.env <
Lifecycle Policy
Hot → Cool: 30 days
Cool → Archive: 90 days
Archive → Delete: 365 days
Delete blobs: Last modified > 365 days
az storage account management-policy create \
--account-name alaibackups0ebb \
--policy @lifecycle-policy.json
lifecycle-policy.json:
{
"rules": [
{
"enabled": true,
"name": "archive-old-backups",
"type": "Lifecycle",
"definition": {
"actions": {
"baseBlob": {
"tierToCool": {"daysAfterModificationGreaterThan": 30},
"tierToArchive": {"daysAfterModificationGreaterThan": 90},
"delete": {"daysAfterModificationGreaterThan": 365}
}
},
"filters": {"blobTypes": ["blockBlob"]}
}
}
]
}
Backup Scripts
LightRAG to Azure Blob
#!/bin/bash
# ~/system/tools/migrate-lightrag-to-azure.sh
source ~/system/config/azure-backup.env
TIMESTAMP=$(date +%Y%m%d-%H%M%S)
BACKUP_FILE="/tmp/lightrag-backup-$TIMESTAMP.tar.gz"
tar -czf "$BACKUP_FILE" ~/system/lightrag/
az storage blob upload \
--account-name alaibackups0ebb \
--container-name system-db-backups \
--name "lightrag-$TIMESTAMP.tar.gz" \
--file "$BACKUP_FILE" \
--auth-mode login
rm "$BACKUP_FILE"
Ollama Models Export
#!/bin/bash
# ~/system/tools/ollama-models-export.sh --azure
source ~/system/config/azure-backup.env
TIMESTAMP=$(date +%Y%m%d-%H%M%S)
EXPORT_DIR="/tmp/ollama-export-$TIMESTAMP"
mkdir -p "$EXPORT_DIR"
ollama list | tail -n +2 | awk '{print $1}' > "$EXPORT_DIR/model-list.txt"
while read -r model; do
ollama show "$model" --modelfile > "$EXPORT_DIR/$model.modelfile"
done < "$EXPORT_DIR/model-list.txt"
tar -czf "$EXPORT_DIR.tar.gz" "$EXPORT_DIR"
az storage blob upload \
--account-name alaibackups0ebb \
--container-name system-db-backups \
--name "ollama-models-$TIMESTAMP.tar.gz" \
--file "$EXPORT_DIR.tar.gz"
rm -rf "$EXPORT_DIR" "$EXPORT_DIR.tar.gz"
Disaster Recovery Path
- List available backups:
az storage blob list \
--account-name alaibackups0ebb \
--container-name system-db-backups \
--output table
- Download latest backup:
az storage blob download \
--account-name alaibackups0ebb \
--container-name system-db-backups \
--name "lightrag-20260420-143000.tar.gz" \
--file /tmp/restore-lightrag.tar.gz
- Verify SHA-256 checksum:
shasum -a 256 /tmp/restore-lightrag.tar.gz
- Restore to target system:
tar -xzf /tmp/restore-lightrag.tar.gz -C ~/system/
Monitoring
- Cron: Hourly backup at :15 (15 * * * *)
- Log:
~/system/logs/azure-backup.log - Alert: HiveMind alert if backup fails 2 consecutive runs
node ~/system/agents/hivemind/hivemind.js post john alert \
"Azure backup failed 2 consecutive runs — check ~/system/logs/azure-backup.log"