Skip to main content

Overview

Regular backups are essential to protect against data loss, hardware failure, and security incidents. This guide covers comprehensive backup strategies for all Wecode components.

What to Back Up

Critical Data (Must Back Up)

  1. Database - All user data, submissions, grades
  2. Assignments Folder - Student-submitted code files
  3. Configuration Files - .env, custom configs
  4. Tester Folder - Test cases and grading scripts

Important Data (Should Back Up)

  1. Storage Folder - Logs, uploaded files, cache
  2. Custom Code - Any modifications to Wecode core
  3. Web Server Configuration - Nginx/Apache configs

Optional Data

  1. Vendor Folder - Can be regenerated with composer install
  2. Node Modules - Can be regenerated with npm install
  3. Compiled Assets - Can be regenerated with build commands

Database Backup

Manual Database Backup

MySQL/MariaDB

# Basic backup
mysqldump -u wecode_user -p wecode_db > backup.sql

# With timestamp
mysqldump -u wecode_user -p wecode_db > "wecode_$(date +%Y%m%d_%H%M%S).sql"

# Compressed backup (saves space)
mysqldump -u wecode_user -p wecode_db | gzip > "wecode_$(date +%Y%m%d_%H%M%S).sql.gz"

# Include routines and triggers
mysqldump -u wecode_user -p --routines --triggers wecode_db > backup.sql

PostgreSQL

# Basic backup
pg_dump -U wecode_user -d wecode_db > backup.sql

# Compressed backup
pg_dump -U wecode_user -d wecode_db | gzip > "wecode_$(date +%Y%m%d_%H%M%S).sql.gz"

# Custom format (faster restore)
pg_dump -U wecode_user -d wecode_db -Fc > backup.dump

Automated Database Backup Script

Create /usr/local/bin/wecode-db-backup.sh:
#!/bin/bash

# Configuration
DB_USER="wecode_user"
DB_NAME="wecode_db"
DB_PASS="your_password"  # Better: read from secure location
BACKUP_DIR="/var/backups/wecode/database"
RETENTION_DAYS=30

# Create backup directory
mkdir -p "$BACKUP_DIR"

# Generate filename with timestamp
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
BACKUP_FILE="$BACKUP_DIR/wecode_db_$TIMESTAMP.sql.gz"

# Perform backup
mysqldump -u "$DB_USER" -p"$DB_PASS" "$DB_NAME" | gzip > "$BACKUP_FILE"

# Check if backup was successful
if [ $? -eq 0 ]; then
    echo "Database backup successful: $BACKUP_FILE"
    
    # Remove old backups
    find "$BACKUP_DIR" -name "wecode_db_*.sql.gz" -mtime +$RETENTION_DAYS -delete
    echo "Removed backups older than $RETENTION_DAYS days"
else
    echo "Database backup failed!"
    exit 1
fi
Make it executable:
sudo chmod +x /usr/local/bin/wecode-db-backup.sh

Schedule Database Backups with Cron

# Edit crontab
sudo crontab -e

# Add daily backup at 2 AM
0 2 * * * /usr/local/bin/wecode-db-backup.sh >> /var/log/wecode-backup.log 2>&1

# Or every 6 hours
0 */6 * * * /usr/local/bin/wecode-db-backup.sh >> /var/log/wecode-backup.log 2>&1

Assignments Folder Backup

The assignments folder contains all student-submitted code and grows over time.

Manual Backup

# Compress and backup assignments folder
tar -czf "assignments_$(date +%Y%m%d_%H%M%S).tar.gz" /var/wecode-data/assignments/

# Backup to remote location
rsync -avz /var/wecode-data/assignments/ user@backup-server:/backups/wecode/assignments/

Automated Backup Script

Create /usr/local/bin/wecode-files-backup.sh:
#!/bin/bash

# Configuration
SOURCE_DIRS=(
    "/var/wecode-data/assignments"
    "/var/wecode-data/tester"
    "/var/www/wecode/storage"
)
BACKUP_DIR="/var/backups/wecode/files"
RETENTION_DAYS=30

# Create backup directory
mkdir -p "$BACKUP_DIR"

# Generate timestamp
TIMESTAMP=$(date +%Y%m%d_%H%M%S)

# Backup each directory
for DIR in "${SOURCE_DIRS[@]}"; do
    if [ -d "$DIR" ]; then
        BASENAME=$(basename "$DIR")
        BACKUP_FILE="$BACKUP_DIR/${BASENAME}_$TIMESTAMP.tar.gz"
        
        echo "Backing up $DIR..."
        tar -czf "$BACKUP_FILE" "$DIR"
        
        if [ $? -eq 0 ]; then
            echo "Success: $BACKUP_FILE"
        else
            echo "Failed: $DIR"
        fi
    else
        echo "Directory not found: $DIR"
    fi
done

# Remove old backups
find "$BACKUP_DIR" -name "*.tar.gz" -mtime +$RETENTION_DAYS -delete
echo "Cleanup complete. Removed backups older than $RETENTION_DAYS days."
Make executable and schedule:
sudo chmod +x /usr/local/bin/wecode-files-backup.sh

# Add to crontab - daily at 3 AM
sudo crontab -e
0 3 * * * /usr/local/bin/wecode-files-backup.sh >> /var/log/wecode-backup.log 2>&1

Configuration Files Backup

Files to Back Up

# Wecode configuration
/var/www/wecode/.env
/var/www/wecode/config/*

# Web server configuration
/etc/nginx/sites-available/wecode
/etc/nginx/nginx.conf
# or
/etc/apache2/sites-available/wecode.conf

# PHP configuration
/etc/php/8.4/fpm/php.ini
/etc/php/8.4/fpm/pool.d/www.conf

# Docker Compose (if used)
/var/www/wecode/compose.yaml

Configuration Backup Script

Create /usr/local/bin/wecode-config-backup.sh:
#!/bin/bash

BACKUP_DIR="/var/backups/wecode/config"
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
BACKUP_FILE="$BACKUP_DIR/config_$TIMESTAMP.tar.gz"

mkdir -p "$BACKUP_DIR"

tar -czf "$BACKUP_FILE" \
    /var/www/wecode/.env \
    /var/www/wecode/config/ \
    /etc/nginx/sites-available/wecode \
    /etc/php/8.4/fpm/php.ini \
    2>/dev/null

if [ $? -eq 0 ]; then
    echo "Configuration backup successful: $BACKUP_FILE"
else
    echo "Configuration backup completed with warnings"
fi

# Keep last 90 days
find "$BACKUP_DIR" -name "config_*.tar.gz" -mtime +90 -delete

Complete System Backup

Full Backup Script

Create /usr/local/bin/wecode-full-backup.sh:
#!/bin/bash

set -e  # Exit on error

# Configuration
BACKUP_ROOT="/var/backups/wecode"
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
BACKUP_DIR="$BACKUP_ROOT/full/$TIMESTAMP"

DB_USER="wecode_user"
DB_NAME="wecode_db"
DB_PASS="your_password"

echo "Starting full Wecode backup..."
mkdir -p "$BACKUP_DIR"

# 1. Database
echo "Backing up database..."
mysqldump -u "$DB_USER" -p"$DB_PASS" "$DB_NAME" | gzip > "$BACKUP_DIR/database.sql.gz"

# 2. Assignments
echo "Backing up assignments..."
tar -czf "$BACKUP_DIR/assignments.tar.gz" /var/wecode-data/assignments/

# 3. Tester
echo "Backing up tester..."
tar -czf "$BACKUP_DIR/tester.tar.gz" /var/wecode-data/tester/

# 4. Storage
echo "Backing up storage..."
tar -czf "$BACKUP_DIR/storage.tar.gz" /var/www/wecode/storage/

# 5. Configuration
echo "Backing up configuration..."
tar -czf "$BACKUP_DIR/config.tar.gz" \
    /var/www/wecode/.env \
    /var/www/wecode/config/ \
    2>/dev/null

# 6. Create manifest
cat > "$BACKUP_DIR/manifest.txt" << EOF
Wecode Full Backup
Date: $(date)
Timestamp: $TIMESTAMP
Database: $DB_NAME
Files:
$(ls -lh "$BACKUP_DIR")
EOF

echo "Full backup complete: $BACKUP_DIR"
echo "Total size: $(du -sh "$BACKUP_DIR" | cut -f1)"

# Optional: Create single archive
# tar -czf "$BACKUP_ROOT/wecode_full_$TIMESTAMP.tar.gz" -C "$BACKUP_ROOT/full" "$TIMESTAMP"

Remote Backup Storage

Rsync to Remote Server

#!/bin/bash

LOCAL_BACKUP="/var/backups/wecode"
REMOTE_USER="backup"
REMOTE_HOST="backup.example.com"
REMOTE_PATH="/backups/wecode"

# Sync to remote server
rsync -avz --delete \
    -e "ssh -i /root/.ssh/backup_key" \
    "$LOCAL_BACKUP/" \
    "$REMOTE_USER@$REMOTE_HOST:$REMOTE_PATH/"

if [ $? -eq 0 ]; then
    echo "Remote backup sync successful"
else
    echo "Remote backup sync failed!"
    exit 1
fi

Amazon S3 Backup

#!/bin/bash

# Requires: apt install awscli
# Configure: aws configure

BACKUP_FILE="/var/backups/wecode/full/latest.tar.gz"
S3_BUCKET="s3://my-wecode-backups"
TIMESTAMP=$(date +%Y%m%d_%H%M%S)

aws s3 cp "$BACKUP_FILE" "$S3_BUCKET/wecode_$TIMESTAMP.tar.gz"

if [ $? -eq 0 ]; then
    echo "S3 backup successful"
else
    echo "S3 backup failed!"
    exit 1
fi

Database Restore

MySQL/MariaDB Restore

# From uncompressed backup
mysql -u wecode_user -p wecode_db < backup.sql

# From compressed backup
gunzip < backup.sql.gz | mysql -u wecode_user -p wecode_db

# Or in one command
zcat backup.sql.gz | mysql -u wecode_user -p wecode_db

PostgreSQL Restore

# From SQL backup
psql -U wecode_user -d wecode_db < backup.sql

# From compressed backup
gunzip < backup.sql.gz | psql -U wecode_user -d wecode_db

# From custom format
pg_restore -U wecode_user -d wecode_db backup.dump

Safe Restore Procedure

# 1. Backup current database first!
mysqldump -u wecode_user -p wecode_db > pre_restore_backup.sql

# 2. Put application in maintenance mode
cd /var/www/wecode
php artisan down

# 3. Drop and recreate database
mysql -u root -p
DROP DATABASE wecode_db;
CREATE DATABASE wecode_db;
GRANT ALL ON wecode_db.* TO 'wecode_user'@'localhost';
FLUSH PRIVILEGES;
EXIT;

# 4. Restore from backup
zcat backup.sql.gz | mysql -u wecode_user -p wecode_db

# 5. Verify restoration
mysql -u wecode_user -p wecode_db -e "SHOW TABLES;"

# 6. Bring application back online
php artisan up

Files Restore

Restore Assignments Folder

# 1. Stop web server to prevent writes
sudo systemctl stop nginx

# 2. Backup current state (just in case)
mv /var/wecode-data/assignments /var/wecode-data/assignments.old

# 3. Extract backup
mkdir -p /var/wecode-data/assignments
tar -xzf assignments_20260304_020000.tar.gz -C /

# 4. Restore permissions
sudo chown -R www-data:www-data /var/wecode-data/assignments
sudo chmod -R 775 /var/wecode-data/assignments

# 5. Start web server
sudo systemctl start nginx

# 6. Verify
ls -la /var/wecode-data/assignments/

Restore Configuration

# Extract configuration backup
tar -xzf config_20260304_020000.tar.gz -C /

# Restore .env permissions
sudo chmod 600 /var/www/wecode/.env
sudo chown www-data:www-data /var/www/wecode/.env

# Restart services
sudo systemctl restart nginx
sudo systemctl restart php8.4-fpm

Docker Compose Backup/Restore

Backup Docker Volumes

# Backup MariaDB volume
docker run --rm \
    -v wecode_sail-mariadb:/data \
    -v $(pwd):/backup \
    alpine tar czf /backup/mariadb-volume.tar.gz /data

# Backup Redis volume
docker run --rm \
    -v wecode_sail-redis:/data \
    -v $(pwd):/backup \
    alpine tar czf /backup/redis-volume.tar.gz /data

Restore Docker Volumes

# Stop services
docker compose down

# Restore MariaDB volume
docker run --rm \
    -v wecode_sail-mariadb:/data \
    -v $(pwd):/backup \
    alpine sh -c "rm -rf /data/* && tar xzf /backup/mariadb-volume.tar.gz -C /"

# Restore Redis volume
docker run --rm \
    -v wecode_sail-redis:/data \
    -v $(pwd):/backup \
    alpine sh -c "rm -rf /data/* && tar xzf /backup/redis-volume.tar.gz -C /"

# Start services
docker compose up -d

Backup Verification

Test Database Backup

# Create test database
mysql -u root -p -e "CREATE DATABASE wecode_test;"

# Restore to test database
zcat backup.sql.gz | mysql -u root -p wecode_test

# Verify tables
mysql -u root -p wecode_test -e "SHOW TABLES;"
mysql -u root -p wecode_test -e "SELECT COUNT(*) FROM users;"

# Drop test database
mysql -u root -p -e "DROP DATABASE wecode_test;"

Verify File Backup

# Test extraction (without actually extracting)
tar -tzf assignments.tar.gz | head -20

# Verify archive integrity
tar -tzf assignments.tar.gz > /dev/null
if [ $? -eq 0 ]; then
    echo "Archive is valid"
else
    echo "Archive is corrupted!"
fi

Backup Monitoring

Check Backup Status Script

Create /usr/local/bin/check-wecode-backups.sh:
#!/bin/bash

BACKUP_DIR="/var/backups/wecode"
MAX_AGE_HOURS=24

echo "Wecode Backup Status"
echo "===================="

# Check database backup
LATEST_DB=$(find "$BACKUP_DIR/database" -name "*.sql.gz" -mtime -1 | head -1)
if [ -n "$LATEST_DB" ]; then
    echo "✓ Database: $(basename "$LATEST_DB") ($(stat -c%y "$LATEST_DB" | cut -d' ' -f1,2 | cut -d'.' -f1))"
else
    echo "✗ Database: No recent backup found!"
fi

# Check files backup
LATEST_FILES=$(find "$BACKUP_DIR/files" -name "*.tar.gz" -mtime -1 | head -1)
if [ -n "$LATEST_FILES" ]; then
    echo "✓ Files: $(basename "$LATEST_FILES") ($(stat -c%y "$LATEST_FILES" | cut -d' ' -f1,2 | cut -d'.' -f1))"
else
    echo "✗ Files: No recent backup found!"
fi

# Check disk space
echo ""
echo "Disk Usage:"
du -sh "$BACKUP_DIR"/*

Disaster Recovery Plan

Complete System Recovery Steps

  1. Install Base System
    # Install required software (see requirements.mdx)
    sudo apt update
    sudo apt install nginx mariadb-server php8.4-fpm docker-ce
    
  2. Restore Database
    mysql -u root -p -e "CREATE DATABASE wecode_db;"
    zcat database_backup.sql.gz | mysql -u root -p wecode_db
    
  3. Restore Application Files
    cd /var/www
    git clone https://github.com/truongan/wecode.git
    cd wecode
    tar -xzf /path/to/config_backup.tar.gz -C /
    
  4. Restore Data Folders
    tar -xzf assignments_backup.tar.gz -C /
    tar -xzf tester_backup.tar.gz -C /
    
  5. Set Permissions
    sudo chown -R www-data:www-data /var/www/wecode
    sudo chown -R www-data:www-data /var/wecode-data
    sudo chmod -R 775 /var/wecode-data
    sudo chmod 600 /var/www/wecode/.env
    
  6. Install Dependencies
    cd /var/www/wecode
    composer install
    php artisan config:cache
    php artisan route:cache
    
  7. Verify and Start
    php artisan migrate:status
    sudo systemctl restart nginx php8.4-fpm
    

Best Practices

  • 3-2-1 Rule: 3 copies, 2 different media, 1 offsite
  • Test Restores: Regularly verify backups can be restored
  • Automate: Use cron for automatic backups
  • Monitor: Check backup logs daily
  • Encrypt: Encrypt backups containing sensitive data
  • Document: Keep restore procedures updated
  • Retention: Keep daily (7 days), weekly (4 weeks), monthly (12 months)

Next Steps

Build docs developers (and LLMs) love