Getting Started
Configure Storage Provider

Configure Storage Provider

Configure cloud storage for secure backup management across your environments.


Overview

Storage configuration is a prerequisite for creating backups. OEC.SH supports 6 cloud storage providers, allowing you to choose based on cost, performance, and compliance requirements.


Why Storage Configuration is Required

Before you can create backups, you must configure at least one cloud storage provider:

  • Backups Require Storage: All environment backups (database + filestore) are stored in cloud storage
  • Cannot Create Backups Without Config: The backup creation endpoint will fail if no storage is configured
  • Multi-Cloud Support: Use different storage providers for different organizations or backup policies
  • BYOS (Bring Your Own Storage): You control your backup data - we never lock you into our storage

Storage Architecture

Environment Backup
    ├─ Database Dump (PostgreSQL .sql)
    ├─ Filestore Archive (.tar.gz)
    └─ Manifest (metadata.json)

    Cloud Storage Provider
    (S3, R2, B2, MinIO, FTP, SFTP)

Quick Start: Cloudflare R2 (Recommended)

Why Cloudflare R2?

  • Zero Egress Fees: Download backups without bandwidth charges
  • S3-Compatible: Drop-in replacement for AWS S3
  • Affordable: $0.015/GB/month storage (50% cheaper than S3)
  • Global Network: Cloudflare's edge network ensures fast uploads/downloads
  • Built-in Security: Automatic encryption at rest

Step 1: Create Cloudflare R2 Bucket

  1. Log in to Cloudflare Dashboard

  2. Create R2 Bucket

    • Go to R2 in the left sidebar
    • Click "Create Bucket"
    • Enter bucket name (e.g., oecsh-backups)
    • Choose location hint (optional, R2 auto-distributes)
    • Click "Create Bucket"
  3. Note Your Account ID

    • Find your Account ID in the R2 overview page
    • Format: 32 character hexadecimal string (e.g., a1b2c3d4e5f6...)

Step 2: Generate R2 API Token

  1. Create API Token

    • In R2 dashboard, click "Manage R2 API Tokens"
    • Click "Create API Token"
    • Enter token name: OEC.SH Backups
    • Permissions:
      • Object Read & Write
      • Admin Read & Write (if you want lifecycle management)
    • Click "Create API Token"
  2. Save Credentials

    Access Key ID: YOUR_ACCESS_KEY_ID_HERE
    Secret Access Key: YOUR_SECRET_ACCESS_KEY_HERE

    ⚠️ Important: Copy these credentials immediately - you won't see them again!

Step 3: Configure in OEC.SH

  1. Navigate to Storage Settings

    • Go to DashboardSettings
    • Click "Storage" tab
    • Click "Add Storage Provider"
  2. Fill R2 Configuration

    Name:               Cloudflare R2 Production
    Provider:           Cloudflare R2
    Bucket Name:        oecsh-backups
    Account ID:         YOUR_ACCOUNT_ID_HERE
    Access Key:         YOUR_ACCESS_KEY_ID_HERE
    Secret Key:         YOUR_SECRET_ACCESS_KEY_HERE
    Path Prefix:        backups/  (optional, organizes your buckets)
    Set as Default:     ✅ Yes
  3. Test Connection

    • Click "Test Connection" button
    • Wait for connection verification
    • Success: "Connection test successful"
    • Failed: See Troubleshooting section
  4. Save Configuration

    • Click "Save Storage Config"
    • Configuration is now available for backups

Setup Complete! 🎉 You can now create backups. Proceed to Create Your First Project.


Storage Provider Comparison

Choose the right provider for your needs:

ProviderBest ForCostEgress FeesSetup ComplexityNotes
Cloudflare R2Most users$0.015/GB/mo$0⭐ EasyZero egress, S3-compatible, recommended
AWS S3Enterprise$0.023/GB/mo$0.09/GB⭐ EasyIndustry standard, multi-region
Backblaze B2Archival$0.005/GB/mo$0.01/GB⭐⭐ ModerateLowest storage cost, good for long-term
MinIOSelf-hostedInfrastructure cost$0⭐⭐⭐ AdvancedGDPR compliance, full control
SFTPLegacy systemsInfrastructure cost$0⭐⭐ ModerateUse existing SFTP servers
FTPLegacy systemsInfrastructure cost$0⭐⭐ ModerateLess secure than SFTP

Feature Comparison

FeatureR2S3B2MinIOSFTPFTP
S3-Compatible API
Encryption at Rest⚠️ Manual⚠️ Manual
Versioning
Lifecycle Policies
Multi-Region✅ Auto✅ Manual⚠️ DIYN/AN/A
Zero Egress
Compliance (GDPR)

Test Storage Connection

Testing your storage connection ensures backups will succeed before you create them.

UI Method

  1. Open Storage Config

    • Navigate to DashboardSettingsStorage
    • Find your storage configuration in the list
  2. Click Test Connection

    • Click the "Test Connection" button next to your config
    • Wait for the test to complete (2-5 seconds)
  3. View Results

    • ✅ Success: Connection verified

      ✓ Connection test successful
      ✓ Bucket exists
      ✓ Can write to bucket
      ✓ Can read from bucket
      ✓ Latency: 145ms
    • ❌ Failed: Connection test failed

      ✗ Connection failed: Invalid credentials

API Method

Test connection before saving credentials:

curl -X POST https://api.oec.sh/api/v1/backups/storage-configs/test-connection?organization_id=YOUR_ORG_ID \
  -H "Authorization: Bearer YOUR_JWT_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Test R2 Config",
    "provider": "cloudflare_r2",
    "bucket": "oecsh-backups",
    "account_id": "YOUR_ACCOUNT_ID",
    "access_key": "YOUR_ACCESS_KEY",
    "secret_key": "YOUR_SECRET_KEY",
    "path_prefix": "backups/"
  }'

Response (Success):

{
  "success": true,
  "message": "Connection successful",
  "bucket_exists": true,
  "can_write": true,
  "can_read": true,
  "latency_ms": 145
}

Response (Failed):

{
  "success": false,
  "message": "Connection failed: The AWS Access Key Id you provided does not exist in our records.",
  "bucket_exists": null,
  "can_write": null,
  "can_read": null,
  "latency_ms": null
}

What the Test Checks

  1. Authentication: Verifies access key and secret key are valid
  2. Bucket Exists: Confirms the bucket name is correct
  3. Write Permission: Uploads a test file (test-connection-{timestamp}.txt)
  4. Read Permission: Downloads the test file
  5. Cleanup: Deletes the test file
  6. Latency: Measures round-trip time to storage

Set as Default Storage

The default storage is automatically used when creating backups without specifying a storage configuration.

Why Set a Default?

  • Convenience: No need to select storage for every backup
  • Automated Backups: Backup policies use the default storage
  • Safety Net: Prevents backup failures due to missing storage config

Setting Default Storage

UI Method:

  1. Go to SettingsStorage
  2. Click "Set as Default" next to your preferred storage config
  3. Confirm the action
  4. ✅ The config is now marked with a "Default" badge

API Method:

curl -X PATCH https://api.oec.sh/api/v1/backups/storage-configs/{config_id} \
  -H "Authorization: Bearer YOUR_JWT_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "is_default": true
  }'

Note: Only one storage config can be default per organization. Setting a new default automatically unsets the previous one.


Complete Provider Guides

For detailed setup instructions for each provider, see:


API Reference

Create Storage Configuration

Endpoint: POST /api/v1/backups/storage-configs Permission: org.storage.create

Request:

curl -X POST https://api.oec.sh/api/v1/backups/storage-configs?organization_id=YOUR_ORG_ID \
  -H "Authorization: Bearer YOUR_JWT_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Production Backups",
    "provider": "cloudflare_r2",
    "bucket": "oecsh-backups",
    "account_id": "YOUR_ACCOUNT_ID_HERE",
    "access_key": "YOUR_ACCESS_KEY_ID_HERE",
    "secret_key": "YOUR_SECRET_ACCESS_KEY_HERE",
    "path_prefix": "backups/",
    "is_default": true
  }'

Response (201 Created):

{
  "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "organization_id": "org-uuid",
  "name": "Production Backups",
  "provider": "cloudflare_r2",
  "bucket": "oecsh-backups",
  "region": "auto",
  "endpoint_url": null,
  "path_prefix": "backups/",
  "storage_class": null,
  "is_default": true,
  "is_active": true,
  "total_size_bytes": 0,
  "object_count": 0,
  "last_used_at": null,
  "created_at": "2024-12-11T10:30:00Z",
  "updated_at": "2024-12-11T10:30:00Z"
}

Note: Access key and secret key are encrypted at rest and never returned in API responses.

List Storage Configurations

Endpoint: GET /api/v1/backups/storage-configs?organization_id={org_id} Permission: org.storage.list

curl -X GET https://api.oec.sh/api/v1/backups/storage-configs?organization_id=YOUR_ORG_ID \
  -H "Authorization: Bearer YOUR_JWT_TOKEN"

Response:

[
  {
    "id": "config-1-uuid",
    "name": "Production Backups",
    "provider": "cloudflare_r2",
    "bucket": "oecsh-backups",
    "is_default": true,
    "is_active": true,
    "total_size_bytes": 5368709120,
    "object_count": 42,
    "last_used_at": "2024-12-11T08:00:00Z",
    "created_at": "2024-11-01T12:00:00Z",
    "updated_at": "2024-12-11T08:00:00Z"
  },
  {
    "id": "config-2-uuid",
    "name": "Archive Storage",
    "provider": "backblaze_b2",
    "bucket": "oecsh-archive",
    "is_default": false,
    "is_active": true,
    "total_size_bytes": 10737418240,
    "object_count": 156,
    "last_used_at": "2024-12-10T23:00:00Z",
    "created_at": "2024-11-15T14:30:00Z",
    "updated_at": "2024-12-10T23:00:00Z"
  }
]

Update Storage Configuration

Endpoint: PATCH /api/v1/backups/storage-configs/{config_id} Permission: org.storage.update

curl -X PATCH https://api.oec.sh/api/v1/backups/storage-configs/config-uuid \
  -H "Authorization: Bearer YOUR_JWT_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Updated Name",
    "is_default": true
  }'

Delete Storage Configuration

Endpoint: DELETE /api/v1/backups/storage-configs/{config_id} Permission: org.storage.delete

curl -X DELETE https://api.oec.sh/api/v1/backups/storage-configs/config-uuid \
  -H "Authorization: Bearer YOUR_JWT_TOKEN"

Important: You cannot delete a storage configuration that has backups. You must:

  1. Delete all backups using this config, OR
  2. Migrate backups to another storage config

Error Response (400 Bad Request):

{
  "detail": "Cannot delete storage config with 42 backups. Delete or migrate backups first."
}

Test Connection (Before Saving)

Endpoint: POST /api/v1/backups/storage-configs/test-connection?organization_id={org_id} Permission: org.storage.create

This endpoint lets you test credentials before creating the configuration.

curl -X POST https://api.oec.sh/api/v1/backups/storage-configs/test-connection?organization_id=YOUR_ORG_ID \
  -H "Authorization: Bearer YOUR_JWT_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Test Config",
    "provider": "cloudflare_r2",
    "bucket": "test-bucket",
    "account_id": "account-id",
    "access_key": "access-key",
    "secret_key": "secret-key"
  }'

Test Existing Configuration

Endpoint: POST /api/v1/backups/storage-configs/{config_id}/test Permission: org.storage.list (read-only)

curl -X POST https://api.oec.sh/api/v1/backups/storage-configs/config-uuid/test \
  -H "Authorization: Bearer YOUR_JWT_TOKEN"

Storage Platform Settings

Platform administrators can control which storage providers are available to organizations.

Check Enabled Providers

Endpoint: GET /api/v1/backups/storage-settings

curl -X GET https://api.oec.sh/api/v1/backups/storage-settings \
  -H "Authorization: Bearer YOUR_JWT_TOKEN"

Response:

{
  "enabled_providers": [
    "aws_s3",
    "cloudflare_r2",
    "backblaze_b2",
    "minio",
    "sftp",
    "ftp"
  ],
  "allow_org_byos": true,
  "has_default_storage": false
}

Fields:

  • enabled_providers: List of storage providers available on this platform
  • allow_org_byos: Whether organizations can configure their own storage (BYOS)
  • has_default_storage: Whether platform has default storage configured

Error Response (403 Forbidden):

{
  "detail": "Organization storage configuration is not enabled on this platform. Contact your platform administrator."
}

Next Steps

Now that you have storage configured, you can:

  1. Create Your First Project - Set up your Odoo project
  2. Create Manual Backups - Create on-demand backups (Sprint 2E44 - BAK-001)
  3. Setup Backup Policies - Automate backups with GFS retention (Sprint 2E46 - BAK-003)
  4. Restore from Backup - Disaster recovery (ENV-003)

Troubleshooting

Connection Test Failed

Symptom: Test connection returns success: false

Common Causes:

  1. Invalid Credentials

    Error: The AWS Access Key Id you provided does not exist in our records.

    Solution:

    • Verify access key and secret key are correct
    • Check for extra spaces or special characters
    • Regenerate credentials if needed
  2. Bucket Not Found

    Error: The specified bucket does not exist

    Solution:

    • Verify bucket name spelling
    • Check bucket exists in provider dashboard
    • Ensure bucket is in the correct region/account
  3. Permission Denied

    Error: Access Denied

    Solution:

    • Verify API token has Read & Write permissions
    • For AWS S3: Check IAM policy includes s3:GetObject, s3:PutObject, s3:DeleteObject
    • For R2: Token must have "Object Read & Write" enabled
  4. Network/Firewall Issues

    Error: Connection timeout

    Solution:

    • Check OEC.SH server can reach storage provider
    • Verify no firewall blocking outbound HTTPS (port 443)
    • For SFTP/FTP: Check port is open (22 for SFTP, 21 for FTP)
  5. Incorrect Endpoint URL

    Error: Could not connect to the endpoint URL

    Solution:

    • For Cloudflare R2: Ensure account ID is correct (auto-generates endpoint)
    • For Backblaze B2: Verify endpoint matches bucket region (e.g., s3.us-west-004.backblazeb2.com)
    • For MinIO: Ensure endpoint includes protocol and port (e.g., http://minio.example.com:9000)

Cannot Create Storage Config

Symptom: API returns 403 Forbidden

Cause: Platform has disabled organization BYOS (Bring Your Own Storage)

Solution:

  • Contact platform administrator to enable ALLOW_ORG_BYOS=true
  • Use platform-provided default storage (if available)

Cannot Delete Storage Config

Symptom: API returns 400 Bad Request with backup count

Solution:

  1. Option 1: Delete All Backups

    # List backups using this storage
    curl -X GET https://api.oec.sh/api/v1/backups/organization/YOUR_ORG_ID/backups \
      -H "Authorization: Bearer YOUR_JWT_TOKEN"
     
    # Delete each backup
    curl -X DELETE https://api.oec.sh/api/v1/backups/{backup_id} \
      -H "Authorization: Bearer YOUR_JWT_TOKEN"
  2. Option 2: Migrate Backups (Future Feature - Sprint 2E44)

    • Create new storage config
    • Use backup migration tool to move backups
    • Delete old storage config

Storage Provider Errors

AWS S3

Error: The authorization header is malformed; the region 'us-east-1' is wrong; expecting 'eu-west-1'

Solution: Update region field to match bucket's region

Cloudflare R2

Error: Account ID is required for Cloudflare R2

Solution: Provide account_id field (32-character hex string)

Backblaze B2

Error: Invalid application key

Solution:

  • Use Application Key ID as access_key
  • Use Application Key as secret_key
  • Ensure key has access to the bucket

MinIO

Error: SSL: CERTIFICATE_VERIFY_FAILED

Solution:

  • For self-signed certificates, update MinIO client config
  • For production, use valid SSL certificates
  • Ensure endpoint URL includes protocol (http:// or https://)

SFTP/FTP

Error: Authentication failed (publickey,password)

Solution:

  • For SFTP: Use username as access_key, password as secret_key
  • Verify SSH server allows password authentication
  • For key-based auth, contact support (Sprint 2E44 feature)
Error: Connection refused

Solution:

  • Verify SFTP/FTP server is running
  • Check port is correct (22 for SFTP, 21 for FTP)
  • Ensure firewall allows connections from OEC.SH servers

Slow Upload/Download Performance

Symptom: Backups taking longer than expected

Solutions:

  1. Check Latency: Use test connection to measure latency
  2. Choose Closer Region: Select storage region closest to your OEC.SH server
  3. Use Cloudflare R2: Global edge network provides fast uploads worldwide
  4. Network Bandwidth: Verify OEC.SH server has sufficient bandwidth

Storage Costs Higher Than Expected

Solutions:

  1. Enable Lifecycle Policies: Auto-delete old backups (Sprint 2E44)
  2. Use Cheaper Storage: Backblaze B2 for archival ($0.005/GB/mo)
  3. Optimize Retention: Reduce daily/weekly/monthly retention periods
  4. Compress Backups: Already enabled by default (gzip compression)

Security Best Practices

Credential Management

  1. Use Dedicated Tokens: Create storage API tokens specifically for OEC.SH
  2. Least Privilege: Grant only required permissions (Read, Write, Delete)
  3. Rotate Regularly: Change access keys every 90 days
  4. Never Commit: Don't store credentials in Git repositories

Bucket Security

  1. Private Buckets: Never make backup buckets publicly accessible
  2. Encryption at Rest: Enable provider's encryption (most enable by default)
  3. Versioning: Enable bucket versioning for disaster recovery
  4. Lifecycle Policies: Auto-delete old backups after retention period

Access Control

  1. IP Restrictions: Limit bucket access to OEC.SH server IPs (if provider supports)
  2. MFA: Enable multi-factor authentication on cloud provider accounts
  3. Audit Logs: Enable access logging on buckets
  4. Role Separation: Use different storage configs for dev/staging/production

Storage Usage Tracking

Each storage configuration tracks usage metrics:

  • Total Size: total_size_bytes - Sum of all backup sizes
  • Object Count: object_count - Number of backups stored
  • Last Used: last_used_at - Timestamp of most recent backup

View usage in the Storage settings page or via API:

curl -X GET https://api.oec.sh/api/v1/backups/storage-configs?organization_id=YOUR_ORG_ID \
  -H "Authorization: Bearer YOUR_JWT_TOKEN"

Response Snippet:

{
  "total_size_bytes": 5368709120,  // 5 GB
  "object_count": 42,
  "last_used_at": "2024-12-11T08:00:00Z"
}

Related Documentation


Documentation Version: 1.0 Last Updated: December 11, 2024 Sprint: Phase 1 - Sprint 2E42 Contributors: PaaSPortal Documentation Team