Skip to content

Backup & Recovery

Backup strategy and disaster recovery procedures for JDX Odoo.

Understanding Odoo Data Storage

Odoo stores data in two separate locations:

┌─────────────────────────────────────────────────────────────┐
│                     PostgreSQL Database                      │
│                                                              │
│  - All users and passwords                                   │
│  - All settings and configurations                           │
│  - Installed modules list                                    │
│  - All business data (invoices, orders, contacts)            │
│  - File references (paths only, not actual files)            │
└─────────────────────────────────────────────────────────────┘
                              +
┌─────────────────────────────────────────────────────────────┐
│                        Filestore                             │
│                                                              │
│  - Attachments (PDFs, images, documents)                     │
│  - Report templates                                          │
│  - Email attachments                                         │
│  - Product images                                            │
└─────────────────────────────────────────────────────────────┘

The database stores file paths, not actual files. Odoo links them at runtime:

Database (ir_attachment table):
┌─────────┬──────────────────┬─────────────────────────┐
│ id      │ name             │ store_fname             │
├─────────┼──────────────────┼─────────────────────────┤
│ 1       │ invoice.pdf      │ ab/ab123456789...       │
│ 2       │ photo.jpg        │ cd/cd987654321...       │
└─────────┴──────────────────┴─────────────────────────┘
Filestore folder:
/var/lib/odoo/.local/share/Odoo/filestore/{database_name}/
    ├── ab/
    │   └── ab123456789...  ← actual PDF file
    └── cd/
        └── cd987654321...  ← actual JPG file

Database name must match filestore folder

Odoo looks for files at: {data_dir}/filestore/{DATABASE_NAME}/{store_fname} If names don't match, attachments won't load.

Filestore Locations

Installation Type Filestore Path
Native (package) /var/lib/odoo/filestore/
Native (source) /var/lib/odoo/.local/share/Odoo/filestore/
Docker /var/lib/odoo/filestore/ (inside container)
Custom Check data_dir in odoo.conf

Finding Your Filestore

# Check odoo.conf for data_dir
sudo cat /etc/odoo/odoo.conf | grep data_dir

# Example output:
# data_dir = /var/lib/odoo/.local/share/Odoo

# Filestore is at: {data_dir}/filestore/{database_name}/
sudo ls -la /var/lib/odoo/.local/share/Odoo/filestore/

# Check size
sudo du -sh /var/lib/odoo/.local/share/Odoo/filestore/

odoo.conf Configuration

Key settings in /etc/odoo/odoo.conf:

[options]
; Database connection (for RDS)
db_host = your-rds-endpoint.amazonaws.com
db_port = 5432
db_user = odoo
db_password = your_password
db_name = production

; Data directory (contains filestore)
data_dir = /var/lib/odoo/.local/share/Odoo

; Addons path
addons_path = /opt/odoo/addons,/opt/odoo/custom-addons

Backup Strategy

What Gets Backed Up

Component Method Frequency Retention
PostgreSQL Database pg_dump Daily 30 days
Odoo Filestore tar archive Daily 30 days
Configuration Git On change Forever

Backup Schedule

Type Schedule Retention
Full backup Daily 2:00 AM 30 days
Database only Every 6 hours 7 days
S3 lifecycle Auto-expire 90 days → Glacier

Backup Scripts

Full Backup

./scripts/backup.sh full

Creates: - odoo_db_YYYYMMDD_HHMMSS.sql.gz - odoo_filestore_YYYYMMDD_HHMMSS.tar.gz

Uploads to S3 bucket.

Database Only

./scripts/backup.sh db

Filestore Only

./scripts/backup.sh files

Manual Backup

# Database
docker compose exec db pg_dump -U odoo odoo_test | gzip > backup.sql.gz

# Filestore
docker compose exec odoo tar -czf - -C /var/lib/odoo filestore > filestore.tar.gz

Restore Procedures

Full Restore

./scripts/restore.sh backup.sql.gz filestore.tar.gz

This will: 1. Stop Odoo service 2. Drop existing database 3. Restore database from backup 4. Restore filestore 5. Restart Odoo

Database Only Restore

./scripts/restore.sh backup.sql.gz

Manual Restore

# Stop Odoo
docker compose stop odoo

# Drop and recreate database
docker compose exec db psql -U odoo -d postgres -c "DROP DATABASE IF EXISTS odoo_test;"
docker compose exec db psql -U odoo -d postgres -c "CREATE DATABASE odoo_test OWNER odoo;"

# Restore
gunzip -c backup.sql.gz | docker compose exec -T db psql -U odoo -d odoo_test

# Start Odoo
docker compose start odoo

Native Odoo Restore (AWS EC2 with RDS)

For native Odoo installations using AWS Lightsail/RDS PostgreSQL:

Migration to New Server

Since database is on RDS (separate from EC2), you only need to:

  1. Restore filestore files
  2. Point odoo.conf to existing RDS
┌─────────────────┐         ┌─────────────────┐
│   New EC2       │         │  Lightsail RDS  │
│                 │ ──────► │   PostgreSQL    │
│  - Odoo app     │         │                 │
│  - Filestore    │         │  - Database     │
│  (restore here) │         │  (already has   │
│                 │         │   your data)    │
└─────────────────┘         └─────────────────┘

Step-by-Step New Server Setup

# 1. Install Odoo (no PostgreSQL needed - using RDS)
sudo apt update
sudo apt install odoo

# 2. Stop Odoo before configuration
sudo systemctl stop odoo

# 3. Configure odoo.conf with RDS connection
sudo nano /etc/odoo/odoo.conf
[options]
; RDS connection
db_host = your-rds-endpoint.lightsail.amazonaws.com
db_port = 5432
db_user = odoo
db_password = your_rds_password
db_name = production

; Data directory
data_dir = /var/lib/odoo/.local/share/Odoo

; Addons
addons_path = /opt/odoo/addons,/opt/odoo/custom-addons
# 4. Create filestore directory
sudo mkdir -p /var/lib/odoo/.local/share/Odoo/filestore

# 5. Restore filestore from backup
sudo tar -xzvf filestore-backup.tar.gz -C /var/lib/odoo/.local/share/Odoo/filestore/

# 6. Fix permissions
sudo chown -R odoo:odoo /var/lib/odoo/.local/share/Odoo

# 7. Start Odoo
sudo systemctl start odoo

# 8. Verify
sudo systemctl status odoo
sudo tail -f /var/log/odoo/odoo.log

Full Restore (Database + Filestore)

If restoring to a completely new RDS as well:

# 1. Install Odoo
sudo apt install odoo

# 2. Stop Odoo
sudo systemctl stop odoo

# 3. Restore database to RDS
# Connect to RDS from EC2
PGPASSWORD=your_password psql -h your-rds-endpoint.amazonaws.com -U odoo -d postgres

# Create database (name must match filestore folder)
CREATE DATABASE production OWNER odoo;
\q

# Restore SQL dump
PGPASSWORD=your_password psql -h your-rds-endpoint.amazonaws.com -U odoo -d production < backup.sql

# 4. Restore filestore (folder name must match database name)
sudo tar -xzvf filestore-backup.tar.gz -C /var/lib/odoo/.local/share/Odoo/filestore/

# Verify folder name matches
ls /var/lib/odoo/.local/share/Odoo/filestore/
# Should show: production

# 5. Fix permissions
sudo chown -R odoo:odoo /var/lib/odoo/.local/share/Odoo

# 6. Configure odoo.conf (see above)

# 7. Start Odoo
sudo systemctl start odoo

Backup Commands for Native Odoo

# Backup database from RDS
PGPASSWORD=your_password pg_dump -h your-rds-endpoint.amazonaws.com -U odoo production > backup-$(date +%Y%m%d).sql

# Backup filestore
sudo tar -czvf filestore-$(date +%Y%m%d).tar.gz -C /var/lib/odoo/.local/share/Odoo/filestore production

# Upload to S3
aws s3 cp backup-$(date +%Y%m%d).sql s3://your-bucket/backups/
aws s3 cp filestore-$(date +%Y%m%d).tar.gz s3://your-bucket/backups/

Sync Filestore to S3 (Incremental)

For ongoing filestore backup:

# Sync only changed files (efficient)
aws s3 sync /var/lib/odoo/.local/share/Odoo/filestore/production s3://your-bucket/filestore/

# Add to cron for weekly backup
crontab -e
# Add: 0 3 * * 0 aws s3 sync /var/lib/odoo/.local/share/Odoo/filestore/production s3://your-bucket/filestore/

S3 Backup Configuration

Bucket Setup

# Create bucket
aws s3 mb s3://jdx-odoo-backups --region us-east-1

# Enable versioning
aws s3api put-bucket-versioning \
  --bucket jdx-odoo-backups \
  --versioning-configuration Status=Enabled

# Enable encryption
aws s3api put-bucket-encryption \
  --bucket jdx-odoo-backups \
  --server-side-encryption-configuration '{
    "Rules": [{"ApplyServerSideEncryptionByDefault": {"SSEAlgorithm": "AES256"}}]
  }'

Lifecycle Policy

{
  "Rules": [
    {
      "ID": "BackupRetention",
      "Status": "Enabled",
      "Filter": {"Prefix": "backups/"},
      "Transitions": [
        {"Days": 30, "StorageClass": "STANDARD_IA"},
        {"Days": 90, "StorageClass": "GLACIER"}
      ],
      "Expiration": {"Days": 365}
    }
  ]
}

S3 Cost Estimation

Storage Calculation

Based on typical Odoo installation:

Backup Type Size Frequency Retention Total Storage
Full backup (DB+files) ~900MB Daily 30 days 27 GB
Database only ~100MB 4x/day 7 days 2.8 GB
Total S3 Standard ~30 GB

AWS S3 Pricing (US East)

Storage Class Price/GB/Month 30GB Monthly Cost
S3 Standard $0.023 $0.69
S3 Standard-IA $0.0125 $0.38
S3 Glacier $0.004 $0.12

Monthly Cost Breakdown

Item Cost
S3 Standard (recent 30 days) $0.69
S3 Standard-IA (30-90 days old) $0.38
S3 Glacier (90-365 days old) $0.12
PUT requests (~150/month) $0.001
Total Monthly ~$1.20

Annual Cost

Period Storage Mix Est. Cost
Year 1 (building up) Mixed tiers ~$15-20
Year 2+ (steady state) Mostly Glacier ~$12-15

Cost-Effective Backup

Full disaster recovery coverage for approximately $1.20/month makes S3 backup extremely cost-effective compared to alternatives.

Disaster Recovery

Recovery Time Objectives

Scenario RTO RPO
Service restart 5 min 0
Container failure 10 min 0
Database corruption 30 min 6 hours
Complete server failure 2 hours 24 hours
Region failure 4 hours 24 hours

DR Scenarios

Scenario 1: Container Failure

# Restart failed container
docker compose restart odoo

# Check logs
docker compose logs odoo

Scenario 2: Database Corruption

# 1. Stop services
docker compose stop odoo pwa

# 2. Identify latest backup
aws s3 ls s3://jdx-odoo-backups/backups/ --recursive | tail -5

# 3. Download backup
aws s3 cp s3://jdx-odoo-backups/backups/odoo_db_latest.sql.gz .

# 4. Restore
./scripts/restore.sh odoo_db_latest.sql.gz

Scenario 3: Complete Server Failure

# 1. Launch new server with same specs
# 2. Install Docker
# 3. Clone repository
git clone git@github.com:yourorg/odoo15-production.git
cd odoo15-production

# 4. Configure environment
cp .env.example .env
# Edit .env with production settings:
# - Change COMPOSE_FILE to: docker-compose.yml:docker-compose.prod.yml
# - Set RDS connection details
# - Set production passwords

# 5. Download latest backup
aws s3 cp s3://jdx-odoo-backups/backups/odoo_db_latest.sql.gz .
aws s3 cp s3://jdx-odoo-backups/backups/odoo_filestore_latest.tar.gz .

# 6. Start services (COMPOSE_FILE in .env auto-loads both files)
docker compose up -d db
sleep 10

# 7. Restore data
./scripts/restore.sh odoo_db_latest.sql.gz odoo_filestore_latest.tar.gz

# 8. Start remaining services
docker compose up -d

# 9. Update DNS to new server IP

Testing Backups

Monthly DR Test

  1. Spin up test environment
  2. Download production backup
  3. Restore to test environment
  4. Verify:
  5. Login works
  6. Recent data present
  7. File attachments accessible
  8. Custom modules function
  9. Document results

Test Script

#!/bin/bash
# DR Test Script

echo "Starting DR test..."

# Create test environment (COMPOSE_FILE in .env auto-loads both files)
docker compose up -d db
sleep 10

# Restore latest backup
./scripts/restore.sh /tmp/latest-backup.sql.gz

# Run health check
./scripts/health-check.sh

echo "DR test complete. Verify manually at http://localhost:8016"

Backup Verification

Daily Checks

  • Backup job completed
  • Files uploaded to S3
  • No errors in logs

Weekly Checks

  • Verify backup file sizes are reasonable
  • Test download speed from S3
  • Check S3 storage costs

Monthly Checks

  • Full restore test
  • Verify all data integrity
  • Update DR documentation