Environment Cloning with Sanitization
Environment cloning allows you to duplicate entire Odoo environments, including database, filestore, and source code, with powerful data sanitization capabilities to protect sensitive information when creating staging or development environments.
Overview
The cloning system provides:
- Complete Environment Duplication: Database, filestore, custom addons, and configuration
- Data Sanitization Presets: Pre-configured sanitization levels for different use cases
- Cross-Server Cloning: Clone environments to different servers within your organization
- Granular Control: 15+ sanitization options with impact previews
- Background Processing: Asynchronous cloning with real-time progress tracking
- Automatic Verification: Post-clone validation to ensure successful duplication
Common Use Cases
- Production to Staging: Clone production with sanitized data for testing
- Staging to Development: Create development environments from staging
- Testing Scenarios: Duplicate environments for testing upgrades or migrations
- Training Environments: Create isolated environments with anonymized data
- Disaster Recovery: Quick environment restoration with data protection
Cloning Workflow
Step-by-Step Process
The clone operation follows a 10-step process:
-
Pre-Checks (2%)
- Resource availability validation
- Quota checks (CPU, RAM, disk, environment count)
- Server connectivity verification
- Permission validation
-
Source Validation (5%)
- Verify source environment is running or stopped
- Check container and database exist
- Validate environment is deployed
-
Target Creation (10%)
- Create new environment record or prepare existing target
- Generate unique subdomain for new environments
- Allocate resources from quota
-
Copying Addons (18%)
- Copy custom Odoo modules from source to target
- Handle same-server (cp) and cross-server (tar transfer) scenarios
- Preserve file permissions and ownership
-
Database Backup (25%)
- Execute
pg_dumpon source PostgreSQL container - Create compressed dump file (
-Fc -Z9format) - Store dump path for restore phase
- Execute
-
Filestore Backup (40%)
- Archive source filestore using tar/gzip
- Verify archive integrity
- Handle missing filestore gracefully
-
Database Restore (55%)
- Transfer dump to target server (if cross-server)
- Create target database
- Execute
pg_restorewith--no-owner --no-acl - Handle restoration warnings vs errors
-
Filestore Restore (70%)
- Transfer filestore archive to target (if cross-server)
- Extract to target container's
/var/lib/odoo/filestore/ - Rename filestore directory if database names differ
- Set correct ownership (
odoo:odoo) and permissions
-
Data Sanitization (85%)
- Apply selected sanitization options via SQL
- Execute Odoo-specific data masking
- Track execution results and affected rows
- Always clear asset bundles (required after clone)
-
Verification (95%)
- Verify target container is running
- Check database connectivity
- Validate key Odoo tables exist (
res_users,ir_module_module) - Count active users
-
Completion (100%)
- Mark environment as active and running
- Send notification to user
- Broadcast completion event via SSE
UI Workflow
From the environment list or detail page:
- Click Clone button on the environment card
- Configure clone settings:
- Target Name: Name for the cloned environment
- Environment Type: Development, Staging, or Production
- Target Server: Same server or select compatible server
- Domain: Optional custom domain
- Select Sanitization Preset:
- Recommended (default)
- Minimal
- Full
- None
- (Optional) Customize sanitization options
- Review impact preview showing affected record counts
- Click Start Clone
- Monitor real-time progress in the clone operation modal
- Receive notification on completion
Sanitization Presets
The system provides four preset configurations optimized for different scenarios.
Recommended (Default)
Best for: Production → Staging clones
Safe defaults that prevent data leaks while preserving functionality for testing.
Enabled Options (12):
- Disable outgoing mail servers
- Clear mail queue
- Anonymize customer emails
- Anonymize phone numbers
- Clear API keys and secrets
- Clear OAuth provider credentials
- Clear payment provider credentials
- Clear OAuth access tokens
- Clear webhook URLs
- Clear integration credentials
- Update base URL to staging domain
- Clear asset bundles (required)
Impact:
- Prevents accidental emails to real customers
- Removes production credentials
- Anonymizes customer PII
- Preserves user accounts for testing
{
"disable_mail_servers": true,
"clear_mail_queue": true,
"anonymize_emails": true,
"anonymize_phones": true,
"clear_api_keys": true,
"clear_oauth_providers": true,
"clear_payment_providers": true,
"clear_oauth_tokens": true,
"clear_webhook_urls": true,
"clear_integration_credentials": true,
"update_base_url": true,
"clear_asset_bundles": true
}Minimal
Best for: Quick staging clones where data is already sanitized
Only prevents email sending and clears asset bundles.
Enabled Options (3):
- Disable outgoing mail servers
- Clear mail queue
- Clear asset bundles (required)
Impact:
- Prevents emails only
- Keeps all other data intact
- Fastest sanitization (< 5 seconds)
{
"disable_mail_servers": true,
"clear_mail_queue": true,
"clear_asset_bundles": true
}Full
Best for: Development environments requiring maximum data protection
All 15+ sanitization options enabled.
Enabled Options (All):
- All options from Recommended preset
- Disable email templates
- Anonymize addresses
- Anonymize company names
- Reset user passwords to known value
- Disable 2FA/TOTP
- Disable scheduled actions (crons)
- Clear scheduled messages
Impact:
- Maximum data protection
- All credentials removed
- All PII anonymized
- Cron jobs disabled
- User passwords reset to
staging123
{
"disable_mail_servers": true,
"clear_mail_queue": true,
"disable_mail_templates": true,
"anonymize_emails": true,
"anonymize_phones": true,
"anonymize_addresses": true,
"anonymize_company_names": true,
"clear_api_keys": true,
"update_base_url": true,
"reset_passwords": true,
"reset_password_value": "staging123",
"disable_2fa": true,
"disable_crons": true,
"clear_scheduled_messages": true,
"clear_asset_bundles": true
}None
Best for: Exact production replica (use with caution)
No sanitization applied (except asset bundles which are always cleared).
Enabled Options (1):
- Clear asset bundles (required)
Impact:
- Identical copy of production
- All credentials active
- Risk of data leaks
- Use only in secure, isolated environments
{
"clear_asset_bundles": true
}Sanitization Options Reference
Email & Communications
Disable Outgoing Mail Servers
- Priority: HIGH
- Category: Email
- Default: Enabled in Recommended
- SQL:
UPDATE ir_mail_server SET active = false; - Impact: Disables all outgoing SMTP servers to prevent sending real emails
- Use Case: Essential for staging to avoid customer contact
Clear Mail Queue
- Priority: HIGH
- Category: Email
- Default: Enabled in Recommended
- SQL:
DELETE FROM mail_mail WHERE state IN ('outgoing', 'exception'); - Impact: Deletes pending and failed emails
- Use Case: Remove queued production emails
Disable Mail Templates
- Priority: LOW
- Category: Email
- Default: Disabled
- SQL:
UPDATE mail_template SET active = false WHERE active = true; - Impact: Deactivates all email templates
- Use Case: Extra protection against template-based emails
Customer Data
Anonymize Customer Emails
- Priority: HIGH
- Category: Customer Data
- Default: Enabled in Recommended
- SQL:
UPDATE res_partner SET email = CONCAT('user_', id, '@staging.local') WHERE email IS NOT NULL AND id NOT IN (SELECT partner_id FROM res_users WHERE active = true); - Impact: Replaces customer emails with
user_123@staging.localformat - Note: Preserves emails of active Odoo users for login
- Use Case: Prevent accidental customer emails, GDPR compliance
Anonymize Phone Numbers
- Priority: MEDIUM
- Category: Customer Data
- Default: Enabled in Recommended
- SQL:
UPDATE res_partner SET phone = CONCAT('555-', LPAD(id::text, 4, '0')), mobile = NULL WHERE (phone IS NOT NULL OR mobile IS NOT NULL) AND id NOT IN (SELECT partner_id FROM res_users WHERE active = true); - Impact: Replaces phones with
555-0001format, clears mobile - Use Case: PII protection, prevent accidental SMS
Anonymize Addresses
- Priority: LOW
- Category: Customer Data
- Default: Disabled
- SQL:
UPDATE res_partner SET street = CONCAT('Street ', id), street2 = NULL, city = 'Staging City', zip = '00000' WHERE street IS NOT NULL AND id NOT IN (SELECT partner_id FROM res_users WHERE active = true); - Impact: Replaces real addresses with generic values
- Use Case: Extra PII protection for regulated industries
Anonymize Company Names
- Priority: LOW
- Category: Customer Data
- Default: Disabled
- SQL:
UPDATE res_partner SET name = CONCAT('Company ', id) WHERE is_company = true AND id NOT IN (SELECT company_id FROM res_users WHERE active = true); - Impact: Replaces company names with
Company 123format - Use Case: Extra confidentiality for B2B databases
Security & Integrations
Clear API Keys and Secrets
- Priority: HIGH
- Category: Security
- Default: Enabled in Recommended
- SQL:
UPDATE ir_config_parameter SET value = '' WHERE key ILIKE '%api_key%' OR key ILIKE '%secret%' OR key ILIKE '%token%' OR key ILIKE '%password%' OR key ILIKE '%credential%'; - Impact: Removes all API keys, tokens, passwords from system parameters
- Use Case: Prevent staging from accessing production APIs
- Examples Cleared: Payment gateway keys, SMS provider tokens, analytics IDs
Update Base URL
- Priority: HIGH
- Category: Security
- Default: Enabled in Recommended
- SQL:
UPDATE ir_config_parameter SET value = {system_url} WHERE key = 'web.base.url'; - Impact: Updates
web.base.urlto staging domain - Use Case: Fix absolute URLs in emails, QR codes, links
Clear OAuth Provider Credentials
- Priority: HIGH
- Category: Security
- Default: Enabled in Recommended
- SQL:
UPDATE auth_oauth_provider SET client_id = '', client_secret = '', enabled = false WHERE client_id IS NOT NULL OR client_secret IS NOT NULL; - Impact: Removes OAuth credentials for Google, Microsoft, GitHub logins
- Use Case: Prevent staging users from logging in via production OAuth apps
Clear Payment Provider Credentials
- Priority: HIGH
- Category: Security
- Default: Enabled in Recommended
- SQL:
UPDATE payment_provider SET state = 'disabled', stripe_secret_key = NULL, stripe_publishable_key = NULL, paypal_email_account = NULL, paypal_seller_account = NULL, authorize_login = NULL, authorize_transaction_key = NULL, ogone_pspid = NULL, ogone_userid = NULL, ogone_password = NULL WHERE state != 'disabled'; - Impact: Disables and clears credentials for Stripe, PayPal, Authorize.net, etc.
- Use Case: Prevent staging from processing real payments
Clear OAuth Access Tokens
- Priority: HIGH
- Category: Security
- Default: Enabled in Recommended
- SQL:
DELETE FROM auth_oauth_token; UPDATE res_users SET oauth_access_token = NULL WHERE oauth_access_token IS NOT NULL; - Impact: Removes stored OAuth access/refresh tokens
- Use Case: Prevent staging from accessing production user accounts
Clear Webhook URLs
- Priority: MEDIUM
- Category: Security
- Default: Enabled in Recommended
- SQL:
UPDATE ir_config_parameter SET value = '' WHERE key ILIKE '%webhook%' OR key ILIKE '%callback_url%' OR key ILIKE '%notify_url%'; - Impact: Removes webhook URLs to prevent staging from triggering production webhooks
- Use Case: Prevent staging events from notifying production systems
Clear Integration Credentials
- Priority: MEDIUM
- Category: Security
- Default: Enabled in Recommended
- SQL:
UPDATE ir_config_parameter SET value = '' WHERE key ILIKE '%sms_%' OR key ILIKE '%shipping_%' OR key ILIKE '%analytics_%' OR key ILIKE '%google_analytics%' OR key ILIKE '%facebook_%' OR key ILIKE '%twitter_%' OR key ILIKE '%sendgrid_%' OR key ILIKE '%twilio_%' OR key ILIKE '%mailgun_%'; - Impact: Removes third-party service credentials
- Use Case: Prevent staging from using production integrations
Reset User Passwords
- Priority: LOW
- Category: Security
- Default: Disabled
- SQL:
UPDATE res_users SET password = {password_hash} WHERE active = true; - Impact: Resets all user passwords to
staging123(pbkdf2_sha512 hash) - Use Case: Known password for all users in development
- Note: Use with caution, creates security risk
Disable 2FA/TOTP
- Priority: LOW
- Category: Security
- Default: Disabled
- SQL:
DELETE FROM auth_totp_device; UPDATE res_users SET totp_secret = NULL WHERE totp_secret IS NOT NULL; - Impact: Removes all two-factor authentication devices
- Use Case: Simplify testing by removing 2FA requirement
Automation
Clear Asset Bundles
- Priority: HIGH (ALWAYS ENABLED)
- Category: Automation
- Default: Always enabled (cannot be disabled)
- SQL:
DELETE FROM ir_attachment WHERE name LIKE 'web.assets_%.min.%' OR name LIKE 'web.assets_%.css' OR name LIKE 'web.assets_%.js' OR res_model = 'ir.ui.view' AND res_field = 'arch_db'; - Impact: Deletes compiled CSS/JS bundles, forces regeneration
- Use Case: Required after clone to prevent broken UI from stale assets
- Note: This option is ALWAYS applied regardless of preset
Disable Scheduled Actions
- Priority: MEDIUM
- Category: Automation
- Default: Disabled
- SQL:
UPDATE ir_cron SET active = false WHERE name NOT ILIKE '%Garbage%' AND name NOT ILIKE '%Cleanup%'; - Impact: Deactivates all cron jobs except garbage collection
- Use Case: Prevent staging from running production automation
- Note: Preserves system cleanup crons
Clear Scheduled Messages
- Priority: MEDIUM
- Category: Automation
- Default: Disabled
- SQL:
DELETE FROM mail_mail WHERE state = 'outgoing' AND scheduled_date > NOW(); DELETE FROM sms_sms WHERE state = 'outgoing'; - Impact: Deletes future scheduled emails and SMS
- Use Case: Remove planned customer communications
Clone API Reference
Clone Environment
Initiates an asynchronous clone operation.
Endpoint: POST /api/v1/environments/{environment_id}/clone
Rate Limit: 5 clones per hour per user
Request Body:
{
"target": {
"create_new": true,
"name": "Staging Environment",
"environment_type": "staging",
"vm_id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
"domain": "staging.example.com"
},
"sanitization_preset": "recommended",
"sanitization_options": null,
"include_filestore": true
}Request Schema:
| Field | Type | Required | Description |
|---|---|---|---|
target | Object | Yes | Target configuration |
target.create_new | Boolean | Yes | true to create new env, false to overwrite |
target.name | String | If create_new | Name for new environment (1-100 chars) |
target.environment_type | String | If create_new | development, staging, or production |
target.vm_id | UUID | Optional | Target server (defaults to source server) |
target.domain | String | Optional | Custom domain for environment |
target.target_environment_id | UUID | If !create_new | Existing environment to overwrite |
sanitization_preset | Enum | Optional | recommended, minimal, full, none (default: recommended) |
sanitization_options | Object | Optional | Custom options (overrides preset) |
include_filestore | Boolean | Optional | Include filestore (default: true) |
Response (202 Accepted):
{
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"source_environment_id": "12345678-1234-1234-1234-123456789012",
"target_environment_id": null,
"status": "pending",
"progress": {
"current_step": "queued",
"progress_percent": 0,
"message": "Clone operation queued",
"steps_completed": 0,
"total_steps": 10
},
"sanitization_options": {
"disable_mail_servers": true,
"clear_mail_queue": true,
"anonymize_emails": true,
"clear_api_keys": true,
"update_base_url": true,
"clear_asset_bundles": true
},
"sanitization_result": null,
"started_at": null,
"completed_at": null,
"duration_seconds": null,
"error_message": null,
"error_step": null
}Status Codes:
202 Accepted: Clone operation queued successfully400 Bad Request: Invalid request (missing fields, invalid state)403 Forbidden: User doesn't have permission404 Not Found: Source environment not found429 Too Many Requests: Rate limit exceeded500 Internal Server Error: Failed to queue clone task
Error Examples:
{
"detail": "Cannot clone environment in state 'deploying'. Environment must be running or stopped."
}{
"detail": "CPU quota exceeded. Need 2 cores, but only 1 available (using 15/16)."
}Get Clone Operation Status
Retrieve the current status and progress of a clone operation.
Endpoint: GET /api/v1/clone-operations/{clone_op_id}
Response (200 OK):
{
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"source_environment_id": "12345678-1234-1234-1234-123456789012",
"target_environment_id": "87654321-4321-4321-4321-210987654321",
"status": "sanitizing",
"progress": {
"current_step": "sanitizing",
"progress_percent": 85,
"message": "Applying data sanitization",
"steps_completed": 9,
"total_steps": 10
},
"sanitization_options": {
"disable_mail_servers": true,
"clear_mail_queue": true,
"anonymize_emails": true,
"clear_api_keys": true
},
"sanitization_result": null,
"started_at": "2024-12-11T10:30:00Z",
"completed_at": null,
"duration_seconds": null,
"error_message": null,
"error_step": null
}Status Values:
pending: Queued, not yet startedpre_checks: Running pre-flight checksvalidating: Validating source environmentcreating_target: Creating target environmentcopying_addons: Copying custom modulesbacking_up_database: Creating database dumpbacking_up_filestore: Archiving filestorerestoring_database: Restoring database to targetrestoring_filestore: Extracting filestore to targetsanitizing: Applying data sanitizationverifying: Verifying clone integritycompleted: Clone successfulfailed: Clone failedcancelled: Clone cancelled by user
Get Clone Options
Get available sanitization options with impact counts.
Endpoint: GET /api/v1/environments/{environment_id}/clone/options
Response (200 OK):
{
"available_options": [
{
"key": "disable_mail_servers",
"name": "Disable outgoing mail servers",
"description": "Prevents staging from sending real emails by disabling all mail servers",
"category": "email",
"priority": "high",
"default_enabled": true,
"impact_preview": "3 mail server(s) will be disabled",
"impact_count": 3
},
{
"key": "clear_mail_queue",
"name": "Clear mail queue",
"description": "Delete all pending and failed emails from the mail queue",
"category": "email",
"priority": "high",
"default_enabled": true,
"impact_preview": "847 pending email(s) will be deleted",
"impact_count": 847
}
],
"presets": {
"recommended": [
"disable_mail_servers",
"clear_mail_queue",
"anonymize_emails",
"anonymize_phones",
"clear_api_keys",
"update_base_url",
"clear_asset_bundles"
],
"minimal": [
"disable_mail_servers",
"clear_mail_queue",
"clear_asset_bundles"
],
"full": ["disable_mail_servers", "...all options..."],
"none": ["clear_asset_bundles"]
},
"default_preset": "recommended"
}List Clone Operations
Get recent clone operations for an environment.
Endpoint: GET /api/v1/environments/{environment_id}/clone-operations
Query Parameters:
limit(optional): Max results (default: 10)
Response (200 OK):
[
{
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"source_environment_id": "12345678-1234-1234-1234-123456789012",
"target_environment_id": "87654321-4321-4321-4321-210987654321",
"status": "completed",
"progress_percent": 100,
"started_at": "2024-12-11T10:30:00Z",
"completed_at": "2024-12-11T10:45:23Z",
"created_by": "user-uuid",
"create_date": "2024-12-11T10:29:45Z"
}
]Get Compatible Servers
Get list of servers compatible for cross-server cloning.
Endpoint: GET /api/v1/environments/{environment_id}/clone/compatible-servers
Response (200 OK):
[
{
"id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
"name": "Production Server 2",
"ip_address": "46.101.96.153",
"available_cpu": 8,
"available_ram_mb": 16384,
"available_disk_gb": 120,
"current_environments": 3,
"recommended": true,
"incompatibility_reason": null
},
{
"id": "c2d3e4f5-g6h7-i8j9-k0l1-m2n3o4p5q6r7",
"name": "Dev Server 1",
"ip_address": "165.22.65.97",
"available_cpu": 1,
"available_ram_mb": 1024,
"available_disk_gb": 25,
"current_environments": 8,
"recommended": false,
"incompatibility_reason": "Insufficient RAM (available: 1024MB, required: 2048MB)"
}
]Cancel Clone Operation
Cancel a running clone operation.
Endpoint: POST /api/v1/clone-operations/{clone_op_id}/cancel
Response (200 OK):
{
"status": "cancelled",
"clone_operation_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
}Cancelable States:
pendingvalidatingcreating_target
Error Response (400 Bad Request):
{
"detail": "Cannot cancel clone operation with status 'sanitizing'"
}Cross-Server Cloning
Clone environments to different servers within your organization.
How It Works
- Source Backup: Create database dump and filestore archive on source server
- Network Transfer: Download files to PaaSPortal backend, upload to target server
- Target Restore: Extract database and filestore on target server
- Sanitization: Apply sanitization on target database
- Verification: Validate clone integrity on target
Transfer Process
Same Server:
- Database: Direct PostgreSQL restore (no file transfer)
- Filestore: Direct extraction (no file transfer)
- Addons:
cp -aorrsync
Cross-Server:
- Database:
pg_dump→ SFTP download → SFTP upload →pg_restore - Filestore:
tar -czf→ SFTP download → SFTP upload →tar -xzf - Addons:
tar -czf→ local temp → SFTP upload →tar -xzf
Network Requirements
- SSH access to both source and target servers
- Sufficient disk space on source server for temporary dump/archive files
- Sufficient network bandwidth for transfer (1-10 GB typical)
- Temporary local storage on PaaSPortal backend (cleaned up after transfer)
Performance Considerations
| Environment Size | Same Server | Cross-Server | Network Impact |
|---|---|---|---|
| Small (< 1 GB) | 2-5 min | 5-10 min | 1-2 GB transfer |
| Medium (1-5 GB) | 5-15 min | 15-30 min | 5-10 GB transfer |
| Large (> 5 GB) | 15-45 min | 45-90 min | 10+ GB transfer |
Selecting Target Server
The system automatically filters compatible servers:
Compatibility Checks:
- Server is online/running
- Sufficient CPU cores available
- Sufficient RAM available
- Sufficient disk space available
- Server belongs to same organization
Example: To clone a 2-core, 4GB RAM environment:
✓ Server A: 8 cores available, 16GB RAM → Compatible
✗ Server B: 1 core available, 8GB RAM → Insufficient CPU
✗ Server C: 4 cores, 2GB RAM → Insufficient RAMResource Allocation & Quotas
Default Resource Copying
By default, the cloned environment inherits resources from the source:
- CPU Cores: Same as source
- RAM (MB): Same as source
- Disk (GB): Same as source
- Environment Variables: Copied
- Odoo Configuration: Copied
- Settings: Copied
Override Options
When creating a new environment, you can override:
{
"target": {
"create_new": true,
"name": "Dev Environment",
"cpu_cores": 1,
"ram_mb": 1024,
"disk_gb": 10
}
}Quota Validation
Before cloning, the system validates:
- Environment Limit: Number of active environments in organization
- CPU Quota: Total allocated CPU cores
- RAM Quota: Total allocated RAM in MB
- Disk Quota: Total allocated disk in GB
Example Validation:
Organization Limits:
Max Environments: 10
Max CPU Cores: 16
Max RAM: 32768 MB (32 GB)
Max Disk: 500 GB
Current Usage:
Environments: 8/10 ✓
CPU: 14/16 cores ✓
RAM: 28672/32768 MB ✓
Disk: 350/500 GB ✓
Clone Request:
Source: 2 cores, 4096 MB, 20 GB
Validation:
Environments: 8 + 1 = 9/10 ✓
CPU: 14 + 2 = 16/16 ✓
RAM: 28672 + 4096 = 32768/32768 ✓
Disk: 350 + 20 = 370/500 GB ✓
Result: Clone ALLOWEDQuota Exceeded Example:
{
"detail": "CPU quota exceeded. Need 2 cores, but only 1 available (using 15/16)."
}Overwriting Existing Environment
When overwriting (create_new: false):
- Target environment's resources are freed from quota
- Source environment's resources are allocated
- Net quota change = source resources - target resources
Example:
Target Environment: 4 cores, 8GB RAM
Source Environment: 2 cores, 4GB RAM
Before Clone:
Total: 20 cores, 40 GB RAM
After Clone:
Total: 20 - 4 + 2 = 18 cores
Total: 40 - 8 + 4 = 36 GB RAMPermissions & Access Control
Required Permissions
To Clone an Environment:
org.environments.clone(organization-level)- Access to source environment's organization
- If cross-server: Access to target server's organization
To View Clone Operations:
org.environments.list(organization-level)- Access to environment's organization
To Cancel Clone Operations:
org.environments.clone(organization-level)- Must be member of environment's organization
Permission Validation
The system validates access at multiple levels:
-
Source Environment Access:
- User is member of source environment's organization
- User has
org.environments.clonepermission
-
Target Server Access (if cross-server):
- Target server belongs to same organization
- User has access to target organization
-
Target Environment Access (if overwriting):
- User has access to target environment
- Target environment belongs to same organization
Role Requirements
Minimum Roles:
- Organization Member: Cannot clone
- Project Member: Cannot clone
- Project Admin: Can clone within project
- Organization Admin: Can clone all environments
- Organization Owner: Can clone all environments
- Portal Admin: Can clone all environments
Best Practices
When to Use Each Preset
Recommended
- ✅ Production → Staging: Default choice
- ✅ Staging → Development: If staging has real data
- ✅ Any clone with customer data: GDPR/privacy compliance
- ✅ Shared development environments: Multiple developers
Minimal
- ✅ Staging → Staging: Copy between staging environments
- ✅ Development → Development: Quick duplicates
- ✅ Already sanitized data: Re-cloning from clean source
- ❌ Production → Anything: Too risky without anonymization
Full
- ✅ Production → Development: Maximum protection
- ✅ Regulated industries: Healthcare, finance, legal
- ✅ Training environments: User training with anonymized data
- ✅ External contractors: Sharing with third parties
None
- ✅ Production → DR Server: Disaster recovery replica
- ✅ Isolated secure networks: Air-gapped environments
- ❌ Shared servers: Risk of credential reuse
- ❌ Public networks: Security risk
Data Privacy Considerations
GDPR Compliance:
- Always use Recommended or Full preset when cloning production data
- Document sanitization in your DPIA (Data Protection Impact Assessment)
- Consider right to be forgotten (GDPR Article 17)
PCI DSS Compliance:
- Use Full preset to remove payment card data
- Enable
clear_payment_providersoption - Verify no payment tokens remain after sanitization
HIPAA Compliance:
- Use Full preset with all anonymization options
- Enable address anonymization for healthcare data
- Document sanitization procedures for audits
Testing with Cloned Environments
Before Testing:
- ✅ Verify sanitization completed successfully
- ✅ Check mail servers are disabled
- ✅ Confirm base URL updated to staging domain
- ✅ Test with a known user account
During Testing:
- ✅ Monitor for any production data leaks
- ✅ Check email logs (should be empty)
- ✅ Verify API calls go to staging endpoints
- ✅ Test payment flows with sandbox credentials
After Testing:
- ✅ Review test results
- ✅ Document any issues found
- ✅ Clean up test data if needed
- ✅ Consider destroying clone if no longer needed
Performance Optimization
Faster Clones:
- Use same server instead of cross-server when possible
- Use Minimal preset if data is already sanitized
- Clone during off-peak hours for production
- Ensure sufficient disk space on source server
Disk Space Management:
- Temporary dump files are cleaned up automatically
- Ensure source server has 2x database size in
/tmp - Monitor disk usage during large clones
Network Optimization:
- Use servers in same datacenter for cross-server clones
- Consider compressing filestore before clone
- Schedule large transfers during low-traffic periods
Troubleshooting
Clone Failures
Error: "Source environment not found"
Cause: Environment was deleted or user lacks access
Solution:
- Verify environment exists in database
- Check user is member of environment's organization
- Confirm user has
org.environments.listpermission
Error: "Cannot clone environment in state 'deploying'"
Cause: Source environment must be running or stopped
Solution:
- Wait for environment to finish deploying
- Stop environment if safe to do so
- Check environment status:
GET /api/v1/environments/{id}
Error: "CPU quota exceeded"
Cause: Organization has reached CPU allocation limit
Solution:
- Delete unused environments to free quota
- Reduce resource allocation on existing environments
- Upgrade organization plan for higher quota
- Use overwrite mode to replace existing environment
Error: "pg_dump failed"
Cause: Database dump failed (permissions, disk space, corruption)
Solution:
- Check PostgreSQL container is running:
docker ps | grep _db - Verify disk space on source server:
df -h /tmp - Check PostgreSQL logs:
docker logs {env_id}_db - Test manual dump:
docker exec {env_id}_db pg_dump -U odoo -Fc {db_name} > test.dump
Error: "Failed to copy dump to container"
Cause: Docker container not accessible or disk full
Solution:
- Verify target container is running:
docker inspect {container_name} - Check disk space on target server:
df -h - Restart container if needed:
docker restart {container_name}
Sanitization Issues
Issue: Emails still being sent from cloned environment
Cause: Mail servers not disabled or external SMTP configured
Solution:
- Verify
disable_mail_serverswas enabled - Check
ir_mail_servertable:SELECT id, name, active FROM ir_mail_server; - Manually disable:
UPDATE ir_mail_server SET active = false; - Check for external SMTP in
odoo.conf
Issue: User cannot login after clone
Cause: Password reset enabled or 2FA removed
Solution:
- If
reset_passwordswas enabled, use reset password:staging123 - Check user exists:
SELECT id, login, active FROM res_users WHERE login = 'admin'; - Reset specific user password:
docker exec {container_name} odoo shell -d {db_name} env['res.users'].browse(2).write({'password': 'newpassword'})
Issue: UI broken after clone
Cause: Asset bundles not cleared (should never happen - always forced)
Solution:
- Manually clear assets:
DELETE FROM ir_attachment WHERE name LIKE 'web.assets_%'; - Restart Odoo container:
docker restart {container_name} - Clear browser cache and reload
Cross-Server Transfer Issues
Error: "Failed to transfer dump to target server"
Cause: Network connectivity, SSH access, or firewall
Solution:
- Test SSH connectivity:
ssh user@target-server - Check firewall rules allow SSH (port 22)
- Verify SSH key authentication working
- Check network bandwidth and latency
- Retry transfer with manual SFTP:
sftp user@target-server put /tmp/clone_db_*.dump
Error: "Insufficient storage on target server"
Cause: Not enough disk space for dump/archive
Solution:
- Check target server disk space:
df -h - Clean up old Docker volumes:
docker volume prune - Remove old temporary files:
rm -f /tmp/clone_* - Increase target server disk size
Verification Failures
Error: "Target container is not running"
Cause: Container failed to start or crashed during restore
Solution:
- Check container status:
docker ps -a | grep {container_name} - View container logs:
docker logs {container_name} - Check Odoo configuration in
/opt/paasportal/{env_id}/odoo.conf - Manually start container:
docker start {container_name}
Error: "Cannot connect to database"
Cause: PostgreSQL not ready or database restore failed
Solution:
- Check PostgreSQL container:
docker ps | grep {env_id}_db - Test database connectivity:
docker exec {env_id}_db psql -U odoo -d {db_name} -c 'SELECT 1' - View PostgreSQL logs:
docker logs {env_id}_db - Check network connectivity:
docker network inspect paasportal_net_{env_id}
Error: "Missing Odoo tables"
Cause: Database restore incomplete or corrupted
Solution:
- Check table count:
docker exec {env_id}_db psql -U odoo -d {db_name} -c "SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = 'public'" - Verify key tables:
docker exec {env_id}_db psql -U odoo -d {db_name} -c "\dt res_users ir_module_module" - If tables missing, re-run clone operation
- Check source database integrity before retrying
Advanced Examples
Clone with Custom Sanitization
Clone production to staging with custom sanitization:
curl -X POST "https://api.paasportal.io/api/v1/environments/{env_id}/clone" \
-H "Authorization: Bearer {token}" \
-H "Content-Type: application/json" \
-d '{
"target": {
"create_new": true,
"name": "Staging - Dec 2024",
"environment_type": "staging",
"vm_id": "f47ac10b-58cc-4372-a567-0e02b2c3d479"
},
"sanitization_preset": null,
"sanitization_options": {
"disable_mail_servers": true,
"clear_mail_queue": true,
"anonymize_emails": true,
"anonymize_phones": true,
"anonymize_addresses": true,
"clear_api_keys": true,
"clear_oauth_providers": true,
"clear_payment_providers": true,
"update_base_url": true,
"reset_passwords": true,
"reset_password_value": "Dev2024!",
"disable_crons": true,
"clear_asset_bundles": true
},
"include_filestore": true
}'Overwrite Existing Environment
Replace an existing staging environment with fresh production data:
curl -X POST "https://api.paasportal.io/api/v1/environments/{source_id}/clone" \
-H "Authorization: Bearer {token}" \
-H "Content-Type: application/json" \
-d '{
"target": {
"create_new": false,
"target_environment_id": "87654321-4321-4321-4321-210987654321"
},
"sanitization_preset": "recommended",
"include_filestore": true
}'Cross-Server Clone to Development
Clone to a different server with full sanitization:
curl -X POST "https://api.paasportal.io/api/v1/environments/{prod_id}/clone" \
-H "Authorization: Bearer {token}" \
-H "Content-Type: application/json" \
-d '{
"target": {
"create_new": true,
"name": "Development",
"environment_type": "development",
"vm_id": "c2d3e4f5-g6h7-i8j9-k0l1-m2n3o4p5q6r7",
"domain": "dev.example.com"
},
"sanitization_preset": "full",
"include_filestore": true
}'Monitor Clone Progress
Poll clone operation status:
# Start clone
CLONE_OP_ID=$(curl -X POST "https://api.paasportal.io/api/v1/environments/{env_id}/clone" \
-H "Authorization: Bearer {token}" \
-H "Content-Type: application/json" \
-d '{"target": {"create_new": true, "name": "Clone"}, "sanitization_preset": "recommended"}' \
| jq -r '.id')
# Poll status every 10 seconds
while true; do
STATUS=$(curl -s "https://api.paasportal.io/api/v1/clone-operations/${CLONE_OP_ID}" \
-H "Authorization: Bearer {token}" \
| jq -r '.status')
if [[ "$STATUS" == "completed" ]]; then
echo "Clone completed successfully!"
break
elif [[ "$STATUS" == "failed" ]]; then
echo "Clone failed!"
break
else
echo "Status: $STATUS"
sleep 10
fi
doneGet Sanitization Impact Preview
Preview affected records before cloning:
curl -X GET "https://api.paasportal.io/api/v1/environments/{env_id}/clone/options" \
-H "Authorization: Bearer {token}" \
| jq '.available_options[] | {
name: .name,
impact: .impact_preview,
count: .impact_count
}'Example Output:
{
"name": "Disable outgoing mail servers",
"impact": "3 mail server(s) will be disabled",
"count": 3
}
{
"name": "Clear mail queue",
"impact": "847 pending email(s) will be deleted",
"count": 847
}
{
"name": "Anonymize customer emails",
"impact": "12,453 customer email(s) will be anonymized",
"count": 12453
}Database Schema
CloneOperation Model
| Column | Type | Description |
|---|---|---|
id | UUID | Primary key |
source_environment_id | UUID | Source environment FK (CASCADE) |
target_environment_id | UUID | Target environment FK (SET NULL) |
backup_id | UUID | Backup record FK (SET NULL, optional) |
task_id | UUID | Background task FK (SET NULL, optional) |
status | Enum | Current status (see CloneStatus enum) |
progress_percent | Integer | Progress 0-100 |
current_step | String | Current step name |
sanitization_options | JSON | Enabled sanitization options |
sanitization_result | JSON | Execution results with affected rows |
target_name | String | Target environment name (if create_new) |
target_env_type | String | Target environment type |
target_vm_id | UUID | Target server FK |
target_domain | String | Target domain (optional) |
started_at | Timestamp | Clone start time |
completed_at | Timestamp | Clone completion time |
duration_seconds | Integer | Total duration |
error_message | Text | Error message if failed |
error_step | String | Step where error occurred |
error_details | JSON | Stack trace and error details |
created_by | UUID | User who initiated clone |
create_date | Timestamp | Record creation time |
CloneStatus Enum
class CloneStatus(str, enum.Enum):
PENDING = "pending"
PRE_CHECKS = "pre_checks"
VALIDATING = "validating"
CREATING_TARGET = "creating_target"
COPYING_ADDONS = "copying_addons"
BACKING_UP_DATABASE = "backing_up_database"
BACKING_UP_FILESTORE = "backing_up_filestore"
RESTORING_DATABASE = "restoring_database"
RESTORING_FILESTORE = "restoring_filestore"
SANITIZING = "sanitizing"
VERIFYING = "verifying"
COMPLETED = "completed"
FAILED = "failed"
CANCELLED = "cancelled"SanitizationOptions Schema
class SanitizationOptions(BaseModel):
# Email & Communications
disable_mail_servers: bool = True
clear_mail_queue: bool = True
disable_mail_templates: bool = False
# Customer Data
anonymize_emails: bool = True
anonymize_phones: bool = True
anonymize_addresses: bool = False
anonymize_company_names: bool = False
# Security & Integrations
clear_api_keys: bool = True
update_base_url: bool = True
reset_passwords: bool = False
reset_password_value: str = "staging123"
disable_2fa: bool = False
# Automation
disable_crons: bool = False
clear_scheduled_messages: bool = False
clear_asset_bundles: bool = True # Always TrueRelated Documentation
- Environment Management - Overview of environment features
- Deployment Guide - How to deploy environments
- Backup & Restore - Backup system documentation
- Resource Quotas - Organization quota management
- Permissions Matrix - Access control system
- API Reference - Full API documentation
Summary
Environment cloning with sanitization provides a powerful way to duplicate Odoo environments while protecting sensitive data. Key takeaways:
✅ Four Sanitization Presets: Recommended (default), Minimal, Full, None ✅ 15+ Sanitization Options: Granular control over data masking ✅ Cross-Server Support: Clone between servers in your organization ✅ Real-Time Progress: Track clone status with 10 detailed steps ✅ Automatic Verification: Post-clone integrity checks ✅ Quota Integration: Respects organization resource limits ✅ Production-Ready: Battle-tested sanitization SQL for Odoo databases
For production → staging clones, always use the Recommended preset to prevent data leaks and maintain GDPR compliance.