Scaling for Multiple Companies¶
Guide for deploying JDX Odoo to multiple companies/clients.
Deployment Strategies¶
Option 1: Multi-Company (Single Instance)¶
Use Odoo's built-in multi-company feature. Single deployment serves multiple companies.
┌─────────────────────────────────────────────┐
│ Single Odoo Instance │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │Company A│ │Company B│ │Company C│ │
│ └─────────┘ └─────────┘ └─────────┘ │
│ │ │
│ ┌─────┴─────┐ │
│ │ Shared DB │ │
│ └───────────┘ │
└─────────────────────────────────────────────┘
Pros: - Single deployment to maintain - Shared infrastructure costs - Easy user access across companies
Cons: - Data isolation concerns - Single point of failure - Resource contention
Best for: Related companies, franchises, holding companies
Option 2: Separate Instances (Recommended for SaaS)¶
Each company gets their own isolated deployment.
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ Company A │ │ Company B │ │ Company C │
│ ┌────────┐ │ │ ┌────────┐ │ │ ┌────────┐ │
│ │ Odoo │ │ │ │ Odoo │ │ │ │ Odoo │ │
│ │ PWA │ │ │ │ PWA │ │ │ │ PWA │ │
│ │ Docs │ │ │ │ Docs │ │ │ │ Docs │ │
│ └────────┘ │ │ └────────┘ │ │ └────────┘ │
│ ┌────────┐ │ │ ┌────────┐ │ │ ┌────────┐ │
│ │ DB │ │ │ │ DB │ │ │ │ DB │ │
│ └────────┘ │ │ └────────┘ │ │ └────────┘ │
└──────────────┘ └──────────────┘ └──────────────┘
Pros: - Complete data isolation - Independent scaling - Custom configurations per client - No cross-company risk
Cons: - More infrastructure to manage - Higher costs per client - Updates applied individually
Best for: SaaS offering, independent clients, compliance requirements
Quick Setup for New Company¶
Step 1: Clone Repository¶
# Create company directory
mkdir -p /opt/clients/company-name
cd /opt/clients/company-name
# Clone from template
git clone git@github.com:yourorg/odoo15-production.git .
Step 2: Configure Environment¶
# Copy and customize environment
cp .env.example .env
# Edit with company-specific settings
nano .env
Key variables to customize:
# Docker Compose - use production overlay
COMPOSE_FILE=docker-compose.yml:docker-compose.prod.yml
# Database
DB_NAME=odoo_companyname
DB_HOST=companyname-db.xxxxx.us-east-1.rds.amazonaws.com
DB_PASSWORD=<unique-strong-password>
# Domains
DOMAIN_ERP=erp.companyname.com
DOMAIN_PWA=field.companyname.com
DOMAIN_DOCS=docs.companyname.com
# Odoo
ODOO_ADMIN_PASSWORD=<unique-admin-password>
# Integrations (company-specific)
JUSTCALL_API_KEY=<company-justcall-key>
XERO_CLIENT_ID=<company-xero-id>
Step 3: Initialize Database¶
# Start database (COMPOSE_FILE in .env auto-loads both files)
docker compose up -d db
sleep 10
# Initialize Odoo
docker compose exec odoo odoo -i base --stop-after-init -d odoo_companyname
Step 4: Configure Company Data¶
# Configure company
company = env['res.company'].browse(1)
company.write({
'name': 'Company Name LLC',
'email': 'info@companyname.com',
'phone': '+1-555-123-4567',
'street': '123 Main St',
'city': 'Austin',
'state_id': env.ref('base.state_us_48').id, # Texas
'country_id': env.ref('base.us').id,
'zip': '78701',
})
# Configure JustCall lines
lines = [
{'name': 'Sales', 'phone': '+1-555-111-1111', 'line_type': 'main', 'is_default': True},
{'name': 'Support', 'phone': '+1-555-222-2222', 'line_type': 'cs'},
]
for line in lines:
env['justcall.line'].create(line)
env.cr.commit()
Step 5: Start Services¶
Step 6: Configure SSL¶
Configuration Templates¶
Company Configuration Script¶
Create scripts/setup-company.sh:
#!/bin/bash
# Usage: ./setup-company.sh <company-slug> <company-name> <admin-email>
COMPANY_SLUG=$1
COMPANY_NAME=$2
ADMIN_EMAIL=$3
if [ -z "$COMPANY_SLUG" ]; then
echo "Usage: $0 <company-slug> <company-name> <admin-email>"
exit 1
fi
echo "Setting up: $COMPANY_NAME ($COMPANY_SLUG)"
# Generate passwords
DB_PASS=$(openssl rand -base64 32)
ADMIN_PASS=$(openssl rand -base64 16)
# Create .env
cat > .env << EOF
# $COMPANY_NAME Configuration
# Generated: $(date)
# Docker Compose - use production overlay
COMPOSE_FILE=docker-compose.yml:docker-compose.prod.yml
DB_NAME=odoo_${COMPANY_SLUG}
DB_USER=odoo
DB_PASSWORD=${DB_PASS}
DB_HOST=db
ODOO_ADMIN_PASSWORD=${ADMIN_PASS}
DOMAIN_ERP=erp.${COMPANY_SLUG}.com
DOMAIN_PWA=field.${COMPANY_SLUG}.com
DOMAIN_DOCS=docs.${COMPANY_SLUG}.com
TZ=America/Chicago
EOF
echo "Environment file created: .env"
echo "Admin password: $ADMIN_PASS"
echo ""
echo "Next steps:"
echo "1. Configure DNS for *.${COMPANY_SLUG}.com"
echo "2. Run: docker compose up -d"
echo "3. Configure SSL certificates"
JustCall Configuration Template¶
Create scripts/setup-justcall.py:
#!/usr/bin/env python3
"""
Configure JustCall lines for a company.
Usage: docker compose exec odoo python3 /scripts/setup-justcall.py
"""
import os
# Configuration - customize per company
COMPANY_LINES = [
{
'name': os.environ.get('JUSTCALL_LINE1_NAME', 'Main'),
'phone': os.environ.get('JUSTCALL_LINE1_PHONE', '+1-555-000-0001'),
'line_type': 'main',
'is_default': True,
},
{
'name': os.environ.get('JUSTCALL_LINE2_NAME', 'Support'),
'phone': os.environ.get('JUSTCALL_LINE2_PHONE', '+1-555-000-0002'),
'line_type': 'cs',
'is_default': False,
},
]
def setup_lines(env):
"""Create or update JustCall lines."""
JustCallLine = env['justcall.line']
for line_data in COMPANY_LINES:
existing = JustCallLine.search([('phone', '=', line_data['phone'])])
if existing:
existing.write(line_data)
print(f"Updated: {line_data['name']}")
else:
JustCallLine.create(line_data)
print(f"Created: {line_data['name']}")
env.cr.commit()
print("JustCall configuration complete!")
if __name__ == '__main__':
# When run in Odoo shell context
setup_lines(env)
Multi-Tenant Architecture¶
For true SaaS with many clients, consider:
Kubernetes Deployment¶
# Per-tenant namespace
apiVersion: v1
kind: Namespace
metadata:
name: tenant-companyname
---
# Odoo deployment per tenant
apiVersion: apps/v1
kind: Deployment
metadata:
name: odoo
namespace: tenant-companyname
spec:
replicas: 1
template:
spec:
containers:
- name: odoo
image: odoo:15
envFrom:
- secretRef:
name: odoo-secrets
Terraform for Infrastructure¶
# modules/client/main.tf
variable "company_slug" {}
variable "company_name" {}
resource "aws_db_instance" "odoo" {
identifier = "odoo-${var.company_slug}"
engine = "postgres"
# ...
}
resource "aws_route53_record" "erp" {
name = "erp.${var.company_slug}"
# ...
}
Checklist: New Company Onboarding¶
- Create infrastructure (server, database, DNS)
- Clone repository
- Configure environment variables
- Initialize database
- Configure company details in Odoo
- Set up JustCall phone lines
- Configure Xero integration (if needed)
- Set up SSL certificates
- Create admin user account
- Test all services
- Configure backups
- Document client-specific settings
- Hand off to client
Maintenance¶
Updating All Clients¶
#!/bin/bash
# update-all-clients.sh
CLIENTS_DIR="/opt/clients"
for client in "$CLIENTS_DIR"/*/; do
echo "Updating: $client"
cd "$client"
git pull origin main
docker compose pull
docker compose up -d --build
echo "---"
done
Backup All Clients¶
#!/bin/bash
# backup-all-clients.sh
CLIENTS_DIR="/opt/clients"
BACKUP_BUCKET="s3://company-backups"
for client in "$CLIENTS_DIR"/*/; do
client_name=$(basename "$client")
echo "Backing up: $client_name"
cd "$client"
./scripts/backup.sh full
aws s3 sync /tmp/odoo-backups/ "$BACKUP_BUCKET/$client_name/"
done