Restore from Backup
Feature ID: BAK-002
Category: Backups & Restore
Required Permission: project.backups.restore (or org.backups.restore for cross-server)
API Endpoint: POST /api/v1/backups/restore/v2
Overview
Restore Odoo environments from backups with flexible targeting options. OEC.SH supports restoring to the same environment, different environments, or creating new environments during restore. Advanced users can perform cross-server restores for disaster recovery or load balancing.
Use this feature when you need to:
- Recover from accidental data loss or corruption
- Revert to a previous stable state after problematic updates
- Clone production data to staging for testing
- Migrate environments between servers
- Restore deleted environments from backups
- Test disaster recovery procedures
Restore modes available:
- Original Environment - Restore backup to where it was created (rollback)
- Existing Environment - Restore backup to a different environment
- New Environment - Create new environment and restore into it
- Cross-Server - Create environment on different server and restore
What gets restored:
- ✅ PostgreSQL database (complete structure + data)
- ✅ Odoo filestore (attachments, documents, images) if included
- ✅ Environment configuration from snapshot (Odoo version, modules)
Prerequisites
Required Conditions
- Completed Backup: Backup must have status
COMPLETEDand be verified - Permission: User has
project.backups.restorepermission (cross-server requiresorg.backups.restore) - Storage Accessible: Storage provider credentials valid and backup file exists
- Target Environment: Destination environment accessible (for existing environment restore)
Important Notes
- Downtime: Target environment will be stopped during restore (typically 2-10 minutes)
- Data Loss: Existing data in target environment will be replaced
- Safety Backup: System can create pre-restore backup before overwriting data
- Irreversible: Restore cannot be undone (use pre-restore backup to rollback)
How to Restore a Backup
Method 1: Via UI (Recommended)
Step 1: Navigate to Backup List
- Go to Dashboard → Environments
- Click environment name
- Click Backups tab
- Find completed backup in list
- Click "Restore" button (right side of backup row)
Step 2: Choose Restore Target Mode
RestoreDialog opens with 4 restore mode options:
Mode 1: Original Environment (Rollback)
○ Restore to Original Environment
Restore backup to the environment where it was created.
Perfect for rolling back to a previous state.When to use:
- Production environment has issues after update
- Need to quickly revert to known-good state
- Testing changes went wrong
What happens:
- Stops Odoo container
- Drops and recreates database
- Restores filestore
- Starts container
- Environment returns to backup state
Mode 2: Different Existing Environment
○ Restore to Different Environment
Select an existing environment to restore this backup into.
Useful for cloning production data to staging.When to use:
- Clone production to staging for testing
- Copy data between development and QA
- Refresh test data from production
Additional fields:
- Environment dropdown: Select compatible environment from list
Compatibility checks:
- ✓ Same project
- ✓ Same or compatible Odoo version
- ✓ User has restore permission
Mode 3: Create New Environment
○ Create New Environment
Create a brand new environment and restore backup into it.
Creates a complete copy with fresh containers.When to use:
- Need additional copy of environment
- Want isolated testing environment
- Restore deleted environment from backup
Additional fields:
- Environment Name: e.g., "production-clone-2024-12"
- Environment Type: Development | Staging | Production | Testing
- Server: Optional (uses same server as source by default)
What happens:
- Create environment database record
- Deploy Docker containers (Odoo, PostgreSQL, PgBouncer)
- Restore backup into new environment
- Mark environment as RUNNING
Mode 4: Cross-Server Restore
○ Different Server (Cross-Server)
Create environment on a different server and restore.
For load balancing or disaster recovery.When to use:
- Migrate environment to less-loaded server
- Disaster recovery to backup server
- Regional deployment (move to different datacenter)
Additional fields:
- Server dropdown: Shows compatible servers with resource indicators
- Environment Name: Name for new environment
- Environment Type: Development | Staging | Production | Testing
Server compatibility displayed:
Server 1 (203.0.113.100)
Available: 4 CPU | 8 GB RAM | 50 GB Disk
✓ Recommended
Server 2 (203.0.113.101)
Available: 1 CPU | 2 GB RAM | 10 GB Disk
⚠️ Low resources (needs 2 CPU, 4 GB RAM, 20 GB disk)Step 3: Configure Safety Options
Pre-Restore Backup
- ☑ Create safety backup first (enabled by default)
- Creates backup of target environment before overwriting
- Allows rollback if restore fails or result is undesired
- Recommended: Always enabled for production
Skipped for:
- New environment restores (no existing data)
- User explicitly disables (not recommended)
Include Filestore
- ☑ Restore filestore (enabled by default)
- Restores Odoo attachments, documents, images
- Disable only if filestore not needed or managed separately
Step 4: Type-to-Confirm Validation
Safety mechanism: Must type "RESTORE" to confirm
┌───────────────────────────────────────────┐
│ Type RESTORE to confirm │
│ ┌───────────────────────────────────────┐ │
│ │ [___________________________] │ │
│ └───────────────────────────────────────┘ │
│ │
│ ⚠️ This will replace all data in the │
│ target environment. This cannot be │
│ undone without the pre-restore backup.│
└───────────────────────────────────────────┘Why: Prevents accidental data loss from misclicks
Step 5: Start Restore
-
Review summary:
Restore Summary ───────────────────────────────────────── Source Backup: Manual Backup (Dec 11, 2024) Size: 1.2 GB (verified ✓) Target: New Environment "prod-clone" Server: Server 1 (203.0.113.100) Type: Staging Options: ✓ Create pre-restore backup ✓ Include filestore -
Click "Restore Backup" button
-
Dialog closes, restore queued notification:
✓ Restore operation queued successfully Monitor progress in the task list
Step 6: Monitor Restore Progress
Real-Time Status Updates (via SSE):
Restore Progress
Status: Restoring database...
Started: 2024-12-11 11:00:00
Elapsed: 2 minutes 15 seconds
ETA: 5 minutes
Steps:
✓ Creating pre-restore backup (10%)
✓ Downloading backup (30%)
✓ Extracting backup (40%)
⟳ Restoring database (60%)
○ Restoring filestore
○ Verifying restoreStatus Progression:
PENDING- Queued, waiting to startDOWNLOADING- Downloading backup from storageRESTORING_DB- Restoring PostgreSQL databaseRESTORING_FILES- Restoring Odoo filestoreVERIFYING- Verifying restore integrityCOMPLETED- Restore finished successfully
Step 7: Verify Completion
Once complete, restore point shows:
- ✓ Status: Completed
- Duration: 6 minutes 32 seconds
- Target Environment: prod-clone (click to view)
- Pre-Restore Backup: backup-xyz (safety backup created)
Environment now available:
- Navigate to restored environment
- Verify data is correct
- Test functionality
- If issues found, restore from pre-restore backup
Method 2: Via API
Simple Restore (V1 API)
Endpoint: POST /api/v1/backups/restore
POST /api/v1/backups/restore
Content-Type: application/json
Authorization: Bearer YOUR_TOKEN
{
"backup_id": "backup-uuid",
"target_environment_id": "env-uuid",
"create_pre_restore_backup": true,
"include_filestore": true
}Response (200 OK):
{
"id": "restore-uuid",
"backup_id": "backup-uuid",
"target_environment_id": "env-uuid",
"status": "pending",
"pre_restore_backup_id": null,
"started_at": null,
"completed_at": null,
"error_message": null
}Advanced Restore (V2 API)
Endpoint: POST /api/v1/backups/restore/v2
POST /api/v1/backups/restore/v2
Content-Type: application/json
Authorization: Bearer YOUR_TOKEN
{
"backup_id": "backup-uuid",
"target": {
"mode": "new",
"new_environment": {
"name": "production-clone",
"environment_type": "staging",
"server_id": null
}
},
"create_pre_restore_backup": true,
"include_filestore": true
}Target Modes:
"original"- Restore to original environment"existing"- Restore to different environment (requiresenvironment_id)"new"- Create new environment (requiresnew_environmentconfig)"cross_server"- Cross-server restore (requiresserver_id)
Response (200 OK):
{
"restore_point_id": "restore-uuid",
"task_id": "task-uuid",
"target_environment_id": "new-env-uuid",
"target_environment_name": "production-clone",
"is_new_environment": true,
"status": "queued",
"message": "Restore operation queued successfully"
}Monitor Restore Progress (API)
GET /api/v1/backups/restore/{restore_id}
Response:
{
"id": "restore-uuid",
"backup_id": "backup-uuid",
"target_environment_id": "env-uuid",
"task_id": "task-uuid",
"status": "restoring_db",
"is_new_environment": true,
"is_cross_server": false,
"target_vm_id": "server-uuid",
"pre_restore_backup_id": "pre-backup-uuid",
"started_at": "2024-12-11T11:00:00Z",
"completed_at": null,
"duration_seconds": null,
"error_message": null,
"error_step": null
}Understanding the Restore Process
Restore Flow Overview
1. Validation
├── Check backup status = COMPLETED
├── Verify user permissions
└── Validate target environment (if existing)
2. Pre-Restore Backup (if enabled)
├── Create backup of target environment
└── Wait for backup to complete
3. Download Backup
├── Connect to storage provider
├── Download ZIP to temporary directory
└── Extract: dump.sql, filestore.tar.gz, manifest.json
4. Restore Database
├── Stop Odoo container
├── Terminate database connections
├── Drop existing database
├── Create fresh database
├── Restore from dump.sql (pg_restore)
└── Verify database connectivity
5. Restore Filestore (if included)
├── Remove existing filestore directory
├── Extract filestore.tar.gz
├── Rename directory (if database name changed)
└── Fix permissions (odoo:odoo)
6. Verify Restore
├── Check container health
├── Test database connection
├── Query test (SELECT COUNT(*) FROM res_users)
└── Mark restore as COMPLETED
7. Cleanup
├── Delete temporary files
├── Start Odoo container
└── Update environment status to RUNNINGDetailed Restore Steps
Step 1: Pre-Restore Safety Backup (if enabled)
Duration: 2-10 minutes (depends on size)
What happens:
if create_pre_restore_backup:
pre_backup = await backup_service.create_backup(
environment_id=target_env_id,
backup_type="pre_restore",
user_id=current_user_id
)
restore_point.pre_restore_backup_id = pre_backup.idSkipped for: New environment restores (no existing data)
Result: Safety backup created and linked to restore point
Step 2: Download Backup from Storage
Duration: 1-10 minutes (depends on size and network)
What happens:
- Get storage configuration for backup
- Create temporary directory:
/tmp/restore-{uuid}/ - Download ZIP from cloud storage
- Extract contents:
dump.sql- PostgreSQL dumpfilestore.tar.gz- Filestore archivemanifest.json- Metadata
- Verify checksums match manifest
Example:
# Download from R2
aws s3 cp s3://bucket/backups/org-abc/backup-xyz.zip /tmp/restore-123/
# Extract
unzip /tmp/restore-123/backup-xyz.zip -d /tmp/restore-123/
# Files extracted:
/tmp/restore-123/dump.sql (50 MB)
/tmp/restore-123/filestore.tar.gz (1.1 GB)
/tmp/restore-123/manifest.json (5 KB)Step 3: Restore Database
Duration: 1-5 minutes (depends on database size)
Critical Steps:
a) Stop Odoo Container
docker stop {env_id}_odoob) Terminate All Database Connections
docker exec {env_id}_db psql -U odoo -d postgres -c \
"SELECT pg_terminate_backend(pid) FROM pg_stat_activity \
WHERE datname = '{db_name}' AND pid <> pg_backend_pid();"c) Drop Existing Database
docker exec {env_id}_db psql -U odoo -d postgres -c \
'DROP DATABASE IF EXISTS "{db_name}";'Important: Database names with hyphens must be quoted: "{db_name}"
d) Create Fresh Database
docker exec {env_id}_db psql -U odoo -d postgres -c \
'CREATE DATABASE "{db_name}" OWNER odoo;'e) Upload Dump to Server
# Via SFTP
sftp.put(local_dump_path, remote_dump_path)
# Copy into container
docker cp {remote_dump} {env_id}_db:/tmp/restore.dumpf) Restore Using pg_restore
docker exec {env_id}_db pg_restore -U odoo \
-d {db_name} -v /tmp/restore.dump
# Timeout: 1 hour for large databasesg) Clean Up and Start
docker exec {env_id}_db rm -f /tmp/restore.dump
docker start {env_id}_odooStep 4: Restore Filestore (if included)
Duration: 1-5 minutes (depends on filestore size)
What happens:
a) Upload Archive to Server
sftp.put(local_archive_path, remote_archive_path)b) Remove Existing Filestore
docker exec {env_id}_odoo rm -rf \
/var/lib/odoo/filestore/{db_name}c) Copy Archive into Container
docker cp {remote_archive} {env_id}_odoo:/tmp/filestore.tar.gzd) Extract Filestore
docker exec {env_id}_odoo tar -xzf /tmp/filestore.tar.gz \
-C /var/lib/odoo/filestoree) Rename Directory (if database name changed)
# Get original database name from backup snapshot
original_db = backup.environment_snapshot["db_name"]
# Rename if different
if original_db != target_db:
docker exec {env_id}_odoo mv \
/var/lib/odoo/filestore/{original_db} \
/var/lib/odoo/filestore/{target_db}f) Fix Permissions
docker exec {env_id}_odoo chown -R odoo:odoo \
/var/lib/odoo/filestore/{db_name}g) Clean Up
docker exec --user root {env_id}_odoo rm -f /tmp/filestore.tar.gzStep 5: Verify Restore
Duration: 10-30 seconds
Health Checks:
a) Wait for Container Health
# Poll container health status (max 30 attempts, 2 sec interval)
docker inspect --format='{{.State.Health.Status}}' {env_id}_odoo
# Expected: "healthy"
# If "unhealthy": Restore failedb) Verify Container Running
docker inspect --format='{{.State.Running}}' {env_id}_odoo
# Expected: "true"c) Test Database Connection
docker exec {env_id}_db psql -U odoo -d "{db_name}" \
-c "SELECT COUNT(*) FROM res_users;"
# Expected: Returns user count (e.g., "5")
# Verifies: Database accessible, tables existAll checks passed → Restore marked as COMPLETED
Step 6: Cleanup and Finalize
What happens:
- Delete temporary files on backend server
- Delete temporary files on remote server
- Update restore_point:
status = COMPLETEDcompleted_at = now()duration_seconds = elapsed
- Update environment (if new):
status = RUNNING
- Broadcast SSE event:
task.completed
Restore Modes Comparison
| Feature | Original | Existing | New | Cross-Server |
|---|---|---|---|---|
| Overwrites Data | Yes | Yes | No | No |
| Creates Environment | No | No | Yes | Yes |
| Pre-Restore Backup | Yes | Yes | No | No |
| Cross-Org | No | No | No | No |
| Cross-Server | No | No | No | Yes |
| Downtime | 2-10 min | 2-10 min | 0 (new) | 0 (new) |
| Permission Required | project.backups.restore | project.backups.restore | project.backups.restore | org.backups.restore |
| Use Case | Rollback | Clone data | Duplicate | Migrate server |
Troubleshooting
Issue 1: Restore Fails with "Container Health Check Failed"
Symptoms: Restore stuck at "Verifying" step, then fails
Causes:
- Odoo container failed to start after restore
- Database configuration incorrect
- Missing required Odoo modules
Solution:
- Check container logs:
docker logs {env_id}_odoo --tail 100 - Look for Odoo startup errors
- Verify database restored correctly
- Check Odoo configuration file
- If pre-restore backup exists, restore from it
- Contact support with container logs
Issue 2: "Backup Not Available for Restore"
Symptoms: API returns 400 Bad Request
Causes:
- Backup status not COMPLETED
- Backup file deleted from storage
- Backup verification failed
Solution:
- Check backup status:
GET /api/v1/backups/{backup_id} - Verify
status = "completed"andis_verified = true - Test storage connection
- Check backup file exists in storage
- Use different backup if this one is corrupted
Issue 3: Restore Takes Longer Than Expected
Symptoms: Restore stuck at same step for 15+ minutes
Causes:
- Very large database (>10 GB)
- Slow download from storage
- Server under heavy load
Solution:
- Check restore progress in task details
- View current step and progress percentage
- Large databases can take 30-60 minutes
- If truly stuck (no progress for 30 min), cancel and retry
- Consider excluding filestore to speed up
Issue 4: Database Name Changed, Filestore Not Working
Symptoms: Restore completed but attachments/documents not accessible
Cause: Filestore directory name doesn't match database name
Explanation: The restore process should automatically rename the filestore directory, but if it fails:
Manual Fix:
# SSH into server
ssh user@server-ip
# Rename filestore directory
docker exec {env_id}_odoo mv \
/var/lib/odoo/filestore/{old_db_name} \
/var/lib/odoo/filestore/{new_db_name}
# Fix permissions
docker exec {env_id}_odoo chown -R odoo:odoo \
/var/lib/odoo/filestore/{new_db_name}
# Restart container
docker restart {env_id}_odooIssue 5: "Permission Denied" Error
Symptoms: API returns 403 Forbidden
Causes:
- Missing
project.backups.restorepermission - Missing
org.backups.restore(for cross-server) - Not a member of target organization
Solution:
- Check your role in organization settings
- Request restore permission from admin
- For cross-server, need org-level permission
- Verify you're a member of both source and target orgs
Best Practices
1. Always Enable Pre-Restore Backup
✅ Do: Create safety backup before restore (enabled by default) ✅ Do: Verify pre-restore backup completed before proceeding ❌ Don't: Skip pre-restore backup for production environments
Reason: If restore fails or result is incorrect, you can restore from pre-restore backup to recover.
2. Test Restores in Non-Production First
✅ Do: Test restore process in staging/development ✅ Do: Verify restored data is complete and correct ✅ Do: Test application functionality after restore ❌ Don't: Test restore procedures in production for the first time
3. Plan for Downtime
✅ Do: Schedule restores during maintenance windows ✅ Do: Notify users of downtime ✅ Do: Estimate restore duration based on backup size ❌ Don't: Restore production during business hours without planning
Downtime Estimates:
- Small backup (less than 1 GB): 2-5 minutes
- Medium backup (1-5 GB): 5-10 minutes
- Large backup (more than 5 GB): 10-30 minutes
4. Verify Restored Environment
✅ Do: Log in and verify data is correct ✅ Do: Test critical workflows ✅ Do: Check attachment/document access ✅ Do: Verify user accounts work ❌ Don't: Assume restore is correct without testing
5. Document Restore Procedures
✅ Do: Maintain runbook for restore process ✅ Do: Document common issues and solutions ✅ Do: Keep emergency contact list ❌ Don't: Rely on memory during crisis situations
Related Documentation
- Create Manual Backup - Create backups
- Backup Policies - Automated backups
- Clone Environment - Alternative to restore
Last Updated: December 11, 2025 Applies to: OEC.SH v2.0+ Related Sprint: Sprint 2E41 - Documentation System