Features
Backups & Restore
Restore from Backup

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:

  1. Original Environment - Restore backup to where it was created (rollback)
  2. Existing Environment - Restore backup to a different environment
  3. New Environment - Create new environment and restore into it
  4. 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 COMPLETED and be verified
  • Permission: User has project.backups.restore permission (cross-server requires org.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

  1. Go to DashboardEnvironments
  2. Click environment name
  3. Click Backups tab
  4. Find completed backup in list
  5. 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:

  1. Create environment database record
  2. Deploy Docker containers (Odoo, PostgreSQL, PgBouncer)
  3. Restore backup into new environment
  4. 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

  1. 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
  2. Click "Restore Backup" button

  3. 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 restore

Status Progression:

  1. PENDING - Queued, waiting to start
  2. DOWNLOADING - Downloading backup from storage
  3. RESTORING_DB - Restoring PostgreSQL database
  4. RESTORING_FILES - Restoring Odoo filestore
  5. VERIFYING - Verifying restore integrity
  6. COMPLETED - 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 (requires environment_id)
  • "new" - Create new environment (requires new_environment config)
  • "cross_server" - Cross-server restore (requires server_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 RUNNING

Detailed 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.id

Skipped 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:

  1. Get storage configuration for backup
  2. Create temporary directory: /tmp/restore-{uuid}/
  3. Download ZIP from cloud storage
  4. Extract contents:
    • dump.sql - PostgreSQL dump
    • filestore.tar.gz - Filestore archive
    • manifest.json - Metadata
  5. 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}_odoo

b) 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.dump

f) 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 databases

g) Clean Up and Start

docker exec {env_id}_db rm -f /tmp/restore.dump
docker start {env_id}_odoo

Step 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.gz

d) Extract Filestore

docker exec {env_id}_odoo tar -xzf /tmp/filestore.tar.gz \
  -C /var/lib/odoo/filestore

e) 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.gz

Step 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 failed

b) 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 exist

All checks passed → Restore marked as COMPLETED


Step 6: Cleanup and Finalize

What happens:

  1. Delete temporary files on backend server
  2. Delete temporary files on remote server
  3. Update restore_point:
    • status = COMPLETED
    • completed_at = now()
    • duration_seconds = elapsed
  4. Update environment (if new):
    • status = RUNNING
  5. Broadcast SSE event: task.completed

Restore Modes Comparison

FeatureOriginalExistingNewCross-Server
Overwrites DataYesYesNoNo
Creates EnvironmentNoNoYesYes
Pre-Restore BackupYesYesNoNo
Cross-OrgNoNoNoNo
Cross-ServerNoNoNoYes
Downtime2-10 min2-10 min0 (new)0 (new)
Permission Requiredproject.backups.restoreproject.backups.restoreproject.backups.restoreorg.backups.restore
Use CaseRollbackClone dataDuplicateMigrate 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:

  1. Check container logs:
    docker logs {env_id}_odoo --tail 100
  2. Look for Odoo startup errors
  3. Verify database restored correctly
  4. Check Odoo configuration file
  5. If pre-restore backup exists, restore from it
  6. 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:

  1. Check backup status:
    GET /api/v1/backups/{backup_id}
  2. Verify status = "completed" and is_verified = true
  3. Test storage connection
  4. Check backup file exists in storage
  5. 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:

  1. Check restore progress in task details
  2. View current step and progress percentage
  3. Large databases can take 30-60 minutes
  4. If truly stuck (no progress for 30 min), cancel and retry
  5. 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}_odoo

Issue 5: "Permission Denied" Error

Symptoms: API returns 403 Forbidden

Causes:

  • Missing project.backups.restore permission
  • Missing org.backups.restore (for cross-server)
  • Not a member of target organization

Solution:

  1. Check your role in organization settings
  2. Request restore permission from admin
  3. For cross-server, need org-level permission
  4. 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


Last Updated: December 11, 2025 Applies to: OEC.SH v2.0+ Related Sprint: Sprint 2E41 - Documentation System