Skip to content

Appwrite

Deploy Appwrite on Kubernetes — a self-hosted Backend-as-a-Service (BaaS) platform providing authentication, databases, storage, functions, messaging, and a real-time API. Ships as a multi-workload chart (~20 Kubernetes Deployments) with bundled MariaDB and Redis.

openSslKeyV1 is the master encryption key — losing it makes encrypted data unrecoverable

appwrite.openSslKeyV1 is a 64-character hex key used to encrypt OAuth provider secrets, API keys, and sensitive credentials stored in the database. If this key changes or is lost, all previously encrypted values become permanently unreadable. Always store it in a Kubernetes Secret via appwrite.existingSecret and never rotate it in production.

Key Features

  • Full Appwrite stack — API, Console v7.5.7, Realtime WebSocket, 12 workers, schedulers, maintenance
  • ~20 Kubernetes workloads — each component runs independently and scales separately
  • MariaDB + Redis — bundled subcharts or external connections
  • Shared PVCs — uploads, cache, certificates, functions, builds, and sites shared across pods
  • Root DB access required — Appwrite needs MariaDB root credentials for schema migrations
  • Ingress routing — Console at /, API at /v1, Realtime WebSocket at /v1/realtime
  • mysqldump backup — scheduled CronJob to S3
Functions execution requires openruntimes-executor (not included in alpha)

The workers.functions and schedulers.functions workloads are deployed, but serverless function execution requires a separate openruntimes-executor service that is not included in this alpha chart. The functions worker will start but function invocations will fail until the executor is available.

Installation

HTTPS repository:

helm repo add helmforge https://repo.helmforge.dev
helm repo update
helm install appwrite helmforge/appwrite -f values.yaml

OCI registry:

helm install appwrite oci://ghcr.io/helmforgedev/helm/appwrite -f values.yaml

Deployment Examples

# values.yaml — Appwrite with bundled MariaDB and Redis
appwrite:
  domain: appwrite.example.com
  # Generate: openssl rand -hex 32
  openSslKeyV1: 'your-64-char-hex-key-here'

mariadb:
  enabled: true
  auth:
    password: 'strong-db-password'
    rootPassword: 'strong-root-password'
  standalone:
    persistence:
      enabled: true
      size: 20Gi

redis:
  enabled: true
  auth:
    enabled: true
    password: 'strong-redis-password'

persistence:
  enabled: true
  uploads:
    size: 20Gi
  functions:
    size: 10Gi
  builds:
    size: 10Gi

ingress:
  enabled: true
  ingressClassName: traefik
  hosts:
    - host: appwrite.example.com
      paths:
        - path: /
          pathType: Prefix
# values.yaml — Appwrite with external MariaDB and Redis
# IMPORTANT: External MariaDB needs root credentials for Appwrite schema migrations
appwrite:
  domain: appwrite.example.com
  existingSecret: appwrite-master-secrets # contains openssl key + JWT secret
  existingSecretOpenSslKey: appwrite-openssl-key
  existingSecretJwtKey: appwrite-jwt-secret

mariadb:
  enabled: false

database:
  mode: external
  external:
    host: mariadb.database.svc.cluster.local
    port: 3306
    name: appwrite
    rootUser: root
    existingSecret: appwrite-mariadb-root # needs ROOT password for migrations
    existingSecretPasswordKey: mariadb-root-password

redis:
  enabled: false

cache:
  mode: external
  external:
    host: redis.cache.svc.cluster.local
    port: 6379
    existingSecret: appwrite-redis-credentials
    existingSecretPasswordKey: redis-password

persistence:
  enabled: true
  uploads:
    size: 20Gi

ingress:
  enabled: true
  ingressClassName: traefik
  hosts:
    - host: appwrite.example.com
      paths:
        - path: /
          pathType: Prefix
# values.yaml — Production Appwrite with SMTP, HTTPS, and backup
appwrite:
  domain: appwrite.example.com
  existingSecret: appwrite-master-secrets
  smtp:
    host: smtp.mailgun.org
    port: '587'
    secure: tls
    username: postmaster@example.com
    existingSecret: appwrite-smtp-credentials
    existingSecretPasswordKey: smtp-password

mariadb:
  enabled: true
  auth:
    password: 'strong-db-password'
    rootPassword: 'strong-root-password'
  standalone:
    persistence:
      enabled: true
      size: 50Gi

redis:
  enabled: true
  auth:
    enabled: true
    password: 'strong-redis-password'
  standalone:
    persistence:
      enabled: true
      size: 4Gi

persistence:
  enabled: true
  uploads:
    size: 50Gi
  functions:
    size: 20Gi
  builds:
    size: 20Gi
  sites:
    size: 20Gi

backup:
  enabled: true
  schedule: '0 3 * * *'
  s3:
    endpoint: https://s3.amazonaws.com
    bucket: appwrite-backups
    existingSecret: appwrite-s3-backup-credentials

ingress:
  enabled: true
  ingressClassName: traefik
  annotations:
    cert-manager.io/cluster-issuer: letsencrypt-prod
  hosts:
    - host: appwrite.example.com
      paths:
        - path: /
          pathType: Prefix
  tls:
    - secretName: appwrite-tls
      hosts:
        - appwrite.example.com

Configuration Reference

Core

ParameterTypeDefaultDescription
nameOverridestring""Override the chart name.
fullnameOverridestring""Override the full release name.
commonLabelsobject{}Extra labels added to all resources.

Images

ParameterTypeDefaultDescription
image.repositorystringdocker.io/appwrite/appwriteAppwrite server image.
image.tagstring"1.8.1"Appwrite image tag.
console.image.repositorystringdocker.io/appwrite/consoleConsole image.
console.image.tagstring"7.5.7"Console image tag.

Appwrite Configuration

ParameterTypeDefaultDescription
appwrite.domainstring""Public domain. Auto-detected from Ingress if empty.
appwrite.openSslKeyV1string""64-char hex encryption key. Auto-generated if empty. Never rotate.
appwrite.existingSecretstring""Existing secret with the OpenSSL key and JWT secret.
appwrite.existingSecretOpenSslKeystringappwrite-openssl-keyKey for the OpenSSL encryption key in the existing secret.
appwrite.existingSecretJwtKeystringappwrite-jwt-secretKey for the JWT secret in the existing secret.
appwrite.localestringenAppwrite locale (ISO 639-1).
appwrite.usageStatsstringenabledUsage statistics collection.
appwrite.graphqlstringenabledGraphQL API.
appwrite.storage.limitinteger30000000Maximum file upload size in bytes (default 30 MB).
appwrite.functions.timeoutinteger900Maximum function execution timeout in seconds.
appwrite.extraEnvarray[]Extra environment variables applied to all Appwrite pods.

SMTP

ParameterTypeDefaultDescription
appwrite.smtp.hoststring""SMTP server hostname.
appwrite.smtp.portstring""SMTP server port.
appwrite.smtp.securestring""SMTP security mode (tls, ssl, or empty).
appwrite.smtp.usernamestring""SMTP username.
appwrite.smtp.existingSecretstring""Existing secret with SMTP password.
appwrite.smtp.existingSecretPasswordKeystringsmtp-passwordKey for the SMTP password.

Services

Each service can be scaled independently:

ComponentParameterDefaultDescription
APIapi.replicaCount1API server replicas.
Realtimerealtime.replicaCount1Realtime WebSocket server replicas.

Workers

12 workers, each enabled by default at replicaCount: 1:

WorkerKeyDescription
auditsworkers.auditsAudit log processing.
webhooksworkers.webhooksOutbound webhook delivery.
deletesworkers.deletesSoft-delete cleanup.
databasesworkers.databasesDatabase event processing.
buildsworkers.buildsFunction build jobs.
certificatesworkers.certificatesSSL certificate management.
functionsworkers.functionsFunction invocation worker.
mailsworkers.mailsEmail delivery.
messagingworkers.messagingSMS/push notification delivery.
migrationsworkers.migrationsData migration worker.
statsResourcesworkers.statsResourcesResource usage statistics.
statsUsageworkers.statsUsageAPI usage statistics.

Each worker accepts enabled, replicaCount, and resources fields.

Database

External MariaDB requires root credentials for schema migrations

Appwrite runs its own schema migrations on startup using MariaDB root access. Unlike typical applications, providing only an app-level user is not sufficient. Supply the root password via database.external.existingSecret.

ParameterTypeDefaultDescription
database.modestringautoDatabase mode: auto or external.
database.external.hoststring""External MariaDB hostname.
database.external.rootUserstringrootRoot username for schema migrations.
database.external.existingSecretstring""Existing secret with root password.
database.external.existingSecretPasswordKeystringmariadb-root-passwordKey for the root password in the existing secret.

Persistence

All PVCs are shared across workers and the API. If api.replicaCount > 1, the storage class must support ReadWriteMany.

ParameterTypeDefaultDescription
persistence.enabledbooleantrueCreate shared PVCs for all Appwrite data.
persistence.storageClassstring""StorageClass. Use RWX class for multi-replica setups.
persistence.accessModesarray[ReadWriteOnce]PVC access modes.
persistence.uploads.sizestring10GiUploads storage.
persistence.cache.sizestring2GiCache storage.
persistence.certificates.sizestring1GiSSL certificate storage.
persistence.functions.sizestring5GiFunction code storage.
persistence.builds.sizestring5GiFunction build storage.
persistence.sites.sizestring5GiSites/hosting storage.

Backup

ParameterTypeDefaultDescription
backup.enabledbooleanfalseEnable scheduled mysqldump S3 backup.
backup.schedulestring"0 3 * * *"Cron schedule.
backup.s3.endpointstring""S3-compatible endpoint URL.
backup.s3.bucketstring""Target bucket name.
backup.s3.existingSecretstring""Existing secret with S3 credentials.
backup.database.mysqldumpArgsstring--single-transaction --routines --triggersExtra mysqldump arguments.

Ingress

ParameterTypeDefaultDescription
ingress.enabledbooleanfalseEnable an Ingress resource.
ingress.ingressClassNamestringtraefikIngress class name.
ingress.annotationsobject{}Annotations for the Ingress.
ingress.hostsarrayHost and path rules. Console: /, API: /v1.
ingress.tlsarray[]TLS configuration.

More Information