Overview
Oyasai Server Platform implements automated backups using Restic for Minecraft world data and custom backup solutions for MariaDB databases. All backups are stored in Cloudflare R2 object storage.
Minecraft World Backups
The production environment uses the itzg/mc-backup container for automated world backups:
packages/cdktf/src/stacks/docker-stack.ts
new Container ( this , this . envAwareId ( "minecraft-backup-container" ), {
name: "minecraft-main-backup" ,
dependsOn: [ minecraftMainContainer ],
image: images . minecraftBackup . imageId ,
networksAdvanced: [ network ],
restart: "unless-stopped" ,
env: envs ({
BACKUP_METHOD: "restic" ,
RESTIC_PASSWORD: this . secrets . RESTIC_PASSWORD ,
AWS_ACCESS_KEY_ID: this . secrets . CLOUDFLARE_ACCESS_KEY_ID ,
AWS_SECRET_ACCESS_KEY: this . secrets . CLOUDFLARE_SECRET_ACCESS_KEY ,
RESTIC_VERBOSE: true ,
RESTIC_REPOSITORY: `s3:https:// ${ this . secrets . CLOUDFLARE_ACCOUNT_ID } .r2.cloudflarestorage.com/production/minecraft-main-backup` ,
RCON_HOST: "minecraft-main" ,
RCON_PASSWORD: this . secrets . RCON_PASSWORD ,
EXCLUDES: "*.jar,cache,logs,*.tmp,bluemap" ,
BACKUP_INTERVAL: "6h" ,
PRUNE_RESTIC_RETENTION: "--keep-daily 7 --keep-weekly 4 --keep-monthly 3" ,
}),
volumes: [
{
hostPath: join ( this . workdir , "minecraft-main" ),
containerPath: "/data" ,
readOnly: true ,
},
],
});
Backup Configuration
Backup tool to use (restic recommended for deduplication)
Frequency of automatic backups (e.g., “6h”, “12h”, “24h”)
S3-compatible storage URL for backup destination
Password for encrypting backup repository
Hostname of Minecraft server for save-all command
RCON password for server communication
Comma-separated patterns for files to exclude from backups
Retention Policy
Backups are automatically pruned according to retention rules:
PRUNE_RESTIC_RETENTION : "--keep-daily 7 --keep-weekly 4 --keep-monthly 3"
Keep the last 7 daily backups
Keep the last 4 weekly backups
Keep the last 3 monthly backups
Excluded Files
The following files are excluded from backups to reduce size:
*.jar - Plugin JAR files (can be rebuilt)
cache - Temporary cache directories
logs - Server log files
*.tmp - Temporary files
bluemap - BlueMap rendered tiles (regenerated)
Excluding regeneratable data significantly reduces backup size and transfer time.
Database Backups
MariaDB databases are backed up using databack/mysql-backup:
packages/cdktf/src/stacks/docker-stack.ts
new Container ( this , this . envAwareId ( "mariadb-backup-container" ), {
name: "mariadb-backup" ,
dependsOn: [ mariadbContainer ],
image: "databack/mysql-backup" ,
restart: "unless-stopped" ,
networksAdvanced: [ network ],
command: [ "dump" ],
env: envs ({
DB_SERVER: "mariadb" ,
DB_USER: "root" ,
DB_PASS: this . secrets . MARIADB_PASSWORD ,
DB_DUMP_FREQUENCY: 360 ,
DB_DUMP_TARGET: `s3:// ${ this . secrets . R2_BUCKET_NAME } /mariadb-backup` ,
AWS_ACCESS_KEY_ID: this . secrets . CLOUDFLARE_ACCESS_KEY_ID ,
AWS_SECRET_ACCESS_KEY: this . secrets . CLOUDFLARE_SECRET_ACCESS_KEY ,
AWS_REGION: "auto" ,
AWS_ENDPOINT_URL: `https:// ${ this . secrets . CLOUDFLARE_ACCOUNT_ID } .r2.cloudflarestorage.com` ,
DB_DUMP_COMPRESSION: "gzip" ,
DB_DUMP_RETENTION: "14d" ,
DB_DEBUG: true ,
}),
});
Database Backup Configuration
Database server hostname (container name on Docker network)
Database user with backup privileges
Backup frequency in minutes (360 = 6 hours)
S3 storage path for database backups
Compression method for SQL dumps
How long to retain old backups (e.g., “14d”, “30d”)
Backup Storage
All backups are stored in Cloudflare R2 object storage:
Storage Configuration
Minecraft Backups
Database Backups
RESTIC_REPOSITORY : `s3:https:// ${ CLOUDFLARE_ACCOUNT_ID } .r2.cloudflarestorage.com/production/minecraft-main-backup`
R2 Credentials
Authentication uses AWS S3-compatible credentials:
Cloudflare R2 access key ID
Cloudflare R2 secret access key
Region setting (use “auto” for R2)
Backup Image Configuration
The mc-backup Docker image is defined in Nix:
{
oyasaiDockerTools ,
dockerTools ,
stdenv ,
}:
let
inherit ( stdenv . hostPlatform ) system ;
name = "mc-backup" ;
imageName = "itzg/mc-backup" ;
hashes = {
"x86_64-linux" = "sha256-3g2ayNkXxJjaXuC8t7EMDTI9nMH2did75gB/nmNH2Aw=" ;
"aarch64-linux" = "sha256-mzKgbyhuH0pMmzFuNKC/ltsDLzx0kfypTEY7XbVPPes=" ;
};
in
oyasaiDockerTools . buildImage {
inherit name ;
fromImage = dockerTools . pullImage {
inherit imageName ;
imageDigest = "sha256:81e68ecbf7c3452079d08fc7058208cdf493633b4e7431d79d56bdb910c4dfea" ;
hash = hashes . ${ system } ;
finalImageName = imageName ;
finalImageTag = "latest" ;
};
config . Entrypoint = [ "/usr/bin/backup" ];
config . Cmd = [ "loop" ];
platforms = builtins . attrNames hashes ;
}
Supported platforms:
x86_64-linux (Intel/AMD)
aarch64-linux (ARM64)
Backup Process
The automated backup workflow:
Trigger backup
Timer reaches configured interval (6 hours by default)
Issue save-all
Backup container connects via RCON and issues save-all command
Wait for save
Server completes world save to disk
Create snapshot
Restic creates deduplicated snapshot of changed files
Upload to R2
Encrypted backup data uploads to Cloudflare R2
Prune old backups
Apply retention policy to remove expired backups
Manual Backup Operations
Trigger Manual Backup
# Execute backup immediately
docker exec minecraft-main-backup backup now
# View backup status
docker logs minecraft-main-backup --tail 50
List Backups
# List all snapshots
docker exec minecraft-main-backup restic snapshots
# Show backup statistics
docker exec minecraft-main-backup restic stats
Restore from Backup
Always stop the Minecraft server before restoring to prevent data corruption.
# Stop server
docker stop minecraft-main
# List available snapshots
docker exec minecraft-main-backup restic snapshots
# Restore latest snapshot
docker exec minecraft-main-backup restic restore latest --target /data
# Restore specific snapshot
docker exec minecraft-main-backup restic restore < snapshot-i d > --target /data
# Start server
docker start minecraft-main
Database Restore
# Stop database-dependent services
docker stop minecraft-main
# Download backup from R2
aws s3 cp s3:// ${ R2_BUCKET_NAME } /mariadb-backup/backup-YYYY-MM-DD.sql.gz . \
--endpoint-url https:// ${ CLOUDFLARE_ACCOUNT_ID } .r2.cloudflarestorage.com
# Restore to database
gunzip < backup-YYYY-MM-DD.sql.gz | docker exec -i mariadb mysql -p
# Restart services
docker start minecraft-main
Backup Monitoring
Check Backup Status
# View backup container logs
docker logs minecraft-main-backup -f
# Check last backup time
docker exec minecraft-main-backup restic snapshots --latest 1
# Verify backup integrity
docker exec minecraft-main-backup restic check
Backup Metrics
Repository Size
Backup Performance
# Check repository size and file count
docker exec minecraft-main-backup restic stats
Volume Configuration
Backup containers mount server data as read-only:
volumes : [
{
hostPath: join ( this . workdir , "minecraft-main" ),
containerPath: "/data" ,
readOnly: true , // Prevent accidental modifications
},
]
This ensures:
No risk of backup process corrupting live data
Clear separation between read and write operations
Safety during concurrent server operation
Environment-Specific Backups
Production
Development
Local
Minecraft : Every 6 hours, retained 7/4/3 (daily/weekly/monthly)
Database : Every 6 hours, retained 14 days
Storage : Cloudflare R2 production bucket
Status : Backup restoration for testing
Purpose : Validate backup integrity
Storage : Separate development bucket
Status : No automated backups
Purpose : Development and testing only
Backup : Manual snapshots as needed
Backup Best Practices
Test Restores Regularly test backup restoration to verify integrity and procedures
Monitor Storage Track R2 storage usage to manage costs and capacity
Verify Encryption Ensure RESTIC_PASSWORD is securely stored and backed up separately
Document Recovery Maintain updated disaster recovery documentation
Disaster Recovery
Full Server Restore
Provision new server
Deploy fresh Oyasai Server Platform infrastructure
Configure R2 access
Set up Cloudflare R2 credentials for backup access
Restore world data
Use Restic to restore latest Minecraft world snapshot
Restore database
Import latest MariaDB dump from R2 storage
Verify integrity
Start server and verify world and player data
Resume backups
Ensure backup containers are running and scheduled
Troubleshooting
Backup fails with authentication error
Verify R2 credentials are correct: docker exec minecraft-main-backup env | grep AWS
Test R2 connectivity: docker exec minecraft-main-backup restic snapshots
Backup container keeps restarting
Check for RCON connectivity: docker exec minecraft-main-backup nc -zv minecraft-main 25575
Verify RCON password: docker logs minecraft-main-backup | grep -i rcon
Backups consuming too much storage
Review exclusion patterns: docker inspect minecraft-main-backup | jq '.[0].Config.Env' | grep EXCLUDES
Adjust retention policy: # Reduce retention in docker-stack.ts
PRUNE_RESTIC_RETENTION: "--keep-daily 3 --keep-weekly 2 --keep-monthly 1"
Next Steps
Monitoring Set up monitoring for backup health and status
Server Configuration Review server configuration for backup optimization