Automated Production Backup #26
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Automated Production Backup | |
| on: | |
| # Run weekly on Sunday at 2 AM UTC | |
| schedule: | |
| - cron: '0 2 * * 0' | |
| # Allow manual trigger | |
| workflow_dispatch: | |
| inputs: | |
| include_database: | |
| description: '⚠️ Include database backup (uses Neon compute - only needed for monthly archives or before migrations)' | |
| required: false | |
| type: boolean | |
| default: false | |
| keep_uncompressed: | |
| description: 'Keep uncompressed backup files (debugging only - uses extra disk space)' | |
| required: false | |
| type: boolean | |
| default: false | |
| jobs: | |
| backup: | |
| name: Backup Application Data | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Notify backup started | |
| if: always() | |
| run: | | |
| curl -H "Content-Type: application/json" \ | |
| -d '{ | |
| "embeds": [{ | |
| "title": "📝 [Blog] Backup Started", | |
| "description": "Daily backup process initiated", | |
| "color": 3447003, | |
| "fields": [ | |
| {"name": "Time", "value": "'"$(date -u)"'", "inline": true}, | |
| {"name": "Server", "value": "Production VPS", "inline": true} | |
| ] | |
| }] | |
| }' \ | |
| ${{ secrets.DISCORD_WEBHOOK_URL }} | |
| - name: Setup SSH | |
| run: | | |
| mkdir -p ~/.ssh | |
| echo "${{ secrets.DEPLOY_SSH_KEY }}" > ~/.ssh/id_ed25519 | |
| chmod 600 ~/.ssh/id_ed25519 | |
| ssh-keyscan -H ${{ secrets.SERVER_IP }} >> ~/.ssh/known_hosts | |
| - name: Test SSH connection | |
| run: | | |
| ssh -i ~/.ssh/id_ed25519 cheloniixd@${{ secrets.SERVER_IP }} "echo '✅ SSH connection successful'" | |
| - name: Run backup script | |
| run: | | |
| ssh -i ~/.ssh/id_ed25519 cheloniixd@${{ secrets.SERVER_IP }} \ | |
| "cd /opt/cpta_blog && bash deployment/backup.sh --upload ${{ inputs.include_database == true && '--include-database' || '' }} ${{ inputs.keep_uncompressed == true && '--keep-uncompressed' || '' }}" | |
| - name: Notify backup success | |
| if: success() | |
| run: | | |
| DB_STATUS="${{ inputs.include_database == true && '✅ Backed up (manual)' || '⏭️ Skipped (Neon auto-backup)' }}" | |
| curl -H "Content-Type: application/json" \ | |
| -d '{ | |
| "embeds": [{ | |
| "title": "✅ [Blog] Backup Completed", | |
| "description": "All data has been backed up", | |
| "color": 5763719, | |
| "fields": [ | |
| {"name": "Time", "value": "'"$(date -u)"'", "inline": true}, | |
| {"name": "Retention", "value": "30 days", "inline": true}, | |
| {"name": "📦 Database", "value": "'"$DB_STATUS"'", "inline": false}, | |
| {"name": "💾 Redis", "value": "✅ Backed up", "inline": true}, | |
| {"name": "⚙️ Config", "value": "✅ Backed up", "inline": true}, | |
| {"name": "🔒 SSL Certs", "value": "✅ Backed up", "inline": true}, | |
| {"name": "📋 Logs", "value": "✅ Last 24h backed up", "inline": true}, | |
| {"name": "☁️ Google Drive", "value": "✅ Uploaded & Verified", "inline": true} | |
| ], | |
| "footer": {"text": "Blog - Automated Backup"} | |
| }] | |
| }' \ | |
| ${{ secrets.DISCORD_WEBHOOK_URL }} | |
| - name: Notify backup failure | |
| if: failure() | |
| run: | | |
| curl -H "Content-Type: application/json" \ | |
| -d '{ | |
| "embeds": [{ | |
| "title": "❌ [Blog] Backup Failed", | |
| "description": "The automated backup process has failed", | |
| "color": 15158332, | |
| "fields": [ | |
| {"name": "Time", "value": "'"$(date -u)"'", "inline": true}, | |
| {"name": "Server", "value": "Production VPS", "inline": true}, | |
| {"name": "⚠️ Action Required", "value": "1. SSH to server and check logs\n2. Verify containers running\n3. Check disk space\n4. Run manual backup", "inline": false} | |
| ], | |
| "footer": {"text": "Blog - Automated Backup"} | |
| }] | |
| }' \ | |
| ${{ secrets.DISCORD_WEBHOOK_URL }} | |
| - name: Backup summary | |
| if: success() | |
| run: | | |
| echo "## 🗄️ Backup Summary" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "- **Time:** $(date -u)" >> $GITHUB_STEP_SUMMARY | |
| echo "- **Server:** ${{ secrets.SERVER_IP }}" >> $GITHUB_STEP_SUMMARY | |
| echo "- **Database included:** ${{ inputs.include_database || 'false' }}" >> $GITHUB_STEP_SUMMARY | |
| echo "- **Keep uncompressed:** ${{ inputs.keep_uncompressed || 'false' }}" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "### What was backed up:" >> $GITHUB_STEP_SUMMARY | |
| if [ "${{ inputs.include_database }}" == "true" ]; then | |
| echo "- ✅ PostgreSQL database (manually included)" >> $GITHUB_STEP_SUMMARY | |
| else | |
| echo "- ⏭️ PostgreSQL database (SKIPPED - Neon provides automatic 7-day point-in-time recovery)" >> $GITHUB_STEP_SUMMARY | |
| fi | |
| echo "- ✅ Redis data (rate limiting, cache)" >> $GITHUB_STEP_SUMMARY | |
| echo "- ✅ Configuration files (.env, nginx, docker-compose)" >> $GITHUB_STEP_SUMMARY | |
| echo "- ✅ SSL certificates" >> $GITHUB_STEP_SUMMARY | |
| echo "- ✅ Application logs (last 24 hours)" >> $GITHUB_STEP_SUMMARY | |
| echo "- ✅ Git commit reference" >> $GITHUB_STEP_SUMMARY | |
| echo "- ✅ Uploaded to Google Drive" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "**Retention:** 30 days (local VPS + Google Drive)" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| if [ "${{ inputs.include_database }}" != "true" ]; then | |
| echo "**Note:** Database backup was skipped to save Neon compute hours. Neon provides automatic 7-day point-in-time recovery. Check the '⚠️ Include database backup' option when running manually if you need a database snapshot." >> $GITHUB_STEP_SUMMARY | |
| fi | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "✅ Backup completed successfully!" >> $GITHUB_STEP_SUMMARY | |
| - name: Summary on failure | |
| if: failure() | |
| run: | | |
| echo "## ❌ Backup Failed" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "The automated backup process has failed." >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "**Possible reasons:**" >> $GITHUB_STEP_SUMMARY | |
| echo "- Database connection issues" >> $GITHUB_STEP_SUMMARY | |
| echo "- Docker containers not running" >> $GITHUB_STEP_SUMMARY | |
| echo "- Disk space issues" >> $GITHUB_STEP_SUMMARY | |
| echo "- SSH connection problems" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "**Action required:**" >> $GITHUB_STEP_SUMMARY | |
| echo "1. SSH to server and check logs" >> $GITHUB_STEP_SUMMARY | |
| echo "2. Verify containers are running: \`docker compose -f docker-compose.prod.yml ps\`" >> $GITHUB_STEP_SUMMARY | |
| echo "3. Check disk space: \`df -h\`" >> $GITHUB_STEP_SUMMARY | |
| echo "4. Run manual backup: \`bash deployment/backup.sh\`" >> $GITHUB_STEP_SUMMARY |