Skip to main content
This guide covers setting up and managing multi-tenant Frappe deployments where multiple sites run on different domains from a single bench instance.

Understanding Multitenancy

Bench supports two modes of operation:

Port-based (Default)

  • Each site runs on a different port
  • Example: site1.localhost:8001, site2.localhost:8002
  • Suitable for development
  • No DNS configuration required

DNS-based (Multitenancy)

  • Each site runs on its own domain
  • Example: company1.com, company2.com
  • Required for production SSL
  • Requires DNS configuration
  • All sites run on ports 80/443
DNS-based multitenancy is required for SSL certificates and production deployments.

Enabling DNS Multitenancy

1
Enable multitenancy mode
2
bench config dns_multitenant on
3
This updates sites/common_site_config.json:
4
{
  "dns_multitenant": true
}
5
Regenerate NGINX configuration
6
bench setup nginx
7
This creates NGINX configuration that routes requests based on domain name rather than port.
8
Reload NGINX
9
sudo bench setup reload-nginx
10
Or manually:
11
sudo systemctl reload nginx

Creating Multi-Tenant Sites

1
Configure DNS
2
Point your domain(s) to your server:
3
# A record
company1.com    A    203.0.113.10
company2.com    A    203.0.113.10

# Or CNAME for subdomains
app.company1.com    CNAME    server.example.com
4
Verify DNS propagation:
5
dig company1.com +short
nslookup company2.com
6
Create sites with domain names
7
bench new-site company1.com
bench new-site company2.com
8
Use the actual domain name as the site name. Bench will use this for routing.
9
Install apps on sites
10
bench --site company1.com install-app erpnext
bench --site company2.com install-app erpnext
11
Regenerate NGINX configuration
12
bench setup nginx
sudo bench setup reload-nginx

Managing Custom Domains

Add additional domains to existing sites:
1
Add custom domain
2
bench setup add-domain custom.example.com --site company1.com
3
This adds the domain to sites/company1.com/site_config.json:
4
{
  "domains": [
    "custom.example.com"
  ]
}
5
Add domain with SSL
6
bench setup add-domain secure.example.com --site company1.com \
  --ssl-certificate /path/to/cert.pem \
  --ssl-certificate-key /path/to/key.pem
7
Remove custom domain
8
bench setup remove-domain custom.example.com --site company1.com
9
Sync domains
10
Update domain list:
11
bench setup sync-domains --site company1.com --domain domain1.com --domain domain2.com

Site Configuration for Multitenancy

Site config structure

Each site’s configuration in sites/[sitename]/site_config.json:
{
  "db_name": "company1_db",
  "db_password": "secretpassword",
  "domains": [
    "www.company1.com",
    "app.company1.com",
    {
      "domain": "secure.company1.com",
      "ssl_certificate": "/path/to/cert.pem",
      "ssl_certificate_key": "/path/to/key.pem"
    }
  ],
  "host_name": "https://company1.com"
}

Common site config

sites/common_site_config.json for all sites:
{
  "dns_multitenant": true,
  "serve_default_site": true,
  "restart_supervisor_on_update": true,
  "webserver_port": 80,
  "socketio_port": 9000
}

Wildcard SSL for Multitenancy

Use wildcard certificates to cover all subdomains:
1
Setup wildcard certificate
2
sudo bench setup wildcard-ssl *.example.com --email [email protected]
3
Configuration is added to common_site_config.json
4
{
  "wildcard": {
    "domain": "*.example.com",
    "ssl_certificate": "/etc/letsencrypt/live/example.com/fullchain.pem",
    "ssl_certificate_key": "/etc/letsencrypt/live/example.com/privkey.pem"
  }
}
5
Create sites under wildcard domain
6
bench new-site app1.example.com
bench new-site app2.example.com
bench new-site app3.example.com
7
All sites automatically use the wildcard certificate.

NGINX Configuration Details

DNS multitenant NGINX structure

With DNS multitenancy, NGINX uses server name routing:
# Generated in config/nginx.conf

upstream frappe-bench-frappe {
    server 127.0.0.1:8000 fail_timeout=0;
}

upstream frappe-bench-socketio {
    server 127.0.0.1:9000 fail_timeout=0;
}

# HTTP to HTTPS redirect
server {
    listen 80;
    server_name company1.com company2.com;
    return 301 https://$host$request_uri;
}

# HTTPS server blocks
server {
    listen 443 ssl http2;
    server_name company1.com;
    
    ssl_certificate /etc/letsencrypt/live/company1.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/company1.com/privkey.pem;
    
    # Proxy to Frappe
    location / {
        proxy_pass http://frappe-bench-frappe;
    }
}

Domain mapping

NGINX maps domains to site directories using X-Frappe-Site-Name header:
map $host $site_name_abc123 {
    ~^company1\.com$  company1.com;
    ~^company2\.com$  company2.com;
    ~^custom\.example\.com$  company1.com;
}

Managing Multiple Sites

List all sites

bench --site all list
Or:
ls -la sites/

Run command on all sites

bench --site all migrate
bench --site all clear-cache
bench --site all backup

Site-specific commands

bench --site company1.com console
bench --site company2.com backup --with-files
bench --site company1.com migrate

Set default site

bench use company1.com
Then run commands without --site flag:
bench migrate
bench backup

Backup and Restore in Multi-Tenant

Backup individual sites

bench --site company1.com backup --with-files
bench --site company2.com backup --with-files

Backup all sites

bench backup-all-sites

Automated backups

Set up cron for all sites:
bench setup backups
This creates backups for all sites in the bench.

Restore specific site

bench --site company1.com restore /path/to/backup.sql.gz

Performance Considerations

Worker configuration

Configure workers based on site load in sites/common_site_config.json:
{
  "gunicorn_workers": 8,
  "background_workers": 4
}
Rule of thumb: gunicorn_workers = (CPU cores * 2) + 1

Database connections

Each site has its own database. Monitor connection pool:
bench --site company1.com mariadb
SHOW PROCESSLIST;
SHOW STATUS LIKE 'Threads_connected';

Redis isolation

All sites share Redis instances by default. For isolation:
  1. Set up separate Redis instances per site (advanced)
  2. Configure in site_config.json:
{
  "redis_cache": "redis://localhost:13001",
  "redis_queue": "redis://localhost:11001"
}

NGINX caching

Enable caching for static assets in NGINX configuration (advanced).

Troubleshooting Multitenancy

Site not resolving

Issue: Domain doesn’t route to correct site Solutions:
  1. Verify DNS:
    dig company1.com +short
    
  2. Check NGINX configuration:
    sudo nginx -t
    cat config/nginx.conf | grep server_name
    
  3. Regenerate NGINX config:
    bench setup nginx
    sudo bench setup reload-nginx
    
  4. Check site exists:
    ls sites/company1.com/
    

Wrong site loading

Issue: Different site loads than expected Solution: Check domain mapping in site_config.json:
cat sites/company1.com/site_config.json
Verify domains list and regenerate:
bench setup nginx

SSL certificate issues

Issue: SSL works for one domain but not others Solution: Verify each site has SSL configured:
sudo certbot certificates
Setup SSL for missing sites:
sudo bench setup lets-encrypt company2.com

Port conflicts

Issue: After enabling multitenancy, sites still use ports Solution:
  1. Ensure dns_multitenant is on:
    cat sites/common_site_config.json | grep dns_multitenant
    
  2. Remove port assignments from site configs:
    # Edit sites/[sitename]/site_config.json
    # Remove "nginx_port" key
    
  3. Regenerate:
    bench setup nginx
    sudo bench setup reload-nginx
    

Default site issues

Issue: Wrong site loads when accessing by IP Solution: Configure default site in common_site_config.json:
{
  "default_site": "company1.com",
  "serve_default_site": true
}

Migration to Multitenancy

Migrating from port-based to DNS-based:
1
Backup everything
2
bench backup-all-sites
3
Enable DNS multitenancy
4
bench config dns_multitenant on
5
Update site names (if needed)
6
If sites are named like site1.local, you may need to rename:
7
  • Backup site
  • Create new site with domain name
  • Restore backup to new site
  • Delete old site
  • 8
    Configure DNS for all sites
    9
    Add A/CNAME records for each site domain.
    10
    Regenerate configuration
    11
    bench setup nginx
    sudo bench setup reload-nginx
    
    12
    Setup SSL certificates
    13
    sudo bench setup lets-encrypt company1.com
    sudo bench setup lets-encrypt company2.com
    
    14
    Test each site
    15
    Verify all sites are accessible via their domains.

    Best Practices

    1
    Use consistent naming
    2
    Name sites with their production domain:
    3
    bench new-site company.com  # Good
    bench new-site mysite       # Avoid in production
    
    4
    Plan your domain structure
    5
  • Main app: app.company.com
  • Admin panel: admin.company.com
  • API: api.company.com
  • 6
    Use wildcard certificates
    7
    For multiple subdomains:
    8
    sudo bench setup wildcard-ssl *.company.com --email [email protected]
    
    9
    Monitor resource usage
    10
    Each site consumes:
    11
  • Database connections
  • Memory for caching
  • Background workers
  • 12
    Scale infrastructure accordingly.
    13
    Document site mappings
    14
    Maintain documentation of:
    15
  • Site name → Domain mapping
  • SSL certificate assignments
  • Custom domain configurations
  • 16
    Regular backups
    17
    Automate backups for all sites:
    18
    bench setup backups
    
    19
    Test before production
    20
    Test multitenancy setup in staging first.

    Multi-Tenant Architecture Example

    SaaS scenario

    Bench: production-bench
    ├── Site: client1.saas.com (ERPNext)
    ├── Site: client2.saas.com (ERPNext)
    ├── Site: client3.saas.com (ERPNext + Custom App)
    └── Wildcard SSL: *.saas.com
    
    Configuration:
    - DNS multitenancy: enabled
    - Wildcard SSL: *.saas.com
    - Automated backups: daily
    - Workers: 8 gunicorn, 4 background
    

    Multi-company scenario

    Bench: corporate-bench
    ├── Site: company-a.com (ERPNext + HRMS)
    ├── Site: company-b.com (ERPNext + HRMS)
    ├── Site: internal.company-a.com (Custom portal)
    └── Individual SSL per domain
    
    Configuration:
    - DNS multitenancy: enabled
    - Individual Let's Encrypt SSL
    - Separate databases
    - Shared apps, isolated data
    

    Next Steps

    Build docs developers (and LLMs) love