Architecture Overview
System architecture and design documentation for the Odoo 15 ERP platform.
System Overview
flowchart TB
subgraph Internet
Users[Users/Browsers]
Mobile[Mobile Devices]
end
subgraph "Nginx Reverse Proxy :80/:443"
SSL[SSL Termination<br/>TLS 1.2/1.3]
end
subgraph "Docker Services"
Odoo[Odoo 15 ERP<br/>:8069 internal<br/>:8016 external]
Landing[Landing Page<br/>Flask :8001]
PWA[Field PWA<br/>Flask :8000]
Docs[Documentation<br/>MkDocs :8002]
end
subgraph "Data Layer"
DB[(PostgreSQL<br/>:5432<br/>Local/AWS RDS)]
FileStore[(Filestore<br/>./odoo-data)]
S3[(AWS S3<br/>Signatures/Photos)]
end
subgraph "External Services"
JustCall[JustCall API<br/>SMS/MMS]
Xero[Xero API<br/>Accounting]
reCAPTCHA[Google reCAPTCHA]
GChat[Google Chat<br/>Notifications]
end
Users --> SSL
Mobile --> SSL
SSL -->|erp.domain| Odoo
SSL -->|www.domain| Landing
SSL -->|field.domain| PWA
SSL -->|docs.domain| Docs
Odoo --> DB
Odoo --> FileStore
Odoo --> S3
Odoo <--> JustCall
Odoo <--> Xero
Odoo --> GChat
Landing --> Odoo
Landing --> reCAPTCHA
PWA --> Odoo
PWA --> S3
Port Mapping
| Service |
Internal Port |
External Port (Test) |
Production |
| Odoo |
8069 |
8016 |
Via nginx only |
| Landing Page |
8001 |
8001 |
Via nginx only |
| Field PWA |
8000 |
8000 |
Via nginx only |
| Documentation |
8002 |
8002 |
Via nginx only |
| Nginx |
80, 443 |
80, 443 |
80, 443 |
| PostgreSQL |
5432 |
5432 (test only) |
AWS RDS |
Technology Stack
Backend
| Component |
Technology |
Version |
Purpose |
| ERP |
Odoo |
15.0 CE |
Core business logic |
| Database |
PostgreSQL |
15 |
Data persistence (Local or AWS RDS) |
| Web Server |
Werkzeug |
Built-in |
Odoo HTTP server |
| PWA Backend |
Flask + Gunicorn |
3.0+ |
Field PWA application |
| Landing Backend |
Flask + Gunicorn |
3.0+ |
Public website |
Frontend
| Component |
Technology |
Version |
Purpose |
| ERP UI |
Odoo Web Client |
15.0 |
Desktop interface |
| PWA |
Bootstrap 5 + JS |
ES6+ |
Mobile field app |
| Landing Page |
Tailwind CSS |
3.x |
Public website styling |
| JS Framework |
Alpine.js |
3.x |
Landing page reactivity |
Infrastructure
| Component |
Technology |
Purpose |
| Containerization |
Docker |
20.10+ |
| Orchestration |
Docker Compose |
2.x |
| Reverse Proxy |
Nginx |
SSL termination, routing |
| Documentation |
MkDocs Material |
9.x |
| SSL Certificates |
Let's Encrypt |
Auto-renewed HTTPS |
External Services
| Service |
Provider |
Purpose |
| SMS/MMS |
JustCall |
Two-way customer communication |
| Storage |
AWS S3 |
Signatures, photos, backups |
| Accounting |
Xero |
Invoice/payment sync |
| Spam Protection |
Google reCAPTCHA v3 |
Landing page form protection |
| Notifications |
Google Chat |
Team alerts (FSM, Sales, etc.) |
Container Architecture
Docker Compose Services
services:
odoo: # Odoo ERP application
db: # PostgreSQL database (test only, prod uses RDS)
pwa: # Field service PWA
landing: # Public landing page
nginx: # Reverse proxy with SSL
docs: # Documentation portal
Container Details
| Service |
Base Image |
Internal Port |
Test External |
Production |
| odoo |
odoo:15 (custom) |
8069 |
8016 |
nginx only |
| db |
postgres:15 |
5432 |
5432 |
AWS RDS |
| pwa |
python:3.11-slim |
8000 |
8000 |
nginx only |
| landing |
python:3.11-slim |
8001 |
8001 |
nginx only |
| nginx |
nginx:alpine |
80, 443 |
80, 443 |
80, 443 |
| docs |
python:3.11-slim |
8002 |
8002 |
nginx only |
Environment Overlays
| File |
Purpose |
Database |
Exposed Ports |
docker-compose.yml |
Base services |
- |
- |
docker-compose.test.yml |
Test overlay |
Local PostgreSQL |
All services |
docker-compose.prod.yml |
Production overlay |
AWS RDS |
Only nginx 80/443 |
# Test environment (default in .env)
COMPOSE_FILE=docker-compose.yml:docker-compose.test.yml
# Production environment
COMPOSE_FILE=docker-compose.yml:docker-compose.prod.yml
Network Architecture
flowchart TB
subgraph "Docker Bridge Network"
subgraph "Services"
odoo[Odoo :8069]
db[PostgreSQL :5432]
pwa[PWA :8000]
landing[Landing :8001]
docs[Docs :8002]
nginx[Nginx :80/:443]
end
odoo <--> db
nginx --> odoo
nginx --> pwa
nginx --> landing
nginx --> docs
pwa --> odoo
landing --> odoo
end
Internet((Internet)) --> nginx
Network isolation:
- All services on internal bridge network
- Only nginx exposes ports to host
- In production, only ports 80/443 accessible externally
Volume Mounts
| Volume |
Type |
Container |
Path |
Purpose |
| ./odoo-data |
Bind mount |
odoo |
/var/lib/odoo |
Filestore, sessions, addons |
| postgres-data |
Docker volume |
db |
/var/lib/postgresql/data |
Database (test only) |
| ./extra-addons/odoo |
Bind mount |
odoo |
/mnt/extra-addons/odoo |
Custom modules |
| ./extra-addons/helpdesk |
Bind mount |
odoo |
/mnt/extra-addons/helpdesk |
Helpdesk modules |
| ./configs/odoo.conf |
Bind mount |
odoo |
/etc/odoo/odoo.conf |
Odoo configuration |
| ./nginx/conf.d |
Bind mount |
nginx |
/etc/nginx/conf.d |
Nginx server configs |
| ./nginx/ssl |
Bind mount |
nginx |
/etc/nginx/ssl |
SSL certificates |
| ./nginx/certbot |
Bind mount |
nginx |
/var/www/certbot |
ACME challenge files |
Odoo Data Storage
The odoo-data directory is a bind mount for easier backup and direct host access:
odoo-data/
├── addons/ # Downloaded Odoo Apps store modules
├── filestore/ # Attachments, images, documents
│ └── <db_name>/ # Database-specific files
└── sessions/ # User session data
PostgreSQL Data
PostgreSQL uses a Docker named volume (postgres-data) for data integrity and proper file permissions.
Module Architecture
Module Categories
extra-addons/
├── odoo/ # Custom Odoo modules
│ ├── jdx_* # JDX company modules
│ ├── justcall_* # JustCall integrations
│ ├── xero_* # Xero integrations
│ └── fieldservice/ # FSM base module
│
├── helpdesk/ # Helpdesk modules
│ ├── helpdesk/ # Core helpdesk
│ └── jdx_helpdesk_fsm/ # FSM integration
│
└── (other categories)
Module Dependencies
┌─────────┐
│ base │
└────┬────┘
│
┌───────────────┼───────────────┐
│ │ │
▼ ▼ ▼
┌─────────┐ ┌──────────┐ ┌──────────┐
│ sale │ │ contacts │ │ stock │
└────┬────┘ └────┬─────┘ └────┬─────┘
│ │ │
│ ┌────┴────┐ │
│ ▼ ▼ │
│ ┌──────────┐ ┌─────────┐ │
│ │ helpdesk │ │ crm │ │
│ └────┬─────┘ └────┬────┘ │
│ │ │ │
▼ ▼ ▼ ▼
┌─────────────────────────────────────────┐
│ fieldservice │
└─────────────────┬───────────────────────┘
│
┌─────────────────┼─────────────────┐
│ │ │
▼ ▼ ▼
┌────────────┐ ┌───────────┐ ┌──────────────┐
│jdx_fsm_ │ │jdx_service│ │jdx_field_ │
│calendar │ │_signature │ │service_auto │
└────────────┘ └───────────┘ └──────────────┘
Custom Module Overview
| Module |
Purpose |
Dependencies |
justcall_sms |
SMS/MMS messaging |
contacts |
jdx_service_signature |
Digital signatures |
fieldservice |
xero_integration |
Accounting sync |
account |
jdx_field_service_automation |
FSM extensions |
fieldservice |
jdx_fsm_calendar |
Calendar sync |
fieldservice, calendar |
restapi |
REST API endpoints |
base |
Data Flow
Order to Field Service Flow
┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐
│ Sale │ │ FSM │ │ PWA │ │ Customer │
│ Order │────▶│ Order │────▶│ App │────▶│ Signature│
└──────────┘ └──────────┘ └──────────┘ └──────────┘
│ │ │ │
│ │ │ │
▼ ▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐
│ Invoice │ │ Schedule │ │ Complete │ │ S3 │
│ Created │ │ Calendar │ │ Job │ │ Storage │
└──────────┘ └──────────┘ └──────────┘ └──────────┘
SMS Notification Flow
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Odoo │ │ JustCall │ │ Customer │
│ Trigger │────▶│ API │────▶│ Phone │
└─────────────┘ └─────────────┘ └─────────────┘
│ │ │
│ │ │
▼ ▼ ▼
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Log SMS │ │ Delivery │ │ Response │
│ Record │◀────│ Status │◀────│ (if any) │
└─────────────┘ └─────────────┘ └─────────────┘
API Authentication Flow
┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐
│ Client │ │ Nginx │ │ Odoo │ │ Database │
│ Request │────▶│ Proxy │────▶│ API │────▶│ Verify │
└──────────┘ └──────────┘ └──────────┘ └──────────┘
│ │
│ │
│ ┌──────────┐ │
│◀──────────│ JSON │◀──────────┘
│ Response │
└──────────┘
Security Architecture
Network Security
Internet ──▶ Firewall ──▶ Nginx (SSL) ──▶ Internal Network
│
▼
┌─────────────────┐
│ Rate Limiting │
│ IP Filtering │
│ SSL/TLS 1.3 │
└─────────────────┘
Authentication Layers
| Layer |
Method |
Purpose |
| Nginx |
Basic Auth (optional) |
Admin protection |
| Odoo |
Session/Cookie |
User authentication |
| API |
API Key / Token |
Service authentication |
| Database |
Password |
Direct access (restricted) |
Data Protection
| Data Type |
Protection |
Storage |
| Passwords |
Hashed (PBKDF2) |
PostgreSQL |
| API Keys |
Encrypted |
PostgreSQL |
| Sessions |
Signed cookies |
Memory/DB |
| Files |
Access controlled |
Filestore/S3 |
Scalability Considerations
Current Architecture (Single Server)
┌─────────────────────────────────────┐
│ Single Server │
│ ┌─────┐ ┌────┐ ┌─────┐ ┌─────┐ │
│ │Odoo │ │ DB │ │ PWA │ │Nginx│ │
│ └─────┘ └────┘ └─────┘ └─────┘ │
└─────────────────────────────────────┘
Scaled Architecture (Future)
┌─────────────┐
│Load Balancer│
└──────┬──────┘
│
┌────────────────┼────────────────┐
│ │ │
▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────┐
│ Odoo 1 │ │ Odoo 2 │ │ Odoo 3 │
└────┬─────┘ └────┬─────┘ └────┬─────┘
│ │ │
└───────────────┼───────────────┘
│
▼
┌─────────────────────┐
│ PostgreSQL RDS │
│ (Primary/Replica) │
└─────────────────────┘
Scaling Strategy
| Component |
Horizontal |
Vertical |
Notes |
| Odoo |
Yes (workers) |
Yes |
Add workers, then instances |
| PostgreSQL |
Read replicas |
Yes |
RDS handles scaling |
| Nginx |
Yes |
Limited |
Multiple load balancers |
| PWA |
Yes |
Yes |
Stateless, easy to scale |
Disaster Recovery
Backup Strategy
| Component |
Frequency |
Retention |
Location |
| Database |
Daily |
30 days |
S3 |
| Filestore |
Daily |
30 days |
S3 |
| Config |
On change |
Version controlled |
Git |
| Secrets |
On change |
Encrypted |
Secure vault |
Recovery Procedures
| Scenario |
RTO |
RPO |
Procedure |
| Container crash |
5 min |
0 |
Auto-restart |
| Server failure |
30 min |
24 hr |
Restore from backup |
| Data corruption |
1 hr |
24 hr |
Point-in-time recovery |
| Region failure |
4 hr |
24 hr |
Cross-region restore |
Monitoring Points
Health Checks
| Service |
Endpoint |
Expected |
| Odoo |
/web/health |
200 OK |
| PWA |
/health |
200 OK |
| Nginx |
/nginx-health |
200 OK |
| PostgreSQL |
pg_isready |
0 exit |
Key Metrics
| Metric |
Warning |
Critical |
| CPU Usage |
> 70% |
> 90% |
| Memory Usage |
> 80% |
> 95% |
| Disk Usage |
> 70% |
> 90% |
| Response Time |
> 2s |
> 5s |
| Error Rate |
> 1% |
> 5% |
Integration Points
External APIs
| Integration |
Direction |
Protocol |
Authentication |
| JustCall |
Outbound |
REST/HTTPS |
API Key |
| Xero |
Bidirectional |
OAuth 2.0 |
Token |
| AWS S3 |
Outbound |
HTTPS |
IAM Keys |
| Google Calendar |
Bidirectional |
REST |
OAuth 2.0 |
Internal APIs
| Endpoint |
Purpose |
Auth |
/api/v1/* |
REST API |
API Key |
/jsonrpc |
JSON-RPC |
Session |
/xmlrpc |
XML-RPC |
Basic Auth |
File Structure
odoo15-production/
├── docker-compose.yml # Base Docker Compose
├── docker-compose.test.yml # Test environment overlay
├── docker-compose.prod.yml # Production environment overlay
├── production_deploy.sh # Centralized deployment script
├── .env # Active configuration (not in git)
├── .env.example # Environment template
├── .env.test # Test environment template
│
├── odoo/ # Custom Odoo Docker image
│ ├── Dockerfile # Extends odoo:15 with Python deps
│ └── requirements.txt # Additional Python packages
│
├── odoo-data/ # Odoo data (bind mount)
│ ├── addons/ # Downloaded Odoo Apps store modules
│ ├── filestore/ # Attachments, images, documents
│ │ └── <db_name>/ # Database-specific files
│ └── sessions/ # User session data
│
├── configs/
│ └── odoo.conf # Odoo configuration
│
├── extra-addons/ # Custom modules
│ ├── odoo/ # Main custom modules
│ │ ├── jdx_core_data/ # Base configuration
│ │ ├── justcall_sms/ # SMS integration
│ │ ├── jdx_service_signature/ # Digital signatures
│ │ ├── xero_integration/ # Accounting sync
│ │ ├── restapi/ # REST API
│ │ └── ...
│ └── helpdesk/ # Helpdesk module stack
│
├── pwa/ # Field service PWA
│ ├── app/ # Flask application
│ ├── templates/ # Jinja2 templates
│ ├── static/ # CSS, JS, images
│ ├── Dockerfile
│ └── requirements.txt
│
├── landing-page/ # Public landing page
│ ├── app/ # Flask application
│ ├── templates/ # Jinja2 templates
│ ├── static/ # CSS, JS, images
│ ├── tailwind.config.js # Tailwind CSS config
│ ├── Dockerfile
│ └── requirements.txt
│
├── nginx/ # Nginx configuration
│ ├── conf.d/
│ │ ├── default.conf # Test HTTP config
│ │ └── production.conf.template # Production HTTPS template
│ ├── ssl/ # SSL certificates
│ │ └── live/<domain>/ # Let's Encrypt certs
│ └── certbot/ # ACME challenge files
│
├── scripts/ # Utility scripts
│ ├── backup.sh # Backup script
│ ├── restore.sh # Restore script
│ ├── health-check.sh # Health check
│ ├── ssl-init.sh # SSL certificate management
│ └── ssl-renew-cron.sh # SSL auto-renewal (cron)
│
├── docs/ # Documentation (MkDocs)
│ ├── mkdocs.yml # MkDocs configuration
│ ├── content/ # Markdown files
│ ├── Dockerfile
│ └── requirements.txt
│
└── LESSONS_LEARNED.md # Project notes
Configuration Flow
┌─────────────────────────────────────────────────────────────┐
│ .env (Source of Truth) │
├─────────────────────────────────────────────────────────────┤
│ ENVIRONMENT → test / production │
│ COMPOSE_FILE → Which compose files to load │
│ DB_* → Database connection │
│ DOMAIN_* → Domain names for nginx │
│ LETSENCRYPT_EMAIL → SSL certificate notifications │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ production_deploy.sh │ ssl-init.sh │ docker-compose │
└─────────────────────────────────────────────────────────────┘