frappe-operator

Concepts

Understanding the key concepts and architecture of Frappe Operator.

Overview

Frappe Operator manages Frappe deployments using two primary resources:

This architecture enables efficient multi-tenancy while maintaining isolation where needed.

Core Concepts

FrappeBench

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:

FrappeSite

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:

  1. Pending: Site resource created
  2. Provisioning: Database and storage being set up
  3. Ready: Site is accessible
  4. Failed: Error occurred (check events)

Architecture

Component Architecture

┌─────────────────────────────────────────────────────────────────┐
│                         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│  │
    │  └──┘   │ │  └──┘  │ │  └──┘  │ │  └──┘  │
    └─────────┘ └────────┘ └────────┘ └────────┘

Request Flow

  1. External Request → Ingress Controller
  2. Ingress → NGINX Service (bench-level)
  3. NGINX → Routes to correct site based on Host header
  4. Gunicorn → Processes request
  5. Database → Site-specific data
  6. Redis → Shared cache/queue

Storage Architecture

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

Database Configurations

Shared Database Mode

Multiple sites share a MariaDB instance but have separate databases:

dbConfig:
  mode: shared
  mariadbRef:
    name: shared-mariadb
    namespace: databases

Pros:

Cons:

Use Cases:

Dedicated Database Mode

Each site gets its own MariaDB instance:

dbConfig:
  mode: dedicated
  storageSize: 50Gi
  resources:
    requests:
      cpu: 1000m
      memory: 2Gi

Pros:

Cons:

Use Cases:

External Database Mode

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:

Component Roles

NGINX (Bench-Level)

Gunicorn (Site-Level)

Socketio (Site-Level)

Scheduler (Site-Level)

Workers (Site-Level)

Three worker types with different queue priorities:

Worker Default

Worker Long

Worker Short

Redis/DragonFly (Bench-Level)

DragonFly vs Redis:

Domain Resolution

Frappe uses the HTTP Host header to determine which site to serve. The operator provides multiple ways to configure domains:

1. Explicit Domain

spec:
  siteName: "mysite"
  domain: "mysite.example.com"

2. Bench-Level Suffix

# In FrappeBench
spec:
  domainConfig:
    suffix: ".platform.com"

# In FrappeSite
spec:
  siteName: "customer1"
  # Results in: customer1.platform.com

3. Auto-Detection

spec:
  domainConfig:
    autoDetect: true
    ingressControllerRef:
      name: ingress-nginx-controller
      namespace: ingress-nginx

The operator detects the Load Balancer IP/hostname and uses it.

4. SiteName Default

If no domain is specified, siteName is used as the domain.

Best Practices:

Resource Management

Resource Tiers

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"}

Autoscaling

Enable Horizontal Pod Autoscaling:

componentHPA:
  gunicorn:
    enabled: true
    minReplicas: 2
    maxReplicas: 10
    targetCPUUtilization: 70

Multi-Tenancy Models

Model 1: Shared Bench, Shared Database

Model 2: Shared Bench, Dedicated Databases

Model 3: Dedicated Bench per Tenant

Model 4: Bench per Environment

Secrets Management

The operator manages several types of secrets:

Admin Password

spec:
  adminPasswordSecretRef:
    name: site-admin-pwd

Database Credentials

Auto-generated or external:

# Auto-generated (dedicated mode)
status:
  dbConnectionSecret: site1-db-connection

# External mode
spec:
  dbConfig:
    connectionSecretRef:
      name: custom-db-credentials

Image Pull Secrets

For private registries:

spec:
  imageConfig:
    pullSecrets:
      - name: docker-registry-secret

Storage Classes

Site Storage (RWO)

Logs Storage (RWO)

Bench Storage (RWX)

High Availability

Application HA

Database HA

Redis HA

Ingress HA

Next Steps