Open-Xchange App Suite
OX App Suite is an enterprise email, calendar and collaboration platform.
This chart wraps the upstream appsuite Helm chart and adds Traefik ingress support.
Table of Contents
- Prerequisites
- Minimal values
- Traefik ingress
- Mail configuration
- Keycloak OIDC
- User provisioning
- Troubleshooting
Prerequisites
- External MariaDB/MySQL database
- Redis (bundled via
core-mw.redis) - Traefik as ingress controller (with
traefik-cert-managerIngressClass) - A wildcard TLS secret in the
traefiknamespace - An IMAP server — required. OX connects to IMAP on every login to read the folder namespace. If IMAP is unreachable, login fails with
Some mandatory configuration could not be loaded. (namespace).
Minimal values
traefik:
enabled: true
host: ox.example.com
appsuite:
ingress:
enabled: true
ingressClassName: traefik-cert-manager
annotations:
traefik.ingress.kubernetes.io/router.entrypoints: websecure
traefik.ingress.kubernetes.io/router.tls: "true"
appsuite:
hosts:
- ox.example.com
dav:
hosts:
- ox.example.com
tls:
enabled: false # Traefik wildcard handles TLS
routes:
http-api-routes-appsuite-api:
annotations:
traefik.ingress.kubernetes.io/router.priority: "200"
http-api-routes-ajax:
annotations:
traefik.ingress.kubernetes.io/router.priority: "200"
http-api-routes-api:
annotations:
traefik.ingress.kubernetes.io/router.priority: "200"
core-mw:
mysql:
host: "mariadb.open-xchange.svc.cluster.local"
port: "3306"
database: "open_xchange"
existingSecret: ox-core-mw-mysql-secret
auth:
user: "openexchange"
Traefik ingress
The upstream appsuite chart generates Ingresses with nginx-style path regex ((.*)) that Traefik
does not evaluate. The routes priority annotations in the minimal values above fix API path
routing for most cases.
This chart also provides an IngressRoute template that:
- Explicitly routes API paths (
/appsuite/api,/ajax,/api) at priority 200 - Redirects the bare root
/to the OIDC login flow automatically
Enable it via:
traefik:
enabled: true
host: ox.example.com
ingressRoute:
enabled: true # default: true
With ingressRoute.enabled: true, visiting https://ox.example.com/ redirects directly to
Keycloak without showing the local login form first.
Mail configuration
OX always connects to IMAP on login. There is no way to skip this step.
Production — Mailcow
Mailcow provides Dovecot IMAP + Postfix SMTP in one stack and is the recommended mail backend.
Step 1 — Create mailboxes in Mailcow
Each OX user needs a matching mailbox in Mailcow. Use the Mailcow admin UI or API:
curl -X POST https://<mailcow-host>/api/v1/add/mailbox \
-H "X-API-Key: <mailcow-api-key>" \
-H "Content-Type: application/json" \
-d '{
"local_part": "rishi",
"domain": "example.com",
"password": "<password>",
"quota": "1024",
"active": "1"
}'
Step 2 — Configure OX to use Mailcow
appsuite:
core-mw:
properties:
com.openexchange.mail.mailServer: "<mailcow-host>:993"
com.openexchange.mail.mailServerSource: "global"
com.openexchange.mail.loginSource: "mail"
com.openexchange.mail.passwordSource: "global"
com.openexchange.mail.masterPassword: "<mailcow-master-password>"
com.openexchange.mail.useSSL: "true"
com.openexchange.mail.transportServer: "<mailcow-host>:587"
com.openexchange.mail.transportServerSource: "global"
com.openexchange.mail.transport.useSSL: "true"
com.openexchange.imap.requireTls: "true"
com.openexchange.smtp.requireTls: "true"
loginSource: mail — OX sends the full email address (e.g. rishi@example.com) as the IMAP
username, which is what Mailcow expects.
passwordSource: global + masterPassword — required for OIDC users since they have no
session password. The master password must be configured in Mailcow as an admin override password.
Production — generic IMAP/SMTP
appsuite:
core-mw:
properties:
com.openexchange.mail.mailServer: "imap.example.com:993"
com.openexchange.mail.mailServerSource: "global"
com.openexchange.mail.loginSource: "mail"
com.openexchange.mail.passwordSource: "session"
com.openexchange.mail.useSSL: "true"
com.openexchange.mail.transportServer: "smtp.example.com:587"
com.openexchange.mail.transportServerSource: "global"
com.openexchange.mail.transport.useSSL: "true"
com.openexchange.imap.requireTls: "true"
com.openexchange.smtp.requireTls: "true"
Use passwordSource: session only when users log in with a password (not OIDC).
Testing — Dovecot + MailHog stub
For testing without a real mail server, deploy Dovecot (IMAP stub) and MailHog (SMTP stub).
Dovecot accepts any username with password admin. MailHog captures sent mail without delivering it.
Deploy both from k8s/<cluster>/dovecot/ in the config repo, then configure OX:
appsuite:
core-mw:
properties:
com.openexchange.mail.mailServer: "dovecot.open-xchange.svc.cluster.local:143"
com.openexchange.mail.mailServerSource: "global"
com.openexchange.mail.loginSource: "name"
com.openexchange.mail.passwordSource: "global"
com.openexchange.mail.masterPassword: "admin"
com.openexchange.mail.useSSL: "false"
com.openexchange.mail.transportServer: "mailhog.open-xchange.svc.cluster.local:1025"
com.openexchange.mail.transportServerSource: "global"
com.openexchange.mail.transport.useSSL: "false"
com.openexchange.imap.enableTls: "false"
com.openexchange.imap.requireTls: "false"
com.openexchange.smtp.requireTls: "false"
com.openexchange.capabilities.hardcoded.deny: "mailfilter"
Why mailfilter denied: OX tries to connect to Sieve (port 4190) on login. The Dovecot stub
has no Sieve, so denying this capability prevents a 500 error.
MailHog web UI: http://mailhog.open-xchange.svc.cluster.local:8025
Keycloak OIDC
Step 1 — Register a client in Keycloak
- Client ID:
open-xchange - Access Type:
confidential - Valid Redirect URIs:
https://ox.example.com/appsuite/*
Copy the client secret from the Credentials tab.
Step 2 — Seal the client secret
# oidc.properties — seal this as ox-oidc-secret
anywhere:
com.openexchange.oidc.clientSecret: "<client-secret>"
appsuite:
core-mw:
existingPropertiesSecret: ox-oidc-secret
Step 3 — Add OIDC properties
appsuite:
core-mw:
properties:
com.openexchange.oidc.enabled: "true"
com.openexchange.oidc.startDefaultBackend: "true"
com.openexchange.oidc.opIssuer: "https://keycloak.example.com/auth/realms/<realm>"
com.openexchange.oidc.opAuthorizationEndpoint: "https://keycloak.example.com/auth/realms/<realm>/protocol/openid-connect/auth"
com.openexchange.oidc.opTokenEndpoint: "https://keycloak.example.com/auth/realms/<realm>/protocol/openid-connect/token"
com.openexchange.oidc.opJwkSetEndpoint: "https://keycloak.example.com/auth/realms/<realm>/protocol/openid-connect/certs"
com.openexchange.oidc.clientId: "open-xchange"
com.openexchange.oidc.rpRedirectURIAuth: "https://ox.example.com/appsuite/api/oidc/auth"
com.openexchange.oidc.userLookupClaim: "email"
com.openexchange.oidc.userLookupNamePart: "local-part"
com.openexchange.oidc.contextLookupClaim: "email"
com.openexchange.oidc.contextLookupNamePart: "domain"
com.openexchange.secret.secretSource: "<user-id> + '@' + <context-id>"
User provisioning
OX requires users to exist in its own database before OIDC login works. OIDC does not auto-create users.
Add domain → context mapping (once per domain):
kubectl -n open-xchange exec open-xchange-mariadb-0 -- \
mariadb -u openexchange -p<password> open_xchange \
-e "INSERT IGNORE INTO login2context (login_info, cid) VALUES ('example.com', 1);"
Create a user:
kubectl -n open-xchange exec statefulset/open-xchange-core-mw-default -- \
/opt/open-xchange/sbin/createuser \
-A <master-admin> -P <master-password> \
-c 1 \
-u <username> \
-e <username>@example.com \
-p admin \
-g <firstname> \
-s <lastname> \
-d "<Display Name>"
Troubleshooting
Some mandatory configuration could not be loaded. (namespace)
OX cannot connect to IMAP during login.
# Test reachability from the OX pod
kubectl -n open-xchange exec open-xchange-core-mw-default-0 -- \
nc -zv <imap-host> <port>
Check: mailServer is correct, requireTls: false if server has no STARTTLS, mailEnabled = 1
in the user table (setting it to 0 fully disables the account).
Forbidden — User X in context Y is not enabled
mailEnabled = 0 in the user table disables the account entirely — not just mail. Fix:
kubectl -n open-xchange exec open-xchange-mariadb-0 -- \
mariadb -u openexchange -p<password> oxdatabase_4 \
-e "UPDATE user SET mailEnabled=1 WHERE cid=1 AND mail='user@example.com';"
Too many redirects after OIDC login
The Traefik redirect rule is matching /appsuite/ after OX redirects back post-login. Ensure
the IngressRoute only redirects the bare / path — not /appsuite/ or /appsuite.
CertificateRequest stuck, cert not renewing
Delete the stuck CertificateRequest and its stale next-key secret — cert-manager retries immediately:
kubectl -n <namespace> delete certificaterequest <name>
kubectl -n <namespace> delete secret <nextPrivateKeySecretName>