Multi-tenancy in the Odoo world means running multiple separate Odoo databases or instances on shared infrastructure. Every tenant — whether that is a client company, a business unit, or a SaaS customer — gets their own data, users, and configuration. The question is how you isolate them.
Odoo was not originally designed as a multi-tenant SaaS platform the way, say, Salesforce was. It started as a single-database ERP. But the community and companies building on Odoo have developed several patterns that work well in production. We have seen all three approaches running at scale across hundreds of deployments managed through OEC.sh's multi-tenant platform.
What Is Multi-Tenancy in Odoo?
Multi-tenancy means serving multiple independent customers (tenants) from shared infrastructure. In the Odoo context, each tenant typically needs their own database — Odoo stores everything in PostgreSQL, including module configurations, business data, and user accounts. Two tenants cannot share a single Odoo database without seeing each other's data.
This is different from Odoo's built-in multi-company feature, which lets you create multiple companies inside one database with record-level access rules. Multi-company works for related business entities under the same organization, but it is not suitable for unrelated tenants because all companies share the same module set, the same Odoo version, and the same admin user pool. If Client A needs Odoo 18 and Client B is still on Odoo 17, multi-company will not work.
True multi-tenancy requires one of three approaches, each with a different isolation level:
- 1Shared Database (dbfilter) — One Odoo process serves multiple PostgreSQL databases, routing by hostname.
- 2Isolated Instances — Each tenant gets their own Odoo process and database on the same server.
- 3Container-Based (Docker/Kubernetes) — Each tenant runs in an isolated container with their own Odoo + database stack.
Architecture Options Compared
Each approach sits at a different point on the isolation-vs-efficiency spectrum. Here is a detailed breakdown.
Option A: Shared Database with dbfilter
Odoo has a built-in mechanism for multi-database operation. You run a single Odoo server process that connects to a PostgreSQL instance containing multiple databases. The --db-filter parameter routes incoming requests to the correct database based on the hostname. For example, client-a.yoursaas.com hits database client_a while client-b.yoursaas.com hits client_b.
Shared Database Architecture
┌──────────────────────────────────────────┐
│ Nginx / Reverse Proxy │
│ client-a.saas.com client-b.saas.com │
└─────────────────┬────────────────────────┘
│
┌─────────────────▼────────────────────────┐
│ Single Odoo Process │
│ --db-filter=^%h$ (hostname routing) │
│ Workers: 4-8 | RAM: ~1.2 GB │
└─────────────────┬────────────────────────┘
│
┌─────────────────▼────────────────────────┐
│ PostgreSQL │
│ ┌──────────┐ ┌──────────┐ ┌────────┐│
│ │client_a │ │client_b │ │client_c││
│ │ database │ │ database │ │database ││
│ └──────────┘ └──────────┘ └────────┘│
└──────────────────────────────────────────┘Pros
- Lowest resource usage — one Odoo process for all tenants
- Simplest setup — just add databases and DNS entries
- Minimal per-tenant overhead (no extra processes)
- Good for 5-30 low-traffic tenants on a single server
Cons
- Shared failure domain — one crash affects all tenants
- All tenants locked to the same Odoo version
- All tenants share the same installed modules
- A heavy query from one tenant slows everyone down
- Difficult to do per-tenant resource limits
When to use it: Internal multi-company setups, dev/staging environments, or very small SaaS operations where all tenants are on the same Odoo version with the same modules. Not recommended when tenants have different SLA requirements or need independent update schedules.
Option B: Isolated Instances
Each tenant gets their own Odoo process running on a different port, with their own PostgreSQL database. A reverse proxy (Nginx, Caddy, or Traefik) routes traffic based on the hostname to the correct Odoo port. Each instance can run a different Odoo version, install different modules, and be restarted independently.
Isolated Instance Architecture
┌──────────────────────────────────────────┐
│ Nginx / Reverse Proxy │
│ client-a.com → :8069 │
│ client-b.com → :8070 │
│ client-c.com → :8071 │
└──┬──────────────┬──────────────┬─────────┘
│ │ │
┌──▼─────┐ ┌───▼────┐ ┌───▼────┐
│ Odoo │ │ Odoo │ │ Odoo │
│ v18 │ │ v17 │ │ v18 │
│ :8069 │ │ :8070 │ │ :8071 │
└──┬─────┘ └───┬────┘ └───┬────┘
│ │ │
┌──▼─────┐ ┌───▼────┐ ┌───▼────┐
│client_a│ │client_b│ │client_c│
│ (PG) │ │ (PG) │ │ (PG) │
└────────┘ └────────┘ └────────┘Pros
- Full isolation — one tenant's crash does not affect others
- Independent Odoo versions per tenant
- Independent module sets and configurations
- Per-tenant backups, restores, and update schedules
- Can allocate specific CPU/RAM limits per process
Cons
- Higher RAM usage — each Odoo process takes 150-200 MB per worker
- More processes to manage, monitor, and update
- Port management complexity grows with tenant count
- Manual systemd/supervisor config per tenant unless automated
When to use it: Agencies managing 5-30 client installations where clients need different Odoo versions or module sets. This is the approach most Odoo consulting firms use because it provides strong isolation without the complexity of container orchestration.
Option C: Container-Based (Docker / Kubernetes)
Each tenant runs inside their own Docker container (or Kubernetes pod) with Odoo and optionally their own PostgreSQL instance. A reverse proxy like Traefik or an Ingress controller routes traffic. Container orchestration handles resource limits, health checks, restarts, and scaling.
Container-Based Architecture
┌──────────────────────────────────────────┐
│ Traefik / Ingress Controller │
│ Automatic SSL | Host-based routing │
└──┬──────────────┬──────────────┬─────────┘
│ │ │
┌──▼───────────┐ ┌▼────────────┐ ┌▼───────────┐
│ Container A │ │Container B │ │Container C │
│ ┌──────────┐ │ │┌──────────┐ │ │┌──────────┐ │
│ │ Odoo v18 │ │ ││ Odoo v17 │ │ ││ Odoo v18 │ │
│ └────┬─────┘ │ │└────┬─────┘ │ │└────┬─────┘ │
│ ┌────▼─────┐ │ │┌────▼─────┐ │ │┌────▼─────┐ │
│ │ PG (own) │ │ ││ PG (own) │ │ ││ PG (own) │ │
│ └──────────┘ │ │└──────────┘ │ │└──────────┘ │
│ CPU: 2 cores │ │CPU: 1 core │ │CPU: 4 cores │
│ RAM: 2 GB │ │RAM: 1 GB │ │RAM: 4 GB │
└──────────────┘ └─────────────┘ └─────────────┘Pros
- Strongest isolation — process, filesystem, and network separation
- Enforced resource limits (CPU, RAM, disk I/O) per tenant
- Reproducible deployments via container images
- Easy horizontal scaling — add more nodes as tenants grow
- Self-healing with automatic restarts on failure
Cons
- Highest operational complexity (especially Kubernetes)
- Container overhead adds ~50-100 MB per tenant
- Requires Docker/K8s expertise on your team
- Persistent storage management is non-trivial
- Debugging is harder — logs scattered across containers
When to use it: SaaS providers running 30+ tenants who need strict resource isolation and automated scaling. Also the right choice when you have a DevOps team comfortable with container orchestration. Overkill for agencies managing fewer than 10 clients.
Quick Comparison
| Factor | Shared DB | Isolated | Container |
|---|---|---|---|
| Data isolation | Database-level | Process-level | Container-level |
| Version independence | No | Yes | Yes |
| RAM per tenant | ~50 MB | ~300-400 MB | ~400-500 MB |
| Max tenants (16 GB) | 20-30 | 8-10 | 6-8 |
| Failure isolation | None | Per-process | Full |
| Setup complexity | Low | Medium | High |
| Resource limits | Shared | cgroups/ulimit | Docker/K8s native |
| Backup granularity | Per-database | Per-instance | Per-container |
Security Considerations
Multi-tenancy introduces security concerns that do not exist in single-tenant deployments. When multiple clients share infrastructure, you need to guarantee that Tenant A can never access Tenant B's data — not through the UI, not through the API, and not through the filesystem.
Each tenant must have their own PostgreSQL database. Odoo's multi-database mode enforces this at the application level, but you should also enforce it at the PostgreSQL level by creating separate database roles per tenant with GRANT permissions limited to their own database. Never let Odoo's database manager UI stay enabled in production — set --no-database-list in your Odoo configuration.
Odoo stores uploaded files (attachments, images, documents) in a filestore directory. Each tenant needs their own filestore path. In shared database mode, Odoo handles this automatically (one directory per database name). In isolated or container setups, each instance has its own filestore by default. Verify that filesystem permissions prevent cross-tenant access.
Never share admin credentials across tenants. Each tenant database should have its own admin user with a unique password. If you use a master password for database management, change it from the default and restrict access by IP. Better yet, disable the database manager entirely and manage databases through PostgreSQL directly.
Tenant backups must be stored separately with access controls. A backup of Tenant A's database should never be accessible to Tenant B's admin. Use per-tenant backup directories with restricted permissions, or store backups in separate cloud storage buckets.
In container-based setups, use Docker networks or Kubernetes network policies to prevent containers from communicating directly. Each tenant's Odoo instance should only be able to reach its own database and the reverse proxy — nothing else.
Performance Planning
Multi-tenant Odoo deployments fail most often on resource planning. A server that runs one Odoo instance comfortably can grind to a halt when you add ten more without accounting for memory, CPU, and database connection overhead. Here is how to plan correctly.
Memory Budgeting
Each Odoo worker process consumes 150-200 MB of RAM under typical load. A minimal production setup with 2 workers per instance uses 300-400 MB. Add PostgreSQL shared buffers (25% of RAM is a common starting point) and OS overhead, and you get this baseline:
Memory Budget Example (16 GB Server)
Total RAM: 16,384 MB ───────────────────────────────────────── OS + system services: 1,024 MB PostgreSQL shared_buffers: 4,096 MB (25% of RAM) PostgreSQL connections: 512 MB (~10 MB x 50 connections) ───────────────────────────────────────── Available for Odoo: 10,752 MB Per instance (2 workers): 400 MB ───────────────────────────────────────── Max instances: 10,752 / 400 ≈ 26 With safety margin (80%): ~20 instances With cron workers (+1 per): ~15 instances
These numbers assume light workloads (5-10 active users per tenant). For tenants with 20+ active users, budget 4 workers per instance and cut the instance count in half. Use the OEC.sh server calculator to model your specific setup.
Shared PostgreSQL vs Dedicated
In most multi-tenant setups, all Odoo instances share a single PostgreSQL server. This works well up to 20-30 tenants on a 16 GB server. Beyond that, PostgreSQL becomes the bottleneck — not on CPU but on connection count and shared buffer contention.
Each Odoo worker opens one PostgreSQL connection. With 15 instances running 2 workers each plus 1 cron worker, you need at least 45 connections. Add connection pooling with PgBouncer to multiplex these efficiently:
PgBouncer Configuration for Multi-Tenant
[pgbouncer] pool_mode = transaction # Best for Odoo max_client_conn = 200 # Total from all Odoo instances default_pool_size = 20 # Connections to PostgreSQL reserve_pool_size = 5 # Overflow buffer reserve_pool_timeout = 3 # Seconds before using reserve # Per-database overrides [databases] client_a = host=127.0.0.1 dbname=client_a pool_size=5 client_b = host=127.0.0.1 dbname=client_b pool_size=5 client_c = host=127.0.0.1 dbname=client_c pool_size=3
PgBouncer in transaction pooling mode can reduce your actual PostgreSQL connections by 3-5x, which is critical for multi-tenant setups where connection exhaustion is the most common cause of outages.
CPU Allocation
Odoo is mostly I/O-bound (waiting on PostgreSQL), so CPU usually is not the first bottleneck. A 4-core server can typically handle 8-12 Odoo workers across multiple instances without saturation. The exception is heavy reporting or data-intensive operations — if one tenant runs a large stock valuation report, it can consume a full core for several seconds.
For container setups, set CPU limits per container to prevent one tenant from monopolizing the host. For isolated instances, use cgroups or systemd resource controls.
How OEC.sh Handles Multi-Tenancy
OEC.sh uses the isolated instance approach with additional automation layered on top. When you deploy through OEC.sh, each Odoo project on a server gets:
- Its own Odoo process with independent configuration
- Its own PostgreSQL database with isolated credentials
- Its own filestore directory with restricted filesystem permissions
- Independent backup schedules — daily, weekly, or custom
- Independent Odoo version — run v18, v17, and v16 on the same server
- Independent module management — install, update, and remove per-project
- Its own domain and SSL certificate via Let's Encrypt
- Separate log files for monitoring and debugging
The Agency plan supports unlimited projects per server, which makes it the natural fit for consultancies and SaaS providers running multi-tenant Odoo. You bring your own cloud server (AWS, Hetzner, DigitalOcean, or any provider), and OEC.sh handles the deployment, isolation, and management. You keep full control of your infrastructure and your clients' data.
This avoids the shared-failure-domain problem of dbfilter mode while keeping operational overhead much lower than managing your own Kubernetes cluster. For most agencies, it hits the sweet spot.
Running multi-tenant Odoo? OEC.sh lets you deploy unlimited Odoo projects per server with full isolation. Each project gets its own instance, database, filestore, and backup schedule. Connect your cloud account and deploy your first multi-tenant server in under 10 minutes.
Billing Models for Odoo SaaS
If you are building a SaaS product on Odoo or reselling Odoo deployments, you need a billing model that covers your infrastructure costs while staying competitive. Here are the four models we see in practice:
1. Per-User Pricing
Charge per active Odoo user per month (e.g., $15-50/user). This is how Odoo.com prices Enterprise licenses, so customers are already familiar with the model. Cost scales with usage, which feels fair. The challenge is that user counts fluctuate, and you need infrastructure capacity for peak usage regardless of how many users are active today.
Best for: SaaS products where user count correlates with server load.
2. Per-Database Pricing
Charge a flat fee per Odoo database/instance (e.g., $99-299/month). Simpler for the customer to understand and easier for you to plan capacity since each database has a predictable resource footprint. Works well for agencies managing client installations where each client is one database.
Best for: Agencies and consultants managing client installations.
3. Flat Fee / Tiered Plans
Offer fixed plans with resource limits (e.g., Small: 5 users + 10 GB storage for $149/mo, Medium: 20 users + 50 GB for $349/mo). Predictable revenue and simple decision-making for customers. The downside is that some customers will overpay (low usage on a big plan) or underpay (heavy usage on a small plan).
Best for: Self-service SaaS products targeting SMBs.
4. Resource-Based Pricing
Charge based on actual resource consumption — CPU hours, RAM-hours, storage used, or database size. This is the most “cloud-native” approach and the fairest in theory, but it is hard for non-technical customers to understand and creates unpredictable monthly bills. Odoo.sh uses a variation of this model (staging servers, workers, storage as separate line items).
Best for: Technical audiences and enterprise customers who understand cloud pricing.
Our recommendation: Start with per-database pricing. It is the simplest to implement, easiest for customers to understand, and maps directly to your infrastructure costs (one Odoo instance = one set of resources). As you scale, layer in user-based pricing tiers for larger clients. Avoid pure resource-based pricing unless your customers are technically sophisticated.
To keep your margins healthy, use the server calculator to estimate your per-tenant infrastructure cost, then price at 3-5x that cost. For example, if a single Odoo instance on Hetzner costs you about $5/month in server resources, charge $25-49/month for a managed deployment. That leaves room for support, backups, monitoring, and profit. Check our cloud provider comparison for detailed cost breakdowns.
Frequently Asked Questions
Can Odoo support multiple tenants?
Yes. Odoo supports multi-tenancy through several approaches: shared database mode using the --db-filter parameter to route requests to different databases based on hostname, isolated instances where each tenant gets a separate Odoo process and PostgreSQL database, or container-based isolation using Docker or Kubernetes. The right approach depends on your isolation requirements, the number of tenants, and how much operational complexity you can manage.
What is the best architecture for Odoo SaaS?
For most Odoo SaaS providers, container-based isolation with Docker gives you the best trade-off between security, flexibility, and day-to-day operations. Each tenant gets an isolated Odoo container with its own database, which provides strong data separation without the overhead of managing separate VMs. For smaller operations with fewer than 10 tenants, isolated instances on a single server work well. Shared database mode with dbfilter is the lightest option but carries risk since all tenants share the same Odoo process.
How many Odoo instances can run on one server?
A typical Odoo worker process consumes 150-200 MB of RAM. On a server with 16 GB RAM (reserving 4 GB for PostgreSQL and the OS), you can run approximately 8-10 isolated Odoo instances with 2 workers each, serving light workloads of 5-10 users per instance. For a 32 GB server, that scales to 15-20 instances. With shared database mode (single Odoo process), a 16 GB server can serve 20-30 separate databases, though all tenants share the same failure domain.
Does OEC.sh support multi-tenant Odoo?
Yes. OEC.sh supports multiple Odoo projects per VM, where each project gets its own isolated Odoo instance, PostgreSQL database, and filestore. Projects are fully independent — you can run different Odoo versions, install different modules, and manage backups separately. The Agency plan supports unlimited projects per server, making it ideal for consultancies and SaaS providers managing multiple client deployments.
Deploy Multi-Tenant Odoo in Minutes
Run unlimited Odoo projects per server with full isolation. Each project gets its own instance, database, filestore, and backups. Connect your cloud account and start deploying.