Nextcloud Backup & Recovery Guide
Backing up your Nextcloud instance is crucial for data protection and disaster recovery. This guide covers comprehensive backup strategies for both Docker-based and host installations, with a focus on MariaDB database backups.
Never skip backups! Data loss can happen due to hardware failure, human error, corrupted updates, or security incidents. Regular backups are your insurance policy.
Why Backup Your Nextcloud?
- Data Protection: Protect against hardware failures, corrupted files, and accidental deletions
- Security: Recover from ransomware attacks or security breaches
- Updates: Safely rollback if an update causes issues
- Migration: Move your Nextcloud instance to new hardware
- Peace of Mind: Sleep better knowing your data is safe
What Needs to Be Backed Up
A complete Nextcloud backup includes:
- Database (MariaDB/MySQL/PostgreSQL)
- Nextcloud Files (user data, config, apps)
- Configuration Files (including Docker Compose files)
- External Storage (if applicable)
Docker-Based Backup Strategies
Prerequisites
Before starting, ensure you have sufficient storage space and appropriate permissions:
# Check available disk space
df -h
# Create backup directory
sudo mkdir -p /backup/nextcloud
sudo chown -R $USER:$USER /backup/nextcloud
Method 1: Complete Docker Backup Script
Create a comprehensive backup script for Docker installations:
- Backup Script
- Configuration
- Usage
#!/bin/bash
# Nextcloud Docker Backup Script
# ── Static paths ───────────────────────────────────────────────────────────────
BACKUP_DIR="/backup/nextcloud"
NEXTCLOUD_DIR="/opt/docker/nextcloud" # folder containing docker-compose.yaml and .env
RETENTION_DAYS=30
# ── Colors ─────────────────────────────────────────────────────────────────────
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
CYAN='\033[0;36m'
NC='\033[0m'
# ── Load .env (line-by-line to handle special characters safely) ───────────────
ENV_FILE="${NEXTCLOUD_DIR}/.env"
if [ -f "${ENV_FILE}" ]; then
echo -e "${CYAN}Loading configuration from ${ENV_FILE}${NC}"
while IFS= read -r line || [ -n "$line" ]; do
line="${line%$'\r'}"
[[ -z "$line" || "$line" =~ ^[[:space:]]*# ]] && continue
if [[ "$line" =~ ^([A-Za-z_][A-Za-z0-9_]*)=(.*)$ ]]; then
key="${BASH_REMATCH[1]}"; val="${BASH_REMATCH[2]}"
[[ "$val" =~ ^\"(.*)\"$ ]] && val="${BASH_REMATCH[1]}"
[[ "$val" =~ ^\'(.*)\'$ ]] && val="${BASH_REMATCH[1]}"
printf -v "$key" '%s' "$val"
export "$key"
fi
done < "${ENV_FILE}"
else
echo -e "${YELLOW}No .env found at ${ENV_FILE} — using built-in defaults${NC}"
fi
# ── Resolve config (env file values take priority, then hardcoded fallbacks) ────
NC_NAME_HINT="${CONTAINER_NAME:-nextcloud}"
DB_NAME_HINT="${DB_CONTAINER_NAME:-mariadb}"
DB_NAME="${MYSQL_DATABASE:-${DB_NAME:-nextcloud}}"
DB_USER="${MYSQL_USER:-${DB_USER:-nextcloud}}"
DB_PASSWORD="${MYSQL_PASSWORD:-${DB_PASSWORD:-}}"
# ── Fuzzy container lookup — handles Docker Compose project-name prefixes ──────
# --filter name= does substring matching, e.g. finds "nextcloud-nextcloud-1"
CONTAINER_NAME=$(docker ps --filter "name=${NC_NAME_HINT}" --format '{{.Names}}' \
| grep -E -v '(cron|installer|nginx|backup|harp|redis)' | head -1)
DB_CONTAINER_NAME=$(docker ps --filter "name=${DB_NAME_HINT}" --format '{{.Names}}' | head -1)
echo -e "${CYAN}Using config:${NC}"
echo " Nextcloud dir : ${NEXTCLOUD_DIR}"
echo " Nextcloud container: ${CONTAINER_NAME:-NOT FOUND (hint: ${NC_NAME_HINT})}"
echo " DB container : ${DB_CONTAINER_NAME:-NOT FOUND (hint: ${DB_NAME_HINT})}"
echo " DB name : ${DB_NAME}"
echo " DB user : ${DB_USER}"
echo " Backup dir : ${BACKUP_DIR}"
echo ""
# ── Validate required config ───────────────────────────────────────────────────
if [ -z "${DB_PASSWORD}" ]; then
echo -e "${RED}ERROR: Database password is not set.${NC}"
echo " → Expected: MYSQL_PASSWORD in ${ENV_FILE}"
echo " → Or export DB_PASSWORD before running this script."
exit 1
fi
DATE=$(date +%Y%m%d_%H%M%S)
BACKUP_NAME="nextcloud_backup_${DATE}"
echo -e "${GREEN}Starting Nextcloud backup: ${BACKUP_NAME}${NC}"
# ── Create backup staging directory ────────────────────────────────────────────
if ! mkdir -p "${BACKUP_DIR}/${BACKUP_NAME}"; then
echo -e "${RED}ERROR: Could not create backup directory ${BACKUP_DIR}/${BACKUP_NAME}${NC}"
echo " → Check that ${BACKUP_DIR} exists and is writable by $(whoami)."
echo " → Run: sudo mkdir -p ${BACKUP_DIR} && sudo chown $(whoami) ${BACKUP_DIR}"
exit 1
fi
# ── Step 1: Verify containers are running ─────────────────────────────────────
echo -e "${YELLOW}Checking containers...${NC}"
if [ -z "${CONTAINER_NAME}" ]; then
echo -e "${RED}ERROR: Could not find a running Nextcloud container matching '${NC_NAME_HINT}'.${NC}"
echo " → Running containers: $(docker ps --format '{{.Names}}' | tr '\n' ' ')"
echo " → Start it with: cd ${NEXTCLOUD_DIR} && docker compose up -d"
rm -rf "${BACKUP_DIR:?}/${BACKUP_NAME:?}"
exit 1
fi
if [ -z "${DB_CONTAINER_NAME}" ]; then
echo -e "${RED}ERROR: Could not find a running database container matching '${DB_NAME_HINT}'.${NC}"
echo " → Running containers: $(docker ps --format '{{.Names}}' | tr '\n' ' ')"
echo " → Start it with: cd ${NEXTCLOUD_DIR} && docker compose up -d"
rm -rf "${BACKUP_DIR:?}/${BACKUP_NAME:?}"
exit 1
fi
# ── Step 2: Enable maintenance mode ────────────────────────────────────────────
echo -e "${YELLOW}Enabling maintenance mode...${NC}"
if ! docker exec -u 33 "${CONTAINER_NAME}" php occ maintenance:mode --on; then
echo -e "${RED}ERROR: Could not enable maintenance mode.${NC}"
echo " → Container '${CONTAINER_NAME}' is running but occ failed."
echo " → Check PHP/container health: docker logs ${CONTAINER_NAME} --tail 30"
exit 1
fi
# ── Step 3: Backup database ────────────────────────────────────────────────────
echo -e "${YELLOW}Backing up MariaDB database '${DB_NAME}'...${NC}"
DB_DUMP="${BACKUP_DIR}/${BACKUP_NAME}/database.sql"
# Try mysqldump first (older MariaDB), fall back to mariadb-dump (newer)
if ! docker exec "${DB_CONTAINER_NAME}" mysqldump -u "${DB_USER}" -p"${DB_PASSWORD}" "${DB_NAME}" > "${DB_DUMP}" 2>/dev/null; then
if ! docker exec "${DB_CONTAINER_NAME}" mariadb-dump -u "${DB_USER}" -p"${DB_PASSWORD}" "${DB_NAME}" > "${DB_DUMP}" 2>/dev/null; then
echo -e "${RED}ERROR: Database dump failed.${NC}"
echo " → Container : ${DB_CONTAINER_NAME}"
echo " → DB user : ${DB_USER}"
echo " → DB name : ${DB_NAME}"
echo " → Common causes:"
echo " • Wrong password — check MYSQL_PASSWORD in ${ENV_FILE}"
echo " • DB user lacks DUMP privileges — run SHOW GRANTS FOR '${DB_USER}'@'%';"
echo " • Container not ready — check: docker logs ${DB_CONTAINER_NAME} --tail 30"
docker exec -u 33 "${CONTAINER_NAME}" php occ maintenance:mode --off
rm -rf "${BACKUP_DIR}/${BACKUP_NAME}"
exit 1
fi
fi
# Verify dump is non-empty
if [ ! -s "${DB_DUMP}" ]; then
echo -e "${RED}ERROR: Database dump file is empty — dump command may have silently failed.${NC}"
echo " → Check: docker logs ${DB_CONTAINER_NAME} --tail 30"
docker exec -u 33 "${CONTAINER_NAME}" php occ maintenance:mode --off
rm -rf "${BACKUP_DIR}/${BACKUP_NAME}"
exit 1
fi
echo -e "${GREEN}Database backup completed ($(du -h "${DB_DUMP}" | cut -f1))${NC}"
# ── Step 4: Backup Nextcloud data directory ────────────────────────────────────
echo -e "${YELLOW}Backing up Nextcloud data directory (${NEXTCLOUD_DIR})...${NC}"
DATA_ARCHIVE="${BACKUP_DIR}/${BACKUP_NAME}/nextcloud_data.tar.gz"
if ! tar -czf "${DATA_ARCHIVE}" -C "${NEXTCLOUD_DIR}" .; then
echo -e "${RED}ERROR: Data directory backup failed.${NC}"
echo " → Source path : ${NEXTCLOUD_DIR}"
echo " → Common causes:"
echo " • Path does not exist — verify NEXTCLOUD_DIR is correct"
echo " • Permission denied — run: ls -la ${NEXTCLOUD_DIR}"
echo " • Not enough disk space — run: df -h ${BACKUP_DIR}"
docker exec -u 33 "${CONTAINER_NAME}" php occ maintenance:mode --off
rm -rf "${BACKUP_DIR}/${BACKUP_NAME}"
exit 1
fi
echo -e "${GREEN}Data backup completed ($(du -h "${DATA_ARCHIVE}" | cut -f1))${NC}"
# ── Step 5: Backup Docker Compose config ──────────────────────────────────────
echo -e "${YELLOW}Backing up Docker Compose configuration...${NC}"
for f in docker-compose.yaml docker-compose.yml .env; do
[ -f "${NEXTCLOUD_DIR}/${f}" ] && cp "${NEXTCLOUD_DIR}/${f}" "${BACKUP_DIR}/${BACKUP_NAME}/"
done
# ── Step 6: Write backup manifest ─────────────────────────────────────────────
cat > "${BACKUP_DIR}/${BACKUP_NAME}/backup_info.txt" << EOF
Nextcloud Backup Information
============================
Backup Date : $(date)
Backup Name : ${BACKUP_NAME}
Nextcloud container: ${CONTAINER_NAME}
DB container : ${DB_CONTAINER_NAME}
DB name : ${DB_NAME}
Nextcloud dir : ${NEXTCLOUD_DIR}
Files:
database.sql — MariaDB dump
nextcloud_data.tar.gz — Nextcloud data directory
docker-compose.yaml — Compose file (if found)
.env — Environment variables (if found)
EOF
# ── Step 7: Disable maintenance mode ──────────────────────────────────────────
echo -e "${YELLOW}Disabling maintenance mode...${NC}"
if ! docker exec -u 33 "${CONTAINER_NAME}" php occ maintenance:mode --off; then
echo -e "${RED}WARNING: Could not disable maintenance mode — Nextcloud may still be offline.${NC}"
echo " → Run manually: docker exec -u 33 ${CONTAINER_NAME} php occ maintenance:mode --off"
exit 1
fi
# ── Step 8: Create final archive ───────────────────────────────────────────────
echo -e "${YELLOW}Creating compressed archive...${NC}"
FINAL_ARCHIVE="${BACKUP_DIR}/${BACKUP_NAME}.tar.gz"
if ! tar -czf "${FINAL_ARCHIVE}" -C "${BACKUP_DIR}" "${BACKUP_NAME}/"; then
echo -e "${RED}ERROR: Could not create final archive ${FINAL_ARCHIVE}${NC}"
echo " → Staging files are still at ${BACKUP_DIR}/${BACKUP_NAME}/"
echo " → Check disk space: df -h ${BACKUP_DIR}"
exit 1
fi
rm -rf "${BACKUP_DIR}/${BACKUP_NAME}"
# ── Step 9: Prune old backups ──────────────────────────────────────────────────
echo -e "${YELLOW}Removing backups older than ${RETENTION_DAYS} days...${NC}"
find "${BACKUP_DIR}" -name "nextcloud_backup_*.tar.gz" -mtime +${RETENTION_DAYS} -delete
BACKUP_SIZE=$(du -h "${FINAL_ARCHIVE}" | cut -f1)
echo -e "${GREEN}Backup complete!${NC}"
echo " Location : ${FINAL_ARCHIVE}"
echo " Size : ${BACKUP_SIZE}"
# Optional: uncomment to send an email notification
# echo "Nextcloud backup completed: ${BACKUP_NAME} (${BACKUP_SIZE})" | mail -s "Nextcloud Backup Success" admin@yourdomain.com
The script automatically reads ${NEXTCLOUD_DIR}/.env (default: /opt/docker/nextcloud/.env) and maps:
.env variable | Script variable | Fallback |
|---|---|---|
MYSQL_PASSWORD | DB_PASSWORD | (required — script exits if missing) |
MYSQL_DATABASE | DB_NAME | nextcloud |
MYSQL_USER | DB_USER | nextcloud |
CONTAINER_NAME | CONTAINER_NAME | nextcloud |
DB_CONTAINER_NAME | DB_CONTAINER_NAME | mariadb |
Only two things need to be set directly in the script if your paths differ:
# Edit these two lines at the top of the script
BACKUP_DIR="/backup/nextcloud" # Where backups are stored
NEXTCLOUD_DIR="/opt/docker/nextcloud" # Folder containing docker-compose.yaml and .env
Make the script executable:
chmod +x nextcloud-backup.sh
Run the backup script:
# Run manually
./nextcloud-backup.sh
# Run with sudo if needed for Docker permissions
sudo ./nextcloud-backup.sh
# Test the script first
./nextcloud-backup.sh --dry-run # (if you add dry-run functionality)
Automate with cron:
# Edit crontab
crontab -e
# Add daily backup at 2 AM
0 2 * * * /path/to/nextcloud-backup.sh >> /var/log/nextcloud-backup.log 2>&1
# Add weekly backup on Sundays at 1 AM
0 1 * * 0 /path/to/nextcloud-backup.sh >> /var/log/nextcloud-backup.log 2>&1
Method 2: Individual Component Backup
For more granular control, you can backup components individually:
Database Backup
# Create database backup
docker exec mariadb mysqldump -u nextcloud -pYOUR_PASSWORD nextcloud > nextcloud_db_$(date +%Y%m%d).sql
# Compress the backup
gzip nextcloud_db_$(date +%Y%m%d).sql
# Alternative: Direct compressed backup
docker exec mariadb mysqldump -u nextcloud -pYOUR_PASSWORD nextcloud | gzip > nextcloud_db_$(date +%Y%m%d).sql.gz
Data Directory Backup
# Stop containers for consistent backup
docker compose down
# Backup data directory
tar -czf nextcloud_data_$(date +%Y%m%d).tar.gz -C /var/docker/nextcloud .
# Start containers
docker compose up -d
Configuration Backup
# Backup Docker configuration
cp /var/docker/nextcloud/docker-compose.yaml /backup/
cp /var/docker/nextcloud/.env /backup/
# Backup Nextcloud config
cp /var/docker/nextcloud/nextcloud/config/config.php /backup/
Host Installation Backup Strategies
Method 1: Complete Host Backup Script
For traditional server installations:
#!/bin/bash
# Nextcloud Host Installation Backup Script
# Configuration
BACKUP_DIR="/backup/nextcloud"
NEXTCLOUD_DIR="/var/www/nextcloud"
NEXTCLOUD_DATA_DIR="/var/nextcloud/data"
DB_NAME="nextcloud"
DB_USER="nextcloud"
DB_PASSWORD="YOUR_DB_PASSWORD"
WEB_USER="www-data"
DATE=$(date +%Y%m%d_%H%M%S)
BACKUP_NAME="nextcloud_host_backup_${DATE}"
echo "Starting Nextcloud host backup: ${BACKUP_NAME}"
# Create backup directory
mkdir -p "${BACKUP_DIR}/${BACKUP_NAME}"
# Enable maintenance mode
sudo -u ${WEB_USER} php ${NEXTCLOUD_DIR}/occ maintenance:mode --on
# Backup database
echo "Backing up database..."
mysqldump -u ${DB_USER} -p${DB_PASSWORD} ${DB_NAME} > "${BACKUP_DIR}/${BACKUP_NAME}/database.sql"
# Backup Nextcloud installation
echo "Backing up Nextcloud installation..."
tar -czf "${BACKUP_DIR}/${BACKUP_NAME}/nextcloud_install.tar.gz" -C $(dirname ${NEXTCLOUD_DIR}) $(basename ${NEXTCLOUD_DIR})
# Backup data directory (if separate)
if [ "${NEXTCLOUD_DATA_DIR}" != "${NEXTCLOUD_DIR}/data" ]; then
echo "Backing up data directory..."
tar -czf "${BACKUP_DIR}/${BACKUP_NAME}/nextcloud_data.tar.gz" -C $(dirname ${NEXTCLOUD_DATA_DIR}) $(basename ${NEXTCLOUD_DATA_DIR})
fi
# Backup web server configuration
echo "Backing up web server configuration..."
if [ -d "/etc/nginx" ]; then
tar -czf "${BACKUP_DIR}/${BACKUP_NAME}/nginx_config.tar.gz" -C /etc nginx
fi
if [ -d "/etc/apache2" ]; then
tar -czf "${BACKUP_DIR}/${BACKUP_NAME}/apache_config.tar.gz" -C /etc apache2
fi
# Disable maintenance mode
sudo -u ${WEB_USER} php ${NEXTCLOUD_DIR}/occ maintenance:mode --off
# Create backup archive
cd "${BACKUP_DIR}"
tar -czf "${BACKUP_NAME}.tar.gz" "${BACKUP_NAME}/"
rm -rf "${BACKUP_NAME}/"
echo "Backup completed: ${BACKUP_DIR}/${BACKUP_NAME}.tar.gz"
Method 2: Rsync-based Backup
For incremental backups with rsync:
#!/bin/bash
# Incremental backup with rsync
BACKUP_DIR="/backup/nextcloud"
NEXTCLOUD_DIR="/var/www/nextcloud"
NEXTCLOUD_DATA_DIR="/var/nextcloud/data"
# Create incremental backup
rsync -av --delete "${NEXTCLOUD_DIR}/" "${BACKUP_DIR}/current/nextcloud/"
rsync -av --delete "${NEXTCLOUD_DATA_DIR}/" "${BACKUP_DIR}/current/data/"
# Create daily snapshot
cp -al "${BACKUP_DIR}/current" "${BACKUP_DIR}/daily.$(date +%Y%m%d)"
# Cleanup old snapshots (keep 30 days)
find "${BACKUP_DIR}" -maxdepth 1 -name "daily.*" -mtime +30 -exec rm -rf {} \;
Recovery Procedures
Docker Recovery
- Full Recovery
- Database Only
- Files Only
Complete system recovery from backup:
# 1. Stop existing containers
cd /var/docker/nextcloud
docker compose down
# 2. Remove existing data (CAUTION!)
sudo rm -rf nextcloud/ mariadb/
# 3. Extract backup
cd /backup/nextcloud
tar -xzf nextcloud_backup_YYYYMMDD_HHMMSS.tar.gz
cd nextcloud_backup_YYYYMMDD_HHMMSS
# 4. Restore configuration files
cp docker-compose.yaml /var/docker/nextcloud/
cp .env /var/docker/nextcloud/
# 5. Extract data
cd /var/docker/nextcloud
tar -xzf /backup/nextcloud/nextcloud_backup_YYYYMMDD_HHMMSS/nextcloud_data.tar.gz
# 6. Start database container first
docker compose up -d mariadb
sleep 30
# 7. Restore database
docker exec -i mariadb mysql -u nextcloud -pYOUR_PASSWORD nextcloud < /backup/nextcloud/nextcloud_backup_YYYYMMDD_HHMMSS/database.sql
# 8. Start all containers
docker compose up -d
# 9. Verify restoration
docker exec -u 33 nextcloud php occ status
Database-only recovery:
# 1. Enable maintenance mode
docker exec -u 33 nextcloud php occ maintenance:mode --on
# 2. Restore database
docker exec -i mariadb mysql -u nextcloud -pYOUR_PASSWORD nextcloud < database_backup.sql
# 3. Run Nextcloud upgrade/repair
docker exec -u 33 nextcloud php occ upgrade
docker exec -u 33 nextcloud php occ db:add-missing-indices
docker exec -u 33 nextcloud php occ db:add-missing-columns
# 4. Disable maintenance mode
docker exec -u 33 nextcloud php occ maintenance:mode --off
Files-only recovery:
# 1. Stop containers
docker compose down
# 2. Restore files
tar -xzf nextcloud_data_backup.tar.gz -C /var/docker/nextcloud/
# 3. Fix permissions
sudo chown -R 33:33 /var/docker/nextcloud/nextcloud/
# 4. Start containers
docker compose up -d
# 5. Rescan files
docker exec -u 33 nextcloud php occ files:scan --all
Host Installation Recovery
# 1. Stop web server
sudo systemctl stop nginx # or apache2
# 2. Enable maintenance mode (if possible)
sudo -u www-data php /var/www/nextcloud/occ maintenance:mode --on
# 3. Restore database
mysql -u nextcloud -pYOUR_PASSWORD nextcloud < database_backup.sql
# 4. Restore files
tar -xzf nextcloud_install_backup.tar.gz -C /var/www/
tar -xzf nextcloud_data_backup.tar.gz -C /var/nextcloud/
# 5. Fix permissions
sudo chown -R www-data:www-data /var/www/nextcloud/
sudo chown -R www-data:www-data /var/nextcloud/data/
# 6. Start web server
sudo systemctl start nginx
# 7. Disable maintenance mode
sudo -u www-data php /var/www/nextcloud/occ maintenance:mode --off
Best Practices
Backup Strategy
- 3-2-1 Rule: 3 copies of data, 2 different media types, 1 offsite
- Regular Schedule: Daily incremental, weekly full backups
- Test Restores: Regularly test your backup restoration process
- Monitor Backups: Set up alerts for backup failures
- Document Process: Keep recovery procedures documented and accessible
Security Considerations
# Encrypt backups
gpg --symmetric --cipher-algo AES256 nextcloud_backup.tar.gz
# Secure backup storage permissions
chmod 600 /backup/nextcloud/*
chown backup-user:backup-group /backup/nextcloud/*
# Use dedicated backup user
useradd -r -s /bin/false nextcloud-backup
Storage Considerations
- Local Backups: Fast recovery, but vulnerable to local disasters
- Remote Backups: Slower recovery, but protected from local disasters
- Cloud Storage: Consider services like AWS S3, Google Cloud, or Backblaze B2
# Example: Upload to cloud storage
aws s3 cp nextcloud_backup_${DATE}.tar.gz s3://your-backup-bucket/nextcloud/
# Example: Remote backup with rsync
rsync -avz --delete /backup/nextcloud/ user@backup-server:/backups/nextcloud/
Automated Backup Solutions
Using Cron for Automation
# Edit crontab
crontab -e
# Daily backup at 2 AM
0 2 * * * /usr/local/bin/nextcloud-backup.sh >> /var/log/nextcloud-backup.log 2>&1
# Weekly full backup on Sundays at 1 AM
0 1 * * 0 /usr/local/bin/nextcloud-full-backup.sh >> /var/log/nextcloud-backup.log 2>&1
# Monthly backup cleanup (keep 3 months)
0 3 1 * * find /backup/nextcloud -name "*.tar.gz" -mtime +90 -delete
Backup Monitoring Script
#!/bin/bash
# Monitor backup completion and size
BACKUP_DIR="/backup/nextcloud"
EXPECTED_MIN_SIZE="100M" # Adjust based on your data size
NOTIFICATION_EMAIL="admin@yourdomain.com"
# Check if today's backup exists
TODAY=$(date +%Y%m%d)
BACKUP_FILE=$(find ${BACKUP_DIR} -name "*${TODAY}*.tar.gz" -type f | head -1)
if [ -z "${BACKUP_FILE}" ]; then
echo "ERROR: No backup found for today (${TODAY})" | mail -s "Nextcloud Backup Missing" ${NOTIFICATION_EMAIL}
exit 1
fi
# Check backup size
BACKUP_SIZE=$(du -h "${BACKUP_FILE}" | cut -f1)
BACKUP_SIZE_BYTES=$(du -b "${BACKUP_FILE}" | cut -f1)
MIN_SIZE_BYTES=$(echo ${EXPECTED_MIN_SIZE} | numfmt --from=iec)
if [ ${BACKUP_SIZE_BYTES} -lt ${MIN_SIZE_BYTES} ]; then
echo "WARNING: Backup size (${BACKUP_SIZE}) is smaller than expected (${EXPECTED_MIN_SIZE})" | mail -s "Nextcloud Backup Size Warning" ${NOTIFICATION_EMAIL}
fi
echo "Backup successful: ${BACKUP_FILE} (${BACKUP_SIZE})"
Troubleshooting
Common Backup Issues
Database Connection Errors:
# Test database connection
docker exec mariadb mysql -u nextcloud -pYOUR_PASSWORD -e "SELECT 1;"
# Check container status
docker ps
docker logs mariadb
Permission Issues:
# Fix Nextcloud permissions
docker exec nextcloud chown -R 33:33 /var/www/html
# Fix backup directory permissions
sudo chown -R $USER:$USER /backup/nextcloud
Disk Space Issues:
# Check disk space
df -h /backup
# Clean old backups
find /backup/nextcloud -name "*.tar.gz" -mtime +30 -delete
# Compress existing backups
gzip /backup/nextcloud/*.sql
Recovery Issues
Database Import Errors:
# Check database encoding
docker exec mariadb mysql -u nextcloud -pYOUR_PASSWORD -e "SHOW VARIABLES LIKE 'character_set%';"
# Import with specific charset
docker exec -i mariadb mysql -u nextcloud -pYOUR_PASSWORD --default-character-set=utf8mb4 nextcloud < database.sql
File Permission Errors:
# Reset Nextcloud permissions (Docker)
docker exec nextcloud chown -R 33:33 /var/www/html
docker exec nextcloud chmod -R 755 /var/www/html
# Reset Nextcloud permissions (Host)
sudo chown -R www-data:www-data /var/www/nextcloud
sudo chmod -R 755 /var/www/nextcloud
Testing Your Backups
Backup Verification Checklist
-
File Integrity:
# Test backup archivetar -tzf nextcloud_backup.tar.gz > /dev/null && echo "Archive OK" || echo "Archive CORRUPTED"# Test database dumpmysql -u nextcloud -pYOUR_PASSWORD --execute="SET SQL_MODE='NO_AUTO_VALUE_ON_ZERO'; SOURCE database.sql;" 2>/dev/null && echo "Database OK" || echo "Database CORRUPTED" -
Restoration Test:
- Set up a test environment
- Restore from backup
- Verify all functionality works
- Test user login and file access
-
Regular Testing Schedule:
- Monthly: Quick verification tests
- Quarterly: Full restoration tests
- Annually: Disaster recovery simulation
Conclusion
A robust backup strategy is essential for any Nextcloud installation. Whether you're running Docker containers or a host installation, regular automated backups combined with tested recovery procedures will protect your valuable data.
Remember to:
- Test your backups regularly
- Keep multiple backup copies
- Store backups in different locations
- Document your recovery procedures
- Monitor backup completion and file integrity
For additional help with backup and recovery procedures, visit our Discord or leave a comment below.
💬 Recent Comments