Build hosting-restore
We now have:
hosting-create
hosting-list
hosting-info
hosting-health
hosting-repair
hosting-ssl
hosting-backup
The next component completes the backup system:
BACKUP
↓
RESTORE
The target command:
sudo hosting-restore example.com BACKUP_ID
For example:
sudo hosting-restore example.com 2026-08-13_160000
1. Restore Is a High-Risk Operation
Unlike:
hosting-info
hosting-health
restore modifies customer data.
Therefore the workflow must be much stricter.
SELECT BACKUP
↓
VERIFY BACKUP
↓
LOCK SITE
↓
CREATE PRE-RESTORE BACKUP
↓
RESTORE
↓
VERIFY
↓
HEALTH CHECK
↓
ACTIVE
If anything fails:
RESTORE ERROR
↓
ROLLBACK
2. Never Restore Directly Over the Live Site
Avoid:
backup
↓
rm -rf public
↓
extract
This is dangerous.
If extraction fails halfway:
public/
├── some files restored
├── some files missing
└── site broken
Instead use a temporary restore directory.
3. Safe Restore Architecture
LIVE SITE
│
│ remains intact
▼
TEMPORARY RESTORE
│
▼
VERIFY
│
▼
ACTIVATE
This is much safer.
4. Example Directory Structure
Current:
/storage/websites/example.com/public
Temporary:
/storage/websites/example.com/.restore/
For example:
.restore/
└── 2026-08-13_160000/
5. Better Temporary Location
For very large websites, temporary restoration can require substantial disk space.
Therefore eventually you may want:
/storage/cresignsys/restore-work/
instead of putting temporary data inside the website.
For the first implementation, choose a dedicated location outside the public document root.
6. Restore States
Use:
id="v4l0q8"
RESTORE_REQUESTED
↓
VERIFYING
↓
PREPARING
↓
RESTORING_FILES
↓
RESTORING_DATABASE
↓
VERIFYING_SITE
↓
HEALTH_CHECK
↓
ACTIVE
Failure:
RESTORE_ERROR
7. First Step — Find the Backup
Given:
sudo hosting-restore example.com 2026-08-13_160000
the script constructs:
/backup/cresignsys/example.com/2026-08-13_160000/
Then checks:
files archive
database dump
manifest
checksums
8. Verify Manifest
Read:
manifest.json
Check:
domain
site_id
backup ID
status
files archive
database dump
The backup should say:
status = COMPLETE
If:
status = ERROR
stop immediately.
9. Verify Domain
This is a critical safety check.
Suppose you accidentally execute:
hosting-restore shop.example.com BACKUP_FROM_OTHER_SITE
You must not restore it.
The manifest should contain:
DOMAIN=example.com
and the requested domain must match.
10. Verify Site ID
Also compare:
Current site:
SITE_ID=1027
Backup:
SITE_ID=1027
If they don’t match:
ERROR:
Backup belongs to a different site.
This prevents accidental cross-site restoration.
11. Verify Checksums
Run:
sha256sum -c checksums.sha256
Expected:
files.tar.gz: OK
database.sql.gz: OK
If a checksum fails:
RESTORE STOPPED
Do not continue.
12. Why Checksum Verification Comes Before Restore
Without verification:
corrupted backup
↓
restore
↓
corrupted website
With verification:
corrupted backup
↓
checksum failure
↓
restore stopped
13. Acquire Site Lock
Before modifying anything:
/var/lock/cresignsys-example.com.lock
The lock prevents:
hosting-backup
hosting-repair
hosting-delete
hosting-restore
from simultaneously modifying the same site.
14. Create Pre-Restore Backup
This is one of the most important steps.
Before restoring:
CURRENT SITE
↓
BACKUP
↓
RESTORE OLD BACKUP
If restoration fails:
OLD CURRENT SITE
↓
ROLLBACK
Therefore:
Never perform a production restore without preserving the current state.
15. Pre-Restore Backup
You can call:
hosting-backup example.com
from the restore workflow, but ideally the backup engine should be implemented as a reusable internal module rather than invoking a separate command blindly.
16. Maintenance Mode
For database/file replacement, users shouldn’t continue modifying the site.
WordPress maintenance mode can be enabled with WP-CLI:
sudo -u "$SITE_USER" \
wp --path="$WEB_ROOT" maintenance-mode activate
However, if the site is already inaccessible, this may not work.
Therefore maintenance mode is an enhancement, not the primary safety mechanism.
17. Database Restore Is Different From File Restore
Files:
archive
↓
extract
Database:
SQL dump
↓
MySQL
They need separate verification.
18. Never Drop the Live Database First
A dangerous workflow:
DROP DATABASE
↓
CREATE DATABASE
↓
IMPORT
If import fails:
database gone
Instead use a safer strategy.
19. Database Restore Strategy
For a first controlled implementation:
current database
↓
pre-restore backup
↓
prepare restore database
↓
import
↓
verify
↓
activate
There are several ways to implement this depending on your MySQL architecture.
20. Option A — Temporary Database
Example:
Current:
csp1027_wp
Temporary:
csp1027_restore_20260813
Import the dump into:
csp1027_restore_20260813
Then verify it.
This is safer than destroying the current database first.
21. Verify Temporary Database
After import:
temporary database
↓
WordPress tables?
↓
wp_options?
↓
wp_posts?
↓
wp_users?
You can query the temporary database to ensure the expected WordPress structure exists.
22. Database Swap
Once the temporary database is verified, the final database transition must be performed carefully.
Depending on your MySQL setup, this may involve:
rename/swap strategy
or:
controlled replacement
The exact mechanism should be tested extensively before production use.
23. Why Not Just Import Into Existing Database?
You could:
mysql existing_database < backup.sql
but this can leave old tables or conflicting schema/data depending on the dump.
For example:
current:
wp_posts
wp_users
wp_options
old_custom_table
backup:
wp_posts
wp_users
wp_options
The old custom table may remain.
A clean restore requires a controlled database replacement strategy.
24. File Restore Strategy
Create:
restore-work/
└── public/
Extract:
files.tar.gz
into the temporary location.
Then verify:
wp-admin/
wp-content/
wp-includes/
wp-config.php
or the site’s expected structure.
25. Don’t Assume Every Site Is WordPress
Your CHP platform is currently WordPress-focused.
But the architecture should eventually support:
WordPress
PHP application
static site
Laravel
custom PHP
Therefore the restore engine should eventually know:
APPLICATION_TYPE=wordpress
rather than assuming WordPress forever.
26. Current WordPress Implementation
For now:
APPLICATION_TYPE=wordpress
can be stored in:
site.conf
Example:
APPLICATION_TYPE=wordpress
Then:
hosting-restore
↓
application type
↓
WordPress restore verification
27. File Ownership
After restoring files:
chown -R "$SITE_USER:$SITE_USER" "$WEB_ROOT"
But ownership should match the architecture established during provisioning.
Don’t blindly apply ownership rules to unrelated server paths.
28. Permissions
Then restore expected permissions.
Typical baseline:
directories → 755
files → 644
But special files may require different permissions.
For example:
wp-config.php
may need tighter permissions depending on your PHP-FPM and deployment model.
29. Don’t Run chmod -R 777
Never use:
chmod -R 777
as a restore solution.
It hides permission problems while creating unnecessary security exposure.
30. Verify WordPress
After restoring:
sudo -u "$SITE_USER" \
wp --path="$WEB_ROOT" core is-installed
Then:
sudo -u "$SITE_USER" \
wp --path="$WEB_ROOT" db check
And:
sudo -u "$SITE_USER" \
wp --path="$WEB_ROOT" core verify-checksums
31. Verify Site URL
Check:
sudo -u "$SITE_USER" \
wp --path="$WEB_ROOT" option get siteurl
and:
sudo -u "$SITE_USER" \
wp --path="$WEB_ROOT" option get home
Make sure they match the intended domain/environment.
32. Important Restore Problem
Suppose the backup was created when the site used:
https://example.com
but the current site configuration is:
https://new.example.com
Restoring the old database may restore:
https://example.com
This can create redirects or incorrect URLs.
Therefore restore should preserve the backup’s application state unless the administrator explicitly requests a migration.
33. Restore vs Migration
These are different operations.
Restore
example.com
↓
example.com
Migration
old.example.com
↓
new.example.com
Don’t mix them.
A future command should be something like:
hosting-migrate old.example.com new.example.com
34. SSL During Restore
The database/files backup doesn’t necessarily mean the certificate should be restored as raw certificate files.
Certificates should remain managed by the SSL subsystem.
After restore:
website restored
↓
hosting-ssl status
↓
certificate still valid?
If necessary:
hosting-ssl
can repair/reconfigure HTTPS.
35. Nginx Configuration
Similarly, restore should not blindly overwrite the entire server’s Nginx configuration.
Your backup may contain:
site.conf
as metadata, but Nginx configuration should be generated from the current CHP templates.
Therefore:
Backup
↓
site metadata
↓
current CHP template
↓
Nginx configuration
This avoids restoring obsolete server configuration.
36. PHP Configuration
Same principle.
Don’t blindly restore an old:
/etc/php/8.3/fpm/pool.d/
configuration from a backup.
Instead:
site metadata
↓
current PHP template
↓
current PHP configuration
The backup should restore customer/application data, not necessarily obsolete infrastructure configuration.
37. This Is a Critical Architecture Principle
Separate:
Customer Data
WordPress
uploads
database
themes
plugins
from:
Infrastructure Configuration
Nginx
PHP-FPM
SSL
systemd
firewall
server packages
The restore engine should treat them differently.
38. Restore Architecture
BACKUP
│
┌─────────┴─────────┐
▼ ▼
Customer Data Site Metadata
│ │
▼ ▼
Restore Regenerate
│ infrastructure
│ │
└─────────┬─────────┘
▼
HEALTH CHECK
39. Final Health Check
After restoration:
hosting-health example.com
must run.
Expected:
[OK] Web Root
[OK] PHP-FPM
[OK] PHP Socket
[OK] Nginx
[OK] WordPress
[OK] Database
[OK] HTTP
[OK] HTTPS
Only then:
STATUS=ACTIVE
40. Restore Failure
Suppose:
Files ✓
Database ✓
WordPress ✓
HTTP ✗
Do not mark:
ACTIVE
Instead:
RESTORE_ERROR
Then rollback if appropriate.
41. Rollback
The safest high-level sequence is:
Current site
↓
Pre-restore backup
↓
Selected backup
↓
Restore
↓
Health failure
↓
Restore pre-restore backup
This creates a recovery chain.
42. Restore State Machine
ACTIVE
│
▼
RESTORE_REQUESTED
│
▼
VERIFYING
│
▼
PRE_RESTORE_BACKUP
│
▼
RESTORING
│
▼
VERIFYING
│
▼
HEALTH_CHECK
│
┌┴──────────┐
▼ ▼
PASS FAIL
│ │
▼ ▼
ACTIVE ROLLBACK
│
▼
ACTIVE
If rollback itself fails:
ROLLBACK_ERROR
and immediate administrator intervention is required.
43. Restore Logs
Example:
2026-08-13 17:00 RESTORE_START
2026-08-13 17:00 BACKUP_VERIFIED
2026-08-13 17:01 PRE_RESTORE_BACKUP_COMPLETE
2026-08-13 17:02 FILES_RESTORED
2026-08-13 17:03 DATABASE_RESTORED
2026-08-13 17:03 PERMISSIONS_FIXED
2026-08-13 17:03 WORDPRESS_VERIFIED
2026-08-13 17:04 HEALTH_CHECK
2026-08-13 17:04 RESTORE_COMPLETE
44. Restore Output
A successful restore:
CresignSys Restore
==================
Domain:
example.com
Backup:
2026-08-13_160000
[OK] Backup verified
[OK] Current site backed up
[OK] Files restored
[OK] Database restored
[OK] Ownership verified
[OK] WordPress verified
[OK] Nginx verified
[OK] PHP verified
[OK] Health check
Status:
ACTIVE
45. Failed Restore
CresignSys Restore
==================
Domain:
example.com
[OK] Backup verified
[OK] Current site backed up
[OK] Files restored
[OK] Database restored
[FAIL] WordPress verification
Attempting rollback...
[OK] Previous files restored
[OK] Previous database restored
[OK] Health check
Status:
ACTIVE
This is the kind of behavior you want from a production system.
46. Dry Run
Because restore is dangerous, add:
sudo hosting-restore example.com 2026-08-13_160000 --dry-run
The dry run should show:
Restore Plan
============
Domain:
example.com
Backup:
2026-08-13_160000
Files:
1.4 GB
Database:
28 MB
Actions:
[ ] Create pre-restore backup
[ ] Restore files
[ ] Restore database
[ ] Fix permissions
[ ] Verify WordPress
[ ] Health check
No changes made.
47. Confirmation
For destructive operations, interactive confirmation can provide another safety barrier.
For example:
WARNING:
This operation will replace the current website state.
Current:
2026-08-13 17:00
Restore:
2026-08-10 16:00
Continue? [yes/no]
For automated systems, use an explicit non-interactive approval flag rather than relying on a prompt.
48. Why --force Is Dangerous
Avoid:
hosting-restore example.com BACKUP --force
unless --force has a precisely defined meaning.
Better:
hosting-restore example.com BACKUP --confirm
where the semantics are explicitly:
I understand this operation will modify the live site.
49. Restore Database Credentials
The restored wp-config.php may contain credentials corresponding to the backup environment.
If the current database credentials differ, WordPress may stop working.
Therefore your restore system should decide which configuration is authoritative.
For CHP:
CURRENT INFRASTRUCTURE
↓
current DB credentials
should generally remain authoritative.
Customer data is restored separately.
50. Important Separation
Don’t blindly restore:
old wp-config.php
over the current environment if it contains environment-specific configuration.
A safer design is:
backup wp-config.php
↓
extract application settings
↓
preserve current CHP-managed credentials/configuration
This is another reason to separate application data from infrastructure configuration.
51. WordPress Restore Strategy
A mature CHP restore can therefore do:
Restore:
wp-admin
wp-content
wp-includes
application files
database
Preserve/regenerate:
wp-config.php
Nginx
PHP-FPM
SSL
server configuration
The exact boundary depends on your deployment architecture.
52. Backup Manifest Improvement
Record which components were backed up:
{
"domain": "example.com",
"site_id": "1027",
"application": "wordpress",
"files": true,
"database": true,
"site_metadata": true,
"nginx_config": false,
"php_config": false,
"ssl_private_key": false
}
This prevents ambiguity during restoration.
53. Restore Compatibility
A backup may have been created under:
PHP 8.2
while the current server uses:
PHP 8.3
The restore system should report:
Backup PHP:
8.2
Current PHP:
8.3
rather than silently assuming compatibility.
54. Compatibility Warning
For example:
WARNING:
Backup was created under PHP 8.2.
Current site uses PHP 8.3.
Continue?
This is especially important for old WordPress plugins and custom PHP code.
55. Database Version
Similarly record:
MySQL version
in the backup manifest.
For example:
{
"mysql_version": "8.0"
}
Then the restore process can identify major compatibility changes.
56. Restore Testing
The best way to test restore is:
Production
↓
Backup
↓
Temporary environment
↓
Restore
↓
Health check
This is called a:
Restore Test
You should eventually automate this.
57. Disaster Recovery Testing
A serious hosting platform should periodically test:
Can we actually restore this site?
not simply:
Does a backup file exist?
This distinction is critical.
58. Recovery Point Objective
RPO means approximately:
How much recent data can we afford to lose?
Example:
RPO = 24 hours
means a failure could potentially lose up to a day’s changes.
If:
RPO = 1 hour
you need much more frequent backup/snapshot mechanisms.
59. Recovery Time Objective
RTO means:
How quickly must the site be restored?
Example:
RTO = 4 hours
versus:
RTO = 15 minutes
The second requires much more sophisticated infrastructure.
60. CHP Backup Strategy
Eventually hosting plans could define:
Plan
│
├── Backup frequency
├── Retention
├── Remote backup
├── RPO
└── RTO
This makes backup a proper hosting service rather than simply a Bash script.
61. Current CHP Recovery Layer
We now have:
RECOVERY
│
┌───────────┴───────────┐
▼ ▼
Backup Restore
│ │
▼ ▼
Verify Verify
│ │
└───────────┬───────────┘
▼
Health Check
│
▼
ACTIVE
62. Complete Website Lifecycle
The platform now looks like:
CREATE
↓
CONFIGURE
↓
SSL
↓
ACTIVE
↓
HEALTH
↓
BACKUP
↓
REPAIR
↓
RESTORE
↓
ACTIVE
63. Current Command Set
hosting-create DOMAIN
hosting-list
hosting-info DOMAIN
hosting-health DOMAIN
hosting-repair DOMAIN --php
hosting-repair DOMAIN --nginx
hosting-repair DOMAIN --permissions
hosting-repair DOMAIN --wordpress
hosting-ssl DOMAIN
hosting-backup DOMAIN
hosting-restore DOMAIN BACKUP_ID
This is becoming a real operational platform.
64. Lesson 071 — Core Principle
The safest restore architecture is:
VERIFY
↓
PROTECT CURRENT STATE
↓
RESTORE
↓
VERIFY
↓
HEALTH CHECK
↓
ACTIVATE
Never:
DELETE CURRENT DATA
↓
HOPE RESTORE WORKS
The key rule is:
A restore operation must have a recovery path of its own.
Next Lesson — 072
Build hosting-backup-list and Retention
We now need to manage multiple backups.
The next commands will be:
sudo hosting-backup-list example.com
sudo hosting-backup-delete example.com BACKUP_ID
and eventually:
sudo hosting-backup-prune example.com
The system will calculate:
Daily backups
Weekly backups
Monthly backups
Retention
Backup size
Oldest backup
Newest backup
Failed backups
and safely remove only backups that are eligible for deletion.
Leave a Reply