Run OpenCloud file sharing and collaboration on Kubernetes,
with collaborative editing, optional CalDAV/CardDAV and full-text search.
The chart deploys OpenCloud as a single-process StatefulSet with persistent configuration and data. The yjs server is enabled by default. Radicale and Apache Tika are optional companions; identity providers, office servers and other external services are configured through values.
Note
CI checks chart rendering, values validation, manifest schemas, policies and generated documentation. It does not run the containers. A separate kind-based end-to-end suite is available; validate the integrations against your own infrastructure before production use.
| Storage | Local files or S3-compatible object storage, with persistent local metadata |
| Identity | Built-in identity services or external OIDC, with optional external LDAP and configurable role mapping |
| Collaboration | yjs editing, external WOPI office integration, optional Radicale and Apache Tika |
| Exposure | Ingress or Gateway API HTTPRoute; configurable proxy routes and Content Security Policy |
| Security | Non-root containers, read-only root filesystems, dropped capabilities, no ServiceAccount token and images pinned by digest |
| Observability | Health probes, Prometheus metrics, optional bearer token and ServiceMonitor |
| GitOps | No Helm hooks; existing Secrets for credentials and optional externally managed service secrets |
| Release tooling | OCI publishing to GHCR, cosign signing and Renovate configuration |
- A Kubernetes cluster and Helm; CI uses Helm 4.
- Persistent storage for configuration and data, or existing claims.
- A public hostname with DNS, HTTPS termination and a certificate trusted by browsers and OpenCloud. The hostname must also resolve and be reachable from inside the cluster.
- External services for the integrations you enable: OIDC/LDAP, S3, SMTP, an office server or an antivirus scanner.
- Optional: Gateway API CRDs for HTTPRoute, Prometheus Operator CRDs for ServiceMonitor, and a NetworkPolicy-capable CNI for Radicale's access restriction.
From a checkout, create the namespace, initial administrator Secret and TLS Secret:
kubectl create namespace opencloud
kubectl --namespace opencloud create secret generic opencloud-admin \
--from-literal=adminPassword="$(openssl rand -base64 24)"
kubectl --namespace opencloud create secret tls opencloud-tls \
--cert=/path/to/fullchain.pem --key=/path/to/privkey.pemThe certificate must cover your public hostname. Save these settings as my-values.yaml, replacing the hostname and ingress class for your cluster:
host: cloud.example.org
admin:
existingSecret: opencloud-admin
ingress:
enabled: true
className: nginx
tls:
- secretName: opencloud-tls
hosts: [cloud.example.org]helm install opencloud . --namespace opencloud -f my-values.yamlBitnami Common is vendored in charts/, so a checkout needs no dependency download. To install a published version, replace . with oci://ghcr.io/comexos/charts/opencloud and add --version 0.1.0.
The chart validates required settings and credential references during rendering. It cannot verify that Secrets exist, storage can be provisioned or external services are reachable. For an example with Keycloak, S3, SMTP and Collabora, see examples/keycloak-s3.yaml.
The release workflow signs published chart digests with cosign:
cosign verify ghcr.io/comexos/charts/opencloud:0.1.0 \
--certificate-identity-regexp '^https://github\.com/comexos/opencloud-chart/\.github/workflows/release\.yml@refs/tags/v' \
--certificate-oidc-issuer https://token.actions.githubusercontent.comOpen https://cloud.example.org and sign in as admin with the password stored in opencloud-admin. Retrieve it with:
kubectl --namespace opencloud get secret opencloud-admin \
-o jsonpath='{.data.adminPassword}' | base64 --decodeOn first start, an init container runs opencloud init and writes service secrets, IDs and the initial administrator password to /etc/opencloud/opencloud.yaml on the config volume. Later starts reuse that file. The administrator Secret supplies the initial password; later password changes belong in OpenCloud's account management.
Preserve the config volume together with the data volume. Its generated secrets and identifiers are needed to access stored data. To supply those values yourself from the outset, use service secrets.
Every option is listed under Values. The sections below explain how the settings fit together.
All four container images are pinned by digest in values.yaml. Changing a tag alone does not change the image: set the corresponding digest too, or clear it to use the tag. This applies to image, yjs.image, radicale.image and tika.image. global.imageRegistry replaces their registries; a mirror must contain every enabled image.
The main image uses opencloudeu/opencloud-rolling. Renovate is configured to update image tags and digests together, along with appVersion for OpenCloud. Keep image fields in the order registry, repository, tag, digest so its matching rules continue to work.
Prefer existing Secrets. Plain credential values are stored in Helm release history even when rendered into the chart-managed <fullname>-env Secret.
| Credential | Existing Secret settings |
|---|---|
| Initial administrator | admin.existingSecret, .passwordSecretKey |
| Internal service secrets and IDs | serviceSecrets.existingSecret (fixed keys; see below) |
| LDAP bind | identity.ldap.existingSecret, .bindPasswordSecretKey |
| S3 | storage.s3.existingSecret, .accessKeySecretKey, .secretKeySecretKey |
| SMTP | smtp.existingSecret, .passwordSecretKey |
| Metrics token | metrics.token.existingSecret, .secretKey |
| Image pulls | global.imagePullSecrets or each image's pullSecrets |
Top-level existingSecret adds all keys of a Secret as environment variables. extraEnv and extraEnvVars set any other OpenCloud environment variable; an entry with the same name as a chart-set variable replaces it. When both specify the same name, extraEnvVars takes precedence over extraEnv. Names within extraEnvVars must be unique; duplicate names fail rendering. The order of extraEnvVars is preserved for Kubernetes variable references such as $(NAME).
identity.mode: builtin runs OpenCloud's own LDAP (idm) and OpenID Connect provider (idp). It needs an https:// public URL, since the built-in idp refuses other issuers.
identity.mode: oidc disables the built-in idp and trusts identity.oidc.issuer. Register a public client for the web UI (identity.oidc.webClientId, default web) and the upstream client IDs OpenCloudDesktop, OpenCloudAndroid and OpenCloudIOS for the apps. Roles come from the roles claim (see Keycloak claims). Set identity.oidc.audiences to enable access-token audience validation. Common identity configurations are:
| Setup | Chart values |
|---|---|
| External IdP with autoprovisioning | identity.oidc.autoprovision: true (default). Users live in the built-in idm, or in an external LDAP with identity.ldap.writeEnabled: true. |
| Keycloak with a shared user directory | identity.oidc.autoprovision: false, userClaim: uuid, userCs3Claim: userid, and identity.ldap with userSchemaId/groupSchemaId: entryUUID, disableUserMechanism: none, writeEnabled: false, serverUuid: true. See ci/full.yaml. |
Setting identity.ldap.uri replaces the built-in idm with that directory. The chart does not deploy Keycloak or LDAP and does not create IdP clients, users or roles.
Federating LDAP in Keycloak does not by itself put the claims OpenCloud needs into the tokens. In the shared-directory setup, configure Keycloak so that the access token and the userinfo response of every OpenCloud client carry:
uuid: the value OpenCloud uses as the user ID, which is the LDAP attribute named inidentity.ldap.userSchemaId(entryUUIDabove). In Keycloak, add a user-attribute LDAP mapper fromentryUUIDto a user attributeuuid(always read from LDAP), and a User Attribute protocol mapper fromuuidto the claimuuidin a client scope assigned to the clients. Keycloak's own user ID is not the LDAPentryUUIDunless the federation's UUID attribute makes it so; do not rely onsub.roles: a multivalued claim containing the role names. OpenCloud's default mapping turnsopencloudAdmin,opencloudSpaceAdmin,opencloudUserandopencloudGuestinto its admin, space admin, user and user-light roles. Grant them as realm roles, directly, through groups, or with a role LDAP mapper from LDAP groups, and map them with a User Realm Role protocol mapper to the claimroles. For other names setidentity.oidc.roleAssignment.mapping; OpenCloud reads that mapping only from its config file, so the chart renders it intoproxy.yaml.
tests/e2e/realm.json is a minimal working realm with these mappers, used by the end-to-end test.
By default opencloud init generates OpenCloud's internal secrets and IDs into /etc/opencloud/opencloud.yaml on the config volume. For GitOps, provide them instead with serviceSecrets.existingSecret: the init container is skipped, nothing is generated, and every value is read from the Secret. This mode is supported with identity.mode: oidc and an external identity.ldap.uri only; the built-in idm and idp need further generated credentials and signing keys, and the chart rejects those combinations.
Create the Secret once per installation and store it like any other secret (SOPS, Sealed Secrets, an external secret store). Keep these values stable for the lifetime of the installation:
scripts/service-secrets.py generate --name opencloud-service-secrets > opencloud-service-secrets.yamlThe script uses the operating system's cryptographically secure random generator, writes random version-4 UUIDs for the IDs, and only prints; Helm never generates or rotates these values. Helm also cannot check the Secret's contents when rendering: a missing key fails at pod start, a wrong value at runtime.
| Key | Environment variable | Purpose |
|---|---|---|
storageUsersMountId |
STORAGE_USERS_MOUNT_ID, GATEWAY_STORAGE_USERS_MOUNT_ID |
ID of the user storage; part of the resource ID of every space and file |
systemUserId |
OC_SYSTEM_USER_ID |
ID of the internal system user that stores system metadata such as shares and settings |
graphApplicationId |
GRAPH_APPLICATION_ID |
Application ID that OpenCloud's app roles belong to |
serviceAccountId |
OC_SERVICE_ACCOUNT_ID (also the settings service's service-account list) |
Account the services use to act on their own behalf |
serviceAccountSecret |
OC_SERVICE_ACCOUNT_SECRET |
Credential of that account |
jwtSecret |
OC_JWT_SECRET |
Signs and verifies internal access tokens |
machineAuthApiKey |
OC_MACHINE_AUTH_API_KEY |
Authenticates internal service-to-service requests |
systemUserApiKey |
OC_SYSTEM_USER_API_KEY |
Credential of the system user |
transferSecret |
OC_TRANSFER_SECRET |
Signs upload and download transfer tokens |
urlSigningSecret |
OC_URL_SIGNING_SECRET |
Signs URLs, for example image downloads by the office suite |
wopiSecret |
COLLABORATION_WOPI_SECRET |
Signs and encrypts WOPI tokens for the office integration |
thumbnailsTransferToken |
THUMBNAILS_TRANSFER_TOKEN |
Signs thumbnail download tokens |
With service secrets supplied externally, the config volume is not needed for generated credentials; persistence.config.enabled: false replaces its volume with an emptyDir. The chart's own files (csp.yaml, proxy.yaml, apps.yaml and the others) are still mounted into it read-only.
storage.driver selects where file content lives. Choose it before the first start: switching drivers does not migrate data.
| Driver | File content | Notes |
|---|---|---|
posix (default) |
Data volume | Upstream default |
decomposed |
Data volume | Alternative local storage layout |
decomposeds3 |
S3 bucket | Metadata stays on the data volume; set storage.s3 |
Whatever the driver, /var/lib/opencloud holds local service state, including NATS JetStream, the search index, thumbnails, metadata and the built-in idm database when used, so disabling persistence loses local state on pod replacement, even if file content remains in S3. The chart supports exactly one OpenCloud replica and does not provide a distributed deployment. Volumes are ReadWriteOnce by default; existingClaim adopts existing claims instead of the StatefulSet templates.
The <fullname> Service serves HTTP on service.port (default 9200); terminate TLS at the Ingress or Gateway. Set a body-size limit suitable for uploads on the controller, for example nginx.ingress.kubernetes.io/proxy-body-size: "0". gatewayAPI.httpRoute attaches to an existing Gateway instead.
OpenCloud calls its own public URL: uploads, for example, go through https://<host>/data. The host must resolve inside the cluster to the Ingress or Gateway, with a certificate the pod trusts. Use hostAliases for split-horizon setups. insecure: true skips certificate verification and is meant only for self-signed test installs.
The proxy also serves WOPI under /wopi and /collaboration, yjs under /yjs, and CalDAV/CardDAV under /caldav/, /carddav/ and their .well-known paths; no other hostnames are needed for these. proxy.additionalRoutes adds custom routes.
collaboration.enabled runs OpenCloud's collaboration (WOPI) service in the main process and points it at collaboration.app.url. Deploy the office server separately, for example with the Collabora Online chart, and allow OpenCloud as WOPI host there (aliasgroup1=https://<host>). The chart adds the office origin to the Content Security Policy.
For Euro-Office or OnlyOffice set collaboration.app.name, product: OnlyOffice and proofDisable: true, and supply an app registry that makes it the default for OOXML types through collaboration.appRegistry. Run only one office app: there is no locking between apps.
Public links require passwords by default (sharing.publicShareMustHavePassword: true). sharing.publicWriteableShareMustHavePassword can require passwords specifically on writable links when the general requirement is disabled. passwordPolicy controls public-link password strength and an optional banned-password list; it does not configure LDAP or OIDC account passwords.
These settings govern passwords, not whether public links may be created. The chart currently exposes no switch to disable public-link creation or enforce share expiry.
| Values | Effect |
|---|---|
smtp.enabled |
Email notifications. smtp.sender is required; the notifications service exits without it. |
tika.enabled or tika.externalUrl |
Full-text search through a bundled or external Apache Tika server. |
radicale.enabled |
Personal calendar and address book per user, authenticated by the OpenCloud proxy. Data on its own volume. |
yjs.enabled (default true) |
Real-time collaborative editing in the web UI through the bundled yjs server. |
antivirus.enabled |
Scans uploads through an external ClamAV (clamavSocket: tcp://host:3310) or ICAP server. Keep the scanner's stream limit at least antivirus.maxScanSize. |
web.appsConfig |
Configuration of web extensions, rendered into apps.yaml. Install extensions into /var/lib/opencloud/web/assets/apps with extraInitContainers. |
csp.extraDirectives |
Additional Content Security Policy sources, for example for web extensions that embed other sites. |
Health probes use /healthz and /readyz on the proxy's debug port 9205, which is always bound. The web port answers every path with the web UI, so it cannot serve as a health check. Readiness reflects the proxy only; backend services may still be starting for a few seconds after the pod turns ready.
metrics.enabled adds a <fullname>-metrics Service for /metrics on the same port, and metrics.serviceMonitor.enabled a ServiceMonitor (requires the Prometheus Operator CRDs). In single-process mode this endpoint covers all services. Set metrics.token to require a bearer token for /metrics; health endpoints stay open.
The ServiceMonitor and its authentication Secrets always live in the Helm release namespace. Configure Prometheus's serviceMonitorNamespaceSelector to discover that namespace and its serviceMonitorSelector to match the monitor's labels; Prometheus itself can stay in a separate monitoring namespace.
All containers run as non-root with a read-only root filesystem, RuntimeDefault seccomp, no privilege escalation and all capabilities dropped. ServiceAccount token automounting is disabled; the chart grants no Kubernetes RBAC permissions. When Radicale is enabled, the chart installs an ingress NetworkPolicy allowing TCP port 5232 only from this release's OpenCloud server pods in the same namespace, because Radicale trusts their X-Remote-User header. This requires a network plugin that enforces NetworkPolicy; other policies must not grant broader access to Radicale, since allow rules are additive. Port 9205 also serves metrics and is reachable on the pod IP: restrict it and the other companions with your own NetworkPolicies.
- Backups: back up config and data volumes together, plus the S3 bucket when using
decomposeds3and the Radicale volume when enabled. Stop services or use a consistent snapshot, and test restoration. - Secrets: restart the affected pods after changing an externally managed Secret used as environment variables. Keep internal service secrets and IDs stable.
- Persistence: check PVC retention before uninstalling. Resource and claim names depend on the release name and
fullnameOverride. - Resources: OpenCloud requests 250m CPU and 512Mi memory by default. Tune resources from measured load; the startup probe allows five minutes.
- GitOps: the chart has no hooks. Install any required Gateway API or ServiceMonitor CRDs before applying its rendered resources.
Test browser sign-in, user and role mapping, uploads and downloads, TLS, and backup restoration against your infrastructure. If enabled, also test office editing, antivirus handling, email delivery, calendar/address-book access and full-text search results. Rendering checks cannot establish runtime correctness.
Run local checks with Helm, Python and Docker:
pip install --require-hashes -r ci/requirements.txt
scripts/render-matrix.sh
scripts/validate-manifests.sh
python3 tests/render.py
python3 tests/e2e/test_harness.py
yamllint -c .yamllint.yaml .
scripts/generate-docs.sh --checkCI also checks workflow syntax and the values schema. Optional local hooks are configured in .pre-commit-config.yaml.
tests/e2e/run.sh runs a separate suite on a throwaway kind cluster with Keycloak and OpenLDAP. It exercises token-based sign-in, roles, LDAP groups, file transfers, companions, persistent state and externally managed service secrets. It requires kind, kubectl, Helm, Docker and Python and is not run by CI. The harness unit tests above do not start a cluster.
policies/kyverno/ contains optional ValidatingPolicy resources for pod hardening, image digests and resource requests. The chart does not install them. They use Deny actions without a namespace restriction; scope them or select audit actions before applying them to a shared cluster.
This README is generated from README.md.gotmpl and the # -- comments in values.yaml by helm-docs. Edit those sources and run scripts/generate-docs.sh. CI checks that the generated README is current.
Bitnami Common is vendored in charts/. Keep Chart.yaml, Chart.lock and the dependency archive in sync; helm dependency build . restores the locked dependency.
Push a vX.Y.Z tag matching version in Chart.yaml to run CI, package the chart, publish it to oci://ghcr.io/comexos/charts/opencloud and sign its digest with cosign. Forks publish under their repository owner's GHCR namespace.
All configuration values and their defaults
| Key | Type | Default | Description |
|---|---|---|---|
| additionalAnnotations | object | {} |
Annotations added to every resource. |
| additionalLabels | object | {} |
Labels added to every resource. |
| additionalServices | list | [] |
Extra services started inside the single OpenCloud process (OC_ADD_RUN_SERVICES). The chart adds notifications, collaboration and antivirus itself when those features are enabled. |
| admin.existingSecret | string | "" |
Existing Secret holding the initial administrator password. |
| admin.password | string | "" |
Fallback only, stored in a chart-managed Secret and visible in Helm's release history. Prefer existingSecret. |
| admin.passwordSecretKey | string | "adminPassword" |
|
| affinity | object | {} |
|
| antivirus.clamavSocket | string | "" |
clamd address, e.g. tcp://clamav.antivirus.svc.cluster.local:3310. |
| antivirus.enabled | bool | false |
|
| antivirus.icapUrl | string | "" |
ICAP URL, e.g. icap://icap.antivirus.svc.cluster.local:1344. |
| antivirus.infectedFileHandling | string | "abort" |
abort, continue or delete. |
| antivirus.maxScanSize | string | "100MB" |
|
| antivirus.maxScanSizeMode | string | "partial" |
partial scans only the first maxScanSize bytes; skip does not scan larger files. (OpenCloud's own description says "truncate", but the code only accepts partial and skip.) |
| antivirus.scanner | string | "clamav" |
clamav or icap |
| antivirus.workers | int | 1 |
|
| archiverMaxSize | string | "10000000000" |
Maximum size in bytes of a folder download archive (FRONTEND_ARCHIVER_MAX_SIZE). |
| basicAuth | bool | false |
Allows HTTP Basic authentication (PROXY_ENABLE_BASIC_AUTH), for WebDAV clients without OpenID Connect support. Not recommended. |
| checkForUpdates | bool | true |
Lets clients check for updates (FRONTEND_CHECK_FOR_UPDATES). |
| collaboration.app.icon | string | "" |
Icon URL; defaults to /favicon.ico. |
| collaboration.app.insecure | bool | false |
Skips TLS verification of the office server. |
| collaboration.app.name | string | "CollaboraOnline" |
Display name and app registry name (COLLABORATION_APP_NAME), e.g. CollaboraOnline or Euro-Office. |
| collaboration.app.product | string | "Collabora" |
Collabora, OnlyOffice (also for Euro-Office) or Microsoft (COLLABORATION_APP_PRODUCT). |
| collaboration.app.proofDisable | bool | false |
Disables WOPI proof key validation, as needed for Euro-Office. |
| collaboration.app.url | string | "" |
Public URL of the office server (COLLABORATION_APP_ADDR). Required. |
| collaboration.appRegistry | string | "" |
Raw app-registry.yaml (mime types and default apps) mounted into /etc/opencloud; empty keeps OpenCloud's built-in registry. |
| collaboration.enabled | bool | false |
|
| collaboration.secureView | bool | true |
Makes the office app the secure-view app and offers the secure-view share role. Collabora only. |
| containerSecurityContext.allowPrivilegeEscalation | bool | false |
|
| containerSecurityContext.capabilities.drop[0] | string | "ALL" |
|
| containerSecurityContext.enabled | bool | true |
|
| containerSecurityContext.privileged | bool | false |
|
| containerSecurityContext.readOnlyRootFilesystem | bool | true |
|
| containerSecurityContext.runAsNonRoot | bool | true |
|
| csp.extraDirectives | object | {} |
Extra sources appended per directive, e.g. {frame-src: ["https://embed.diagrams.net/"]}. |
| csp.override | string | "" |
Replaces the generated csp.yaml entirely when set. |
| defaultLanguage | string | "" |
Default language of services and the web UI as an ISO 639-1 code (OC_DEFAULT_LANGUAGE). Empty means English. |
| demoUsers | bool | false |
Creates the public demo accounts (alan, mary, margaret, dennis, lynn; password "demo"). Never on production. |
| domain | string | "" |
Convenience base domain: when set and host is left empty, the public hostname becomes cloud.. |
| excludeServices | list | [] |
Services excluded from the OpenCloud process (OC_EXCLUDE_RUN_SERVICES). The chart excludes idp, and idm with an external LDAP, in OIDC mode. |
| existingSecret | string | "" |
Existing Secret whose keys are all injected as environment variables (envFrom). |
| externalUrl | string | "" |
Full public URL (OC_URL) when it is not https://, for example with a non-standard port. Every browser-facing URL derives from it. |
| extraEnv | object | {} |
Extra environment variables as plain name/value pairs, for any OpenCloud setting not exposed above. |
| extraEnvVars | list | [] |
Extra environment variables as full Kubernetes env entries (for example valueFrom.secretKeyRef). Takes precedence over extraEnv; names must be unique. |
| extraInitContainers | list | [] |
Extra init containers, for example to copy web extensions into /var/lib/opencloud/web/assets/apps on the data volume. |
| extraSecretEnv | object | {} |
Extra environment variables from the chart-managed Secret. |
| extraVolumeMounts | list | [] |
|
| extraVolumes | list | [] |
|
| fullnameOverride | string | "" |
Overrides the full resource name used by the chart. |
| gatewayAPI.httpRoute.annotations | object | {} |
|
| gatewayAPI.httpRoute.enabled | bool | false |
|
| gatewayAPI.httpRoute.hostnames | list | [] |
Defaults to host. |
| gatewayAPI.httpRoute.parentRefs | list | [] |
Existing Gateway listeners to attach to. |
| global.defaultStorageClass | string | "" |
Default storage class when a persistence storageClass is unset. |
| global.imagePullSecrets | list | [] |
|
| global.imageRegistry | string | "" |
|
| host | string | "" |
Public hostname of OpenCloud, used for OC_URL, Ingress, HTTPRoute and the WOPI source. Defaults to cloud.. |
| hostAliases | list | [] |
OpenCloud calls its own public URL (uploads go through https:///data), so the host must resolve and be reachable from the pod. Use hostAliases when cluster DNS does not resolve it to the Ingress or Gateway. |
| identity.ldap.bindDn | string | "" |
|
| identity.ldap.bindPassword | string | "" |
Fallback only, stored in a chart-managed Secret. Prefer existingSecret. |
| identity.ldap.bindPasswordSecretKey | string | "bindPassword" |
|
| identity.ldap.disableUserMechanism | string | "attribute" |
How disabled users are detected: none, attribute or group. |
| identity.ldap.existingSecret | string | "" |
Existing Secret holding the bind password. |
| identity.ldap.groupBaseDn | string | "" |
|
| identity.ldap.groupCreateBaseDn | string | "" |
Subtree for groups created in OpenCloud (GRAPH_LDAP_GROUP_CREATE_BASE_DN). Empty uses groupBaseDn. |
| identity.ldap.groupFilter | string | "" |
|
| identity.ldap.groupSchemaId | string | "opencloudUUID" |
|
| identity.ldap.insecure | bool | false |
Skips LDAP certificate verification (OC_LDAP_INSECURE). |
| identity.ldap.refintEnabled | bool | false |
The server keeps group memberships consistent (GRAPH_LDAP_REFINT_ENABLED). |
| identity.ldap.serverUuid | bool | false |
The LDAP server generates the ID attribute (GRAPH_LDAP_SERVER_UUID), as with entryUUID. |
| identity.ldap.uri | string | "" |
e.g. ldaps://openldap.identity.svc.cluster.local:636. Disables the built-in idm when set. |
| identity.ldap.userBaseDn | string | "" |
|
| identity.ldap.userFilter | string | "(objectclass=inetOrgPerson)" |
|
| identity.ldap.userSchemaId | string | "opencloudUUID" |
Attribute holding the immutable user ID: opencloudUUID with the OpenCloud LDAP schema, entryUUID for a directory managed by Keycloak. |
| identity.ldap.writeEnabled | bool | true |
OpenCloud may create and change users and groups (OC_LDAP_SERVER_WRITE_ENABLED). |
| identity.mode | string | "builtin" |
builtin or oidc |
| identity.oidc.accountUrl | string | "" |
Account management page linked from the web UI (WEB_OPTION_ACCOUNT_EDIT_LINK_HREF). |
| identity.oidc.assignDefaultUserRole | bool | false |
Assigns the user role to new users (GRAPH_ASSIGN_DEFAULT_USER_ROLE); set it with roleAssignment.driver default. |
| identity.oidc.audiences | list | [] |
Allowed access-token audiences (PROXY_OIDC_AUDIENCES). Recommended for production; only honoured by OpenCloud 8.x and later. |
| identity.oidc.autoprovision | bool | true |
Creates users in OpenCloud's LDAP on their first login (PROXY_AUTOPROVISION_ACCOUNTS). Requires a writable LDAP: the built-in idm or ldap.writeEnabled. |
| identity.oidc.autoprovisionClaimUsername | string | "sub" |
Claim stored as the username of autoprovisioned accounts. |
| identity.oidc.issuer | string | "" |
Issuer URL including the realm, e.g. https://keycloak.example.org/realms/openCloud. |
| identity.oidc.roleAssignment.claim | string | "roles" |
Claim holding the role names (PROXY_ROLE_ASSIGNMENT_OIDC_CLAIM). |
| identity.oidc.roleAssignment.driver | string | "oidc" |
oidc maps a claim to OpenCloud roles; default gives every user the user role. |
| identity.oidc.roleAssignment.mapping | list | [] |
Claim values mapped to OpenCloud roles, e.g. [{role_name: admin, claim_value: opencloud-admins}]. Empty keeps the upstream mapping: opencloudAdmin, opencloudSpaceAdmin, opencloudUser and opencloudGuest to admin, spaceadmin, user and user-light. OpenCloud reads this only from its config file, so the chart renders it into proxy.yaml. |
| identity.oidc.userClaim | string | "sub" |
Claim that identifies the user (PROXY_USER_OIDC_CLAIM). |
| identity.oidc.userCs3Claim | string | "username" |
User attribute the claim is matched against (PROXY_USER_CS3_CLAIM): username, userid or mail. |
| identity.oidc.webClientId | string | "web" |
Client ID of the web UI (WEB_OIDC_CLIENT_ID). The desktop and mobile apps use their upstream client IDs (OpenCloudDesktop, OpenCloudAndroid, OpenCloudIOS) unless overridden through extraEnv. |
| identity.oidc.webScopes | string | "openid profile email" |
Scopes requested by the web UI (WEB_OIDC_SCOPE). |
| image.digest | string | pinned, see values.yaml | Multi-arch index digest of the tag. Takes precedence over the tag; clear it to float on the tag instead. |
| image.pullPolicy | string | "IfNotPresent" |
|
| image.pullSecrets | list | [] |
|
| image.registry | string | "docker.io" |
|
| image.repository | string | "opencloudeu/opencloud-rolling" |
|
| image.tag | string | pinned, see values.yaml | Image tag, kept in sync with the digest by Renovate. |
| ingress.annotations | object | {} |
Large uploads need a body size limit on the controller, e.g. nginx.ingress.kubernetes.io/proxy-body-size: "0". |
| ingress.className | string | "" |
|
| ingress.enabled | bool | false |
|
| ingress.tls | list | [] |
TLS entries; the host is taken from host. |
| initResources | object | {"requests":{"cpu":"50m","memory":"64Mi"}} |
Resources of the init container that runs opencloud init. |
| insecure | bool | false |
Maps to OC_INSECURE. Skips TLS verification for OpenCloud's own calls to its public URL, the IdP and office apps. Only for self-signed test setups. |
| livenessProbe.enabled | bool | true |
|
| livenessProbe.failureThreshold | int | 3 |
|
| livenessProbe.initialDelaySeconds | int | 0 |
|
| livenessProbe.periodSeconds | int | 10 |
|
| livenessProbe.successThreshold | int | 1 |
|
| livenessProbe.timeoutSeconds | int | 5 |
|
| logLevel | string | "info" |
Maps to OC_LOG_LEVEL: panic, fatal, error, warn, info, debug or trace. |
| logPretty | bool | false |
Human-readable, colored logs instead of JSON (OC_LOG_PRETTY, OC_LOG_COLOR). |
| metrics.enabled | bool | false |
|
| metrics.serviceMonitor.additionalLabels | object | {} |
|
| metrics.serviceMonitor.annotations | object | {} |
|
| metrics.serviceMonitor.enabled | bool | false |
|
| metrics.serviceMonitor.honorLabels | bool | false |
|
| metrics.serviceMonitor.interval | string | "30s" |
|
| metrics.serviceMonitor.metricRelabelings | list | [] |
|
| metrics.serviceMonitor.relabelings | list | [] |
|
| metrics.serviceMonitor.scrapeTimeout | string | "" |
|
| metrics.token.existingSecret | string | "" |
|
| metrics.token.secretKey | string | "token" |
|
| metrics.token.value | string | "" |
Fallback only, stored in a chart-managed Secret. Prefer existingSecret. |
| nameOverride | string | "" |
Overrides the chart name used in resource names. |
| nodeSelector | object | {} |
|
| passwordPolicy.bannedPasswords | list | [] |
Passwords rejected by the policy, one per entry. Rendered into banned-password-list.txt; empty disables the list. |
| passwordPolicy.disabled | bool | false |
|
| passwordPolicy.minCharacters | int | 8 |
|
| passwordPolicy.minDigits | int | 1 |
|
| passwordPolicy.minLowercaseCharacters | int | 1 |
|
| passwordPolicy.minSpecialCharacters | int | 1 |
|
| passwordPolicy.minUppercaseCharacters | int | 1 |
|
| persistence.config.accessMode | string | "ReadWriteOnce" |
|
| persistence.config.enabled | bool | true |
Persistent volume for /etc/opencloud. Required unless serviceSecrets.existingSecret provides the service secrets; then an emptyDir suffices. Choose the volume layout before installation. |
| persistence.config.existingClaim | string | "" |
Use an existing claim instead of the StatefulSet volume claim template. |
| persistence.config.size | string | "1Gi" |
|
| persistence.config.storageClass | string | "" |
|
| persistence.data.accessMode | string | "ReadWriteOnce" |
|
| persistence.data.existingClaim | string | "" |
Use an existing claim instead of the StatefulSet volume claim template. |
| persistence.data.size | string | "50Gi" |
|
| persistence.data.storageClass | string | "" |
|
| persistence.enabled | bool | true |
|
| podAnnotations | object | {} |
|
| podLabels | object | {} |
|
| podSecurityContext.enabled | bool | true |
|
| podSecurityContext.fsGroup | int | 1000 |
The published image's non-root user; adjust if a custom image uses a different UID/GID. |
| podSecurityContext.fsGroupChangePolicy | string | "OnRootMismatch" |
|
| podSecurityContext.runAsGroup | int | 1000 |
|
| podSecurityContext.runAsNonRoot | bool | true |
|
| podSecurityContext.runAsUser | int | 1000 |
|
| podSecurityContext.seccompProfile.type | string | "RuntimeDefault" |
|
| proxy.additionalRoutes | list | [] |
Extra routes appended to the proxy's default policy (proxy.yaml additional_policies), e.g. [{endpoint: /app/, backend: "http://app:8080", unprotected: true}]. |
| radicale.affinity | object | {} |
|
| radicale.config | string | "" |
Replaces the generated Radicale configuration file entirely when set. Keep [auth] type = http_x_remote_user: the proxy passes the user in X-Remote-User. |
| radicale.containerSecurityContext.allowPrivilegeEscalation | bool | false |
|
| radicale.containerSecurityContext.capabilities.drop[0] | string | "ALL" |
|
| radicale.containerSecurityContext.enabled | bool | true |
|
| radicale.containerSecurityContext.privileged | bool | false |
|
| radicale.containerSecurityContext.readOnlyRootFilesystem | bool | true |
|
| radicale.containerSecurityContext.runAsNonRoot | bool | true |
|
| radicale.enabled | bool | false |
|
| radicale.image.digest | string | pinned, see values.yaml | Multi-arch index digest of the tag. Takes precedence over the tag; clear it to float on the tag instead. |
| radicale.image.pullPolicy | string | "IfNotPresent" |
|
| radicale.image.pullSecrets | list | [] |
|
| radicale.image.registry | string | "docker.io" |
|
| radicale.image.repository | string | "opencloudeu/radicale" |
|
| radicale.image.tag | string | pinned, see values.yaml | Image tag, kept in sync with the digest by Renovate. |
| radicale.nodeSelector | object | {} |
|
| radicale.persistence.accessMode | string | "ReadWriteOnce" |
|
| radicale.persistence.enabled | bool | true |
|
| radicale.persistence.existingClaim | string | "" |
|
| radicale.persistence.size | string | "5Gi" |
|
| radicale.persistence.storageClass | string | "" |
|
| radicale.podAnnotations | object | {} |
|
| radicale.podLabels | object | {} |
|
| radicale.podSecurityContext.enabled | bool | true |
|
| radicale.podSecurityContext.fsGroup | int | 1000 |
|
| radicale.podSecurityContext.fsGroupChangePolicy | string | "OnRootMismatch" |
|
| radicale.podSecurityContext.runAsGroup | int | 1000 |
|
| radicale.podSecurityContext.runAsNonRoot | bool | true |
|
| radicale.podSecurityContext.runAsUser | int | 1000 |
|
| radicale.podSecurityContext.seccompProfile.type | string | "RuntimeDefault" |
|
| radicale.resources.requests.cpu | string | "50m" |
|
| radicale.resources.requests.memory | string | "64Mi" |
|
| radicale.tolerations | list | [] |
|
| readinessProbe.enabled | bool | true |
|
| readinessProbe.failureThreshold | int | 3 |
|
| readinessProbe.initialDelaySeconds | int | 0 |
|
| readinessProbe.periodSeconds | int | 10 |
|
| readinessProbe.successThreshold | int | 1 |
|
| readinessProbe.timeoutSeconds | int | 5 |
|
| resources | object | {"requests":{"cpu":"250m","memory":"512Mi"}} |
OpenCloud's requests are starting points; the single process runs every service, so tune them from measured load. |
| service.annotations | object | {} |
|
| service.ipFamilies | list | [] |
|
| service.ipFamilyPolicy | string | "" |
SingleStack, PreferDualStack or RequireDualStack. Empty keeps the cluster default. |
| service.port | int | 9200 |
|
| service.type | string | "ClusterIP" |
|
| serviceAccount.annotations | object | {} |
|
| serviceAccount.automountServiceAccountToken | bool | false |
|
| serviceAccount.create | bool | true |
|
| serviceAccount.labels | object | {} |
|
| serviceAccount.name | string | "" |
|
| serviceSecrets.existingSecret | string | "" |
Existing Secret with the keys jwtSecret, machineAuthApiKey, systemUserApiKey, systemUserId, transferSecret, urlSigningSecret, graphApplicationId, serviceAccountId, serviceAccountSecret, wopiSecret, thumbnailsTransferToken and storageUsersMountId. |
| sharing.publicShareMustHavePassword | bool | true |
OC_SHARING_PUBLIC_SHARE_MUST_HAVE_PASSWORD. |
| sharing.publicWriteableShareMustHavePassword | bool | false |
OC_SHARING_PUBLIC_WRITEABLE_SHARE_MUST_HAVE_PASSWORD. |
| smtp.authentication | string | "auto" |
login, plain, crammd5, none or auto. |
| smtp.enabled | bool | false |
|
| smtp.encryption | string | "starttls" |
starttls, ssltls or none. |
| smtp.existingSecret | string | "" |
Existing Secret holding the SMTP password. |
| smtp.host | string | "" |
|
| smtp.insecure | bool | false |
Skips certificate verification of the SMTP server. |
| smtp.password | string | "" |
Fallback only, stored in a chart-managed Secret. Prefer existingSecret. |
| smtp.passwordSecretKey | string | "password" |
|
| smtp.port | int | 587 |
|
| smtp.sender | string | "" |
Sender address, e.g. "OpenCloud noreply@example.org". Required: the notifications service exits without it. |
| smtp.username | string | "" |
|
| startupProbe.enabled | bool | true |
|
| startupProbe.failureThreshold | int | 60 |
|
| startupProbe.initialDelaySeconds | int | 0 |
|
| startupProbe.periodSeconds | int | 5 |
|
| startupProbe.successThreshold | int | 1 |
|
| startupProbe.timeoutSeconds | int | 5 |
|
| storage.driver | string | "posix" |
posix (upstream default, files on the data volume), decomposed, or decomposeds3 (metadata on the data volume, file content in S3). |
| storage.s3.accessKey | string | "" |
Fallback only, stored in a chart-managed Secret. Prefer existingSecret. Both are required together when existingSecret is not set. |
| storage.s3.accessKeySecretKey | string | "accessKey" |
|
| storage.s3.bucket | string | "" |
|
| storage.s3.endpoint | string | "" |
|
| storage.s3.existingSecret | string | "" |
Existing Secret with the access and secret key. |
| storage.s3.region | string | "default" |
|
| storage.s3.secretKey | string | "" |
|
| storage.s3.secretKeySecretKey | string | "secretKey" |
|
| terminationGracePeriodSeconds | int | 60 |
Seconds OpenCloud gets to shut down and flush NATS and the search index. |
| tika.affinity | object | {} |
|
| tika.containerSecurityContext.allowPrivilegeEscalation | bool | false |
|
| tika.containerSecurityContext.capabilities.drop[0] | string | "ALL" |
|
| tika.containerSecurityContext.enabled | bool | true |
|
| tika.containerSecurityContext.privileged | bool | false |
|
| tika.containerSecurityContext.readOnlyRootFilesystem | bool | true |
|
| tika.containerSecurityContext.runAsNonRoot | bool | true |
|
| tika.enabled | bool | false |
|
| tika.externalUrl | string | "" |
Existing Tika server, e.g. http://tika.search.svc.cluster.local:9998. Used instead of the bundled deployment. |
| tika.image.digest | string | pinned, see values.yaml | Multi-arch index digest of the tag. Takes precedence over the tag; clear it to float on the tag instead. |
| tika.image.pullPolicy | string | "IfNotPresent" |
|
| tika.image.pullSecrets | list | [] |
|
| tika.image.registry | string | "docker.io" |
|
| tika.image.repository | string | "apache/tika" |
|
| tika.image.tag | string | pinned, see values.yaml | Image tag, kept in sync with the digest by Renovate. |
| tika.nodeSelector | object | {} |
|
| tika.podAnnotations | object | {} |
|
| tika.podLabels | object | {} |
|
| tika.podSecurityContext.enabled | bool | true |
|
| tika.podSecurityContext.runAsGroup | int | 35002 |
|
| tika.podSecurityContext.runAsNonRoot | bool | true |
|
| tika.podSecurityContext.runAsUser | int | 35002 |
|
| tika.podSecurityContext.seccompProfile.type | string | "RuntimeDefault" |
|
| tika.resources.requests.cpu | string | "100m" |
|
| tika.resources.requests.memory | string | "512Mi" |
|
| tika.tolerations | list | [] |
|
| tolerations | list | [] |
|
| topologySpreadConstraints | list | [] |
|
| web.appsConfig | object | {} |
Configuration of installed web extensions, rendered into apps.yaml. |
| yjs.affinity | object | {} |
|
| yjs.containerSecurityContext.allowPrivilegeEscalation | bool | false |
|
| yjs.containerSecurityContext.capabilities.drop[0] | string | "ALL" |
|
| yjs.containerSecurityContext.enabled | bool | true |
|
| yjs.containerSecurityContext.privileged | bool | false |
|
| yjs.containerSecurityContext.readOnlyRootFilesystem | bool | true |
|
| yjs.containerSecurityContext.runAsNonRoot | bool | true |
|
| yjs.enabled | bool | true |
|
| yjs.extraEnv | object | {} |
|
| yjs.image.digest | string | pinned, see values.yaml | Multi-arch index digest of the tag. Takes precedence over the tag; clear it to float on the tag instead. |
| yjs.image.pullPolicy | string | "IfNotPresent" |
|
| yjs.image.pullSecrets | list | [] |
|
| yjs.image.registry | string | "docker.io" |
|
| yjs.image.repository | string | "opencloudeu/yjs" |
|
| yjs.image.tag | string | pinned, see values.yaml | Image tag, kept in sync with the digest by Renovate. |
| yjs.nodeSelector | object | {} |
|
| yjs.podAnnotations | object | {} |
|
| yjs.podLabels | object | {} |
|
| yjs.podSecurityContext.enabled | bool | true |
|
| yjs.podSecurityContext.runAsGroup | int | 1000 |
|
| yjs.podSecurityContext.runAsNonRoot | bool | true |
|
| yjs.podSecurityContext.runAsUser | int | 1000 |
|
| yjs.podSecurityContext.seccompProfile.type | string | "RuntimeDefault" |
|
| yjs.resources.requests.cpu | string | "50m" |
|
| yjs.resources.requests.memory | string | "128Mi" |
|
| yjs.shutdownGracePeriodMs | int | 15000 |
Milliseconds the server gets to flush documents on shutdown; keep it below terminationGracePeriodSeconds. |
| yjs.terminationGracePeriodSeconds | int | 20 |
|
| yjs.tolerations | list | [] |
Copyright 2026 Thomas Kaltenstein (Sovereign Systems). Licensed under the Apache License, Version 2.0; bundled third-party components are listed in NOTICE. The deployed images have their own licences.