- Knowledge
- technology
- OOP
- Tips
- Programming
- Tips
- Tutorial
- SEO
- Ranking
- Knowledge
- Special Day
- Seo
- Bug
- Data science
- Seo
- artificial intelligence
- Machine Learning
- Robotics
- happyNewYear2021
- newYearEve
- 2021
- Automation
- Smart Home
- Career
- Best Practices
- Git
- Logging
- Web Fundamentals
- DNS
- HTTPS
- Performance
- AI Tools
- ChatGPT
- Claude
- Gemini
- Laravel
- Eloquent
- MySQL
- HTTPS
- TLS
- Web Security
- Certificates
- Developer Life
- Debugging
- Docker
- DevOps
- Transactions
- Queues
- LLMs
- AI
- AI Coding
- Developer Tools
- React Native
- Expo
- Kate PMS
- Mobile Apps
- Laravel
- Authentication
- Sanctum
- Cookies
- API Design
- Payments
- Idempotency
- DeepSeek
- Open Source AI
- LLMs
- AI News
- Git
- Version Control
- AI Coding
- Prompting
- PHP
- Checklist
- MCP
- AI Agents
- OpenAI
- Architecture
- Microservices
- Modular Monolith
- Estimation
- Developer Life
- Project Planning
- Humour
- OAuth
- OpenID Connect
- Authentication
- Embeddings
- Vector Search
- RAG
- pgvector
- OpenAI
- GPT-4.1
- Codex CLI
- Events
- Testing
- Clean Code
- Maintainability
- Code Review
- Webhooks
- API
- Security
- Claude Code
- Workflow
- AI
- LLM
- Prompt Injection
- Mobile
- React
- Networking
- TCP
- UDP
- HTTP/3
- CLAUDE.md
- AWS
- Cloud Security
- Backups
- PHPUnit
- Software Engineering
- Leadership
- Communication
- RAG
- Embeddings
- AI Engineering
- IT Infrastructure
- Networking
- Access Control
- CI/CD
- GitHub Actions
- Gemini CLI
- Claude Code
- JavaScript
- Async/Await
- Node.js
- Promises
- Security
- Cryptography
- Passwords
- MySQL
- Database
- Vibe Coding
- Software Quality
- DNS
- Code Reading
- Onboarding
- Productivity
- Background Jobs
- Developer Humour
- Estimates
- Dev Life
- JWT
- o3-mini
- DeepSeek R1
- Rate Limiting
- Kate PMS
- E-Signing
- Audit Trail
- REST
- GraphQL
- API Design
- Laravel 12
- Upgrade Guide
- Open Source
- Self-Hosting
- Task Scheduling
- Cron
- Secrets
- CORS
- PHP
- PHP-FPM
- OPcache
- GitHub Copilot
- Software Architecture
- Engineering
- TypeScript
- JavaScript
- Type Safety
- AI Security
- React Native
- Product Design
- AI Agents
- Kiro
- Queues
- Redis
- RabbitMQ
- AWS SQS
- Nginx
- Apache
- GPT-5
- gpt-oss
- Clean Code
- Architecture
- Naming
- Documentation
- Career
- ADR
- Teamwork
- Supply Chain
- Kate HRM
- HR Software
- Permissions
- System Design
- Pagination
- SSH
- Linux
- Big O
- Databases
- Laravel Boost
- MCP
- Developer Skills
- Validation
- Databases
- Indexes
- Code Quality
- Deployment
- Developer Humour
- Feature Flags
- Code Review
- Pull Requests
- Docker
- Cursor
- Authorization
- RBAC
- Gemini
- Long Context
- PHP 8.4
- Caching
- Dependency Injection
- Web Performance
- Browser
- CSS
- Database
- Migrations
- ChatGPT
- AI for Developers
- Monitoring
- On-Call
- REST
- Backend
- SQL
- NoSQL
- Database Design
- Coding Agents
- Claude 4
- API Resources
- REST API
- Load Balancing
- Scaling
- AWS
- AI Tools
- Claude
- Sora 2
- CTE
- 2FA
- TOTP
- Programming Languages
- Prompts
- Developer Workflow
- API Gateway
- APIs
- Passport
- API Auth
- Learning
- Burnout
- Developer Growth
- Web Development
- SEO
- Kate Mall
- ChatGPT Atlas
- Agent Skills
- Middleware
- Laravel 12
- Collections
- Context Window
- Monitoring
- Commit Messages
- Self Review
- Growth
- Regex
- Programming Basics
- Text Processing
- Database Design
- Normalization
- Linux
- Server Security
- Linux Foundation
- Open Standards
- Legacy Code
- Documentation
- AI Workflow
- File Uploads
- Test Data
- Hashing
- Performance
- Caching
- Enums
- Scope Creep
- Estimation
- Codex
- Gemini CLI
- Timezones
- Carbon
- Bugs
- PHP 8.5
- Gemini 3
- GPT-5.1
- Data Integrity
- Event Loop
- Async
- Opus 4.5
- AI Models
- React
- Forms
- Frontend
- Backups
- AI Images
- DALL-E
- Midjourney
- Race Conditions
- Concurrency
- Legacy Code
- Refactoring
- Senior Engineer
- Scope
- LLM
- CDN
- Web
- Sub-Agents
- Soft Deletes
- Audit Log
- Concurrency
- AI Learning
- NestJS
- AI Evals
- Policies
- SPF DKIM DMARC
- Unicode
- UTF-8
- Knowledge Graph
- Value Objects
- Technical Debt
- Feature Flags
- Laravel Pennant
- Deployment
- Copilot
- Composer
- Dependencies
- Artisan
- Automation
- AWS S3
- Object Storage
- Cloud
- Small Language Models
- Ollama
- Production
- Sessions
- HTTP
- Mentoring
- SQL
- Virtual Machines
- Web Development
- HTTP/2
- QUIC
- Web Performance
- AI Integration
- LLM API
- SOLID
- OOP
- Hosting
- Serverless
- Merge Conflicts
- Temperature
- AI Development
- Reverse Proxy
- Nginx
- Infrastructure
- Verification
- Passkeys
- WebAuthn
- Teams
- Communication
- Stakeholders
- Monorepo
- CI/CD
- Versioning
- JSON Schema
- Livewire
- Inertia
- Meetings
- Distributed Systems
- Privacy
- Full-Stack
- T-Shaped Skills
- Money
- Notifications
- Web Security
- HTTP Headers
- CSP
- Function Calling
- Load Testing
- k6
- Data Extraction
- Debugging
- WebSockets
- SSE
- Real-Time
- Laravel Reverb
- Infrastructure as Code
- Terraform
- Side Projects
- Laravel Pint
- OpenAPI
- Swagger
- UX
- Multimodal
- Jest
- Pair Programming
- APIs
- Rate Limiting
- Resilience
- Dev Humour
- Design Tokens
- JWT
- API Keys
- Sessions
- PHPStan
- Rector
- Incidents
- Reporting
- Dashboards
- Zero Trust
- IAM
- Search
- Laravel Scout
- Junior Developers
- Mentoring
- Images
- WebP
- AVIF
- Bug Reports
- Let's Encrypt
- Design Docs
- Software Design
- Observers
- Replication
- Accountability
- Data Structures
- Reliability
- LLM Memory
- Error Handling
- Payments
- Payment Gateway
- Webhooks
- PCI DSS
- Observability
- OpenTelemetry
- Personal Brand
- Writing
- Conventions
- Dates
- Scheduling
- Disaster Recovery
- Compression
- Brotli
- Deadlines
- Developer Habits
- State Machines
- Tech Roles
- UUID
- ULID
- Horizon
- Planning
- Engineering Culture
- Ownership
- Soft Skills
- Socialite
- Cost Control
- Collations
- Unicode
- Octane
- PostgreSQL
Backups You Can Actually Restore: A Practical Strategy for Web Apps
About Post
Nobody actually wants backups. What people want is restores. A backup is just a file sitting somewhere, and you only find out whether it's worth anything on the worst day of the year, with everyone watching.
The usual failure isn't "we had no backups". It's "we had backups, but…" The cron job stopped months ago. The dump was empty because the password changed. The files were encrypted and nobody can find the key. The snapshot lived in the same account that just got compromised.
So this is a backup strategy built backwards, starting from the restore.
Start with two questions, not a tool
Before picking a tool, answer these for each system:
- How much data can we afford to lose? This is your RPO (recovery point objective). A nightly backup means you can lose up to a day of payments, contracts and uploads. Is that acceptable?
- How long can we be down? This is your RTO (recovery time objective). Restoring a large database dump can take a long time. If the business needs to be back within an hour, you need to know your restore fits in that hour.
These answers are business decisions, not technical ones. Ask the people who'd feel the pain. "Losing a day of maintenance requests is annoying" and "losing a day of rent payments is a disaster" lead to very different setups.
The 3-2-1 rule, translated for web apps
The classic rule says: keep 3 copies of your data, on 2 different kinds of storage, with 1 copy offsite. For a typical Laravel or Node app on a cloud server, that might look like:
- The live database (copy one).
- Automated backups or snapshots from your provider, like RDS automated backups (copy two).
- Your own logical dumps, shipped to a separate storage bucket, ideally in a different account or region (copy three, and your offsite copy).
Many people now add two more digits: 3-2-1-1-0. One copy that's immutable or offline, so ransomware or a compromised admin account can't delete it, and zero errors when you verify your backups. That last part is the one most teams skip.
Dumps vs snapshots: you probably want both
| Logical dump (mysqldump) | Snapshot (disk or RDS) | |
|---|---|---|
| What it is | SQL statements that recreate the data | A copy of the storage at a moment in time |
| Restore one table | Easy: extract it from the file | Restore the whole thing, then copy what you need |
| Portability | Any MySQL server, any provider | Tied to that provider and usually that account |
| Speed on big databases | Slow to create, slower to restore | Fast to create and restore |
| Point-in-time recovery | Only with binary logs as well | RDS supports it within the retention period |
Snapshots are your quick "roll the whole thing back" option. Dumps are your portable, provider-independent, "get one deleted table back" option. A sensible setup uses snapshots for speed and dumps for independence.
Automate it, and make failure loud
A backup that relies on someone remembering will stop. Here's a simplified nightly script for MySQL:
#!/usr/bin/env bash
set -euo pipefail # stop on any error, including inside the pipe
STAMP=$(date +%F_%H%M)
FILE="/var/backups/app_${STAMP}.sql.gz"
mysqldump --defaults-extra-file=/etc/backup/my.cnf \
--single-transaction --quick --routines --triggers app_db \
| gzip > "$FILE"
gpg --batch --yes --encrypt --recipient [email protected] "$FILE"
aws s3 cp "${FILE}.gpg" "s3://example-db-backups/mysql/"
rm -f "$FILE" "${FILE}.gpg"
curl -fsS "$HEARTBEAT_URL" > /dev/null # "I ran and finished"
A few details doing the real work:
set -o pipefailmatters. Without it, a failedmysqldumppiped into a happygzipcounts as success, and you get a tiny, useless file every night.--single-transactiongives a consistent dump of InnoDB tables without locking them, so the app keeps running.- Credentials live in a protected options file, not on the command line where other users can see them in the process list.
- The last line pings a heartbeat monitor. If the ping doesn't arrive, you get an alert. Alerting on missing success catches the job that silently stopped running, which an error alert never will.
Don't forget what isn't in the database: user uploads (turn on versioning for the S3 bucket), and your secrets and .env values, stored encrypted somewhere separate. Your code is already in Git, but your deploy scripts and server config should be too.
Encryption, and the key problem
Backups contain everything: personal data, contracts, payment records. They must be encrypted at rest. The trap is where you keep the key. If the only copy of the decryption key is on the server you're trying to recover, your backups are encrypted for everyone, including you.
Keep the private key (or its passphrase) in a password manager or secrets vault that doesn't depend on the system being backed up, and make sure more than one person can reach it.
Retention: keep enough, not everything
A common scheme is "grandfather-father-son": daily backups for a short window, weekly ones for longer, monthly ones for longer still. It covers both "I deleted it this morning" and "this data has been wrong since spring".
Use lifecycle rules on your storage bucket so old backups expire automatically, and turn on object lock or a separate restricted account for the copies that must not be deleted. And remember privacy: if you must delete someone's personal data, backups don't get a free pass forever. Your retention period is part of that answer.
Test restores, on a schedule
This is the part that turns "we have backups" into "we can recover". At least once a month, restore the latest backup into a scratch database and check it:
gpg --decrypt app_latest.sql.gz.gpg | gunzip | mysql restore_test
mysql restore_test -e "
SELECT COUNT(*) FROM contracts;
SELECT MAX(created_at) FROM payments;"
The row count tells you the data is there. The latest timestamp tells you it's recent. Time the whole thing too: that number is your real RTO, not the one in the planning document.
Better still, automate the drill and have it report the results. And once a year, do a full rehearsal: a new server, from nothing, using only the backups and your written runbook. It's the only way to find the missing step before it matters.
The rule: a backup you haven't restored is a hope, not a backup. Schedule the restore test the same day you schedule the backup.
The checklist
- RPO and RTO agreed with the business, per system.
- 3 copies, 2 storage types, 1 offsite, and 1 that can't be deleted.
- Snapshots for speed, dumps for independence.
- Uploads, secrets and config covered, not just the database.
- Encrypted, with the key stored somewhere else.
- A heartbeat alert when a backup doesn't finish.
- Restore tests on a schedule, with the time recorded.
When did you last actually restore one of your backups? And what surprised you when you did?

Be first to comment it...