Architecture Overview
System architecture and design documentation for the Odoo 15 ERP platform.
Architecture Evolution
This document describes the current architecture. For the planned FastAPI control plane architecture, see the Architecture Decision Records:
For implementation roadmap, see PDR-001: FastAPI Backend Roadmap.
Current 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]
FastAPI[FastAPI Gateway<br/>:8080<br/>JWT Auth, Audit]
NextJS[Next.js Frontend<br/>:3000<br/>Landing, Portal, PWA]
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"
Postmark[Postmark Pro<br/>Email Primary]
SES[AWS SES<br/>Email Fallback]
JustCall[JustCall API<br/>SMS/MMS]
Xero[Xero API<br/>Accounting]
GChat[Google Chat<br/>Notifications]
end
Users --> SSL
Mobile --> SSL
SSL -->|www.domain| NextJS
SSL -->|api.domain| FastAPI
SSL -->|erp.domain| Odoo
SSL -->|docs.domain| Docs
NextJS --> FastAPI
FastAPI --> Odoo
FastAPI --> DB
Odoo --> DB
Odoo --> FileStore
FastAPI --> S3
FastAPI --> Postmark
FastAPI --> SES
FastAPI <--> JustCall
Odoo <--> Xero
Odoo --> GChat
Port Mapping
| Service |
Internal Port |
External Port (Test) |
Production |
| Odoo |
8069 |
8016 |
Via nginx only |
| FastAPI |
8080 |
8080 |
Via nginx only |
| Next.js |
3000 |
3000 |
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 |
| API Gateway |
FastAPI |
0.109+ |
Control plane, JWT auth, audit |
| Database |
PostgreSQL |
15 |
Data persistence (Local or AWS RDS) |
| Web Server |
Werkzeug |
Built-in |
Odoo HTTP server |
PWA Backend |
Flask |
- |
Deprecated - Replaced by Next.js |
Landing Backend |
Flask |
- |
Deprecated - Replaced by Next.js |
Frontend
| Component |
Technology |
Version |
Purpose |
| ERP UI |
Odoo Web Client |
15.0 |
Desktop interface |
| Frontend App |
Next.js |
15.x |
Unified frontend (Landing, Portal, Field PWA) |
| React |
React |
19.x |
UI library |
| Language |
TypeScript |
5.x |
Type safety |
| Styling |
Tailwind CSS |
4.x |
Utility-first CSS |
| State |
React Query + Zustand |
Latest |
Server + client state |
| Forms |
React Hook Form + Zod |
Latest |
Form handling + validation |
| PWA |
Serwist / next-pwa |
Latest |
Service worker, offline |
| Auth |
NextAuth.js |
5.x |
OAuth/JWT integration |
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 |
| Email (Primary) |
Postmark Pro |
Transactional emails, magic links, per-tenant domains |
| Email (Secondary) |
AWS SES |
Bulk emails, internal notifications, failover |
| 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.) |
Email Architecture (ADR-016)
Hybrid approach: Postmark Pro for customer-facing emails (speed + deliverability), AWS SES for bulk/internal (cost efficiency). Per-tenant sending domains supported via Postmark Domains API.
See ADR-016: Hybrid Email Architecture for details.
Container Architecture
Docker Compose Services
services:
odoo: # Odoo ERP application
db: # PostgreSQL database (test only, prod uses RDS)
fastapi: # FastAPI control plane (API gateway)
nextjs: # Next.js frontend (Landing, Portal, Field PWA)
nginx: # Reverse proxy with SSL
docs: # Documentation portal
# DEPRECATED:
# pwa: # Replaced by nextjs
# landing: # Replaced by nextjs
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 |
| fastapi |
python:3.12-slim |
8080 |
8080 |
nginx only |
| nextjs |
node:22-alpine |
3000 |
3000 |
nginx only |
| nginx |
nginx:alpine |
80, 443 |
80, 443 |
80, 443 |
| docs |
python:3.11-slim |
8002 |
8002 |
nginx only |
pwa |
- |
- |
- |
Deprecated |
landing |
- |
- |
- |
Deprecated |
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]
fastapi[FastAPI :8080]
nextjs[Next.js :3000]
docs[Docs :8002]
nginx[Nginx :80/:443]
end
odoo <--> db
fastapi <--> db
fastapi --> odoo
nginx --> fastapi
nginx --> nextjs
nginx --> odoo
nginx --> docs
nextjs --> fastapi
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
│
├── nextjs-frontend/ # Unified Next.js frontend
│ ├── app/ # Next.js App Router
│ │ ├── (marketing)/ # Landing page routes
│ │ ├── (portal)/ # Customer portal routes
│ │ └── (field)/ # Field service PWA routes
│ ├── components/ # React components
│ ├── lib/ # Utilities, API client
│ ├── public/ # Static assets
│ ├── tailwind.config.ts # Tailwind CSS config
│ ├── next.config.ts # Next.js config
│ ├── Dockerfile
│ └── package.json
│
├── pwa/ # [DEPRECATED] Field service PWA
│ └── (to be removed)
│
├── landing-page/ # [DEPRECATED] Public landing page
│ └── (to be removed)
│
├── 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 │
└─────────────────────────────────────────────────────────────┘