Understanding the key concepts and architecture of Frappe Operator.
Frappe Operator manages Frappe deployments using two primary resources:
This architecture enables efficient multi-tenancy while maintaining isolation where needed.
A FrappeBench represents a Frappe bench environment with shared infrastructure components:
apiVersion: vyogo.tech/v1alpha1
kind: FrappeBench
metadata:
name: production-bench
spec:
frappeVersion: "version-15"
appsJSON: '["erpnext", "hrms"]'
Components Created:
Purpose:
When to Use Multiple Benches:
A FrappeSite represents an individual Frappe site with its own:
apiVersion: vyogo.tech/v1alpha1
kind: FrappeSite
metadata:
name: customer1-site
spec:
benchRef:
name: production-bench
siteName: "customer1.example.com"
dbConfig:
mode: dedicated
Components Created:
Site Lifecycle:
┌─────────────────────────────────────────────────────────────────┐
│ FrappeBench │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────┐ ┌───────────┐ ┌─────────────┐ │
│ │ NGINX │◄────────┤ Redis/ │ │ Common │ │
│ │ (Proxy) │ │ DragonFly │ │ Storage │ │
│ └─────┬────┘ └───────────┘ └─────────────┘ │
│ │ │
└────────┼─────────────────────────────────────────────────────────┘
│
│ Route based on Host header
├──────────┬──────────┬──────────┐
│ │ │ │
┌────▼────┐ ┌──▼─────┐ ┌──▼─────┐ ┌──▼─────┐
│ Site 1 │ │ Site 2 │ │ Site 3 │ │ Site N │
│─────────│ │────────│ │────────│ │────────│
│Gunicorn │ │Gunicorn│ │Gunicorn│ │Gunicorn│
│Socketio │ │Socketio│ │Socketio│ │Socketio│
│Scheduler│ │Scheduler│ │Scheduler│ │Scheduler│
│Workers │ │Workers │ │Workers │ │Workers │
│ │ │ │ │ │ │ │
│ ┌──┐ │ │ ┌──┐ │ │ ┌──┐ │ │ ┌──┐ │
│ │DB│ │ │ │DB│ │ │ │DB│ │ │ │DB│ │
│ └──┘ │ │ └──┘ │ │ └──┘ │ │ └──┘ │
└─────────┘ └────────┘ └────────┘ └────────┘
FrappeBench
└── Common Storage (RWX)
└── /home/frappe/frappe-bench
├── apps/ # Frappe apps
├── config/ # Common config
└── sites/
├── site1.com/
│ ├── private/
│ └── public/
├── site2.com/
│ ├── private/
│ └── public/
└── common_site_config.json
Multiple sites share a MariaDB instance but have separate databases:
dbConfig:
mode: shared
mariadbRef:
name: shared-mariadb
namespace: databases
Pros:
Cons:
Use Cases:
Each site gets its own MariaDB instance:
dbConfig:
mode: dedicated
storageSize: 50Gi
resources:
requests:
cpu: 1000m
memory: 2Gi
Pros:
Cons:
Use Cases:
Use an existing database (RDS, Cloud SQL, etc.):
dbConfig:
mode: external
connectionSecretRef:
name: site1-db-credentials
Secret format:
apiVersion: v1
kind: Secret
metadata:
name: site1-db-credentials
stringData:
host: "mysql.example.com"
port: "3306"
database: "site1_db"
username: "site1_user"
password: "secure_password"
Pros:
Cons:
Use Cases:
Three worker types with different queue priorities:
default, short, longlongshortDragonFly vs Redis:
Frappe uses the HTTP Host header to determine which site to serve. The operator provides multiple ways to configure domains:
spec:
siteName: "mysite"
domain: "mysite.example.com"
# In FrappeBench
spec:
domainConfig:
suffix: ".platform.com"
# In FrappeSite
spec:
siteName: "customer1"
# Results in: customer1.platform.com
spec:
domainConfig:
autoDetect: true
ingressControllerRef:
name: ingress-nginx-controller
namespace: ingress-nginx
The operator detects the Load Balancer IP/hostname and uses it.
If no domain is specified, siteName is used as the domain.
Best Practices:
Small (Development):
componentResources:
gunicorn:
requests: {cpu: "200m", memory: "256Mi"}
limits: {cpu: "500m", memory: "512Mi"}
workerDefault:
requests: {cpu: "100m", memory: "128Mi"}
Medium (Small Production):
componentResources:
gunicorn:
requests: {cpu: "500m", memory: "512Mi"}
limits: {cpu: "1", memory: "1Gi"}
workerDefault:
requests: {cpu: "200m", memory: "256Mi"}
Large (Production):
componentResources:
gunicorn:
requests: {cpu: "1", memory: "2Gi"}
limits: {cpu: "2", memory: "4Gi"}
workerDefault:
requests: {cpu: "500m", memory: "1Gi"}
Enable Horizontal Pod Autoscaling:
componentHPA:
gunicorn:
enabled: true
minReplicas: 2
maxReplicas: 10
targetCPUUtilization: 70
The operator manages several types of secrets:
spec:
adminPasswordSecretRef:
name: site-admin-pwd
Auto-generated or external:
# Auto-generated (dedicated mode)
status:
dbConnectionSecret: site1-db-connection
# External mode
spec:
dbConfig:
connectionSecretRef:
name: custom-db-credentials
For private registries:
spec:
imageConfig:
pullSecrets:
- name: docker-registry-secret