Skip to main content

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​

  • External MariaDB/MySQL database
  • Redis (bundled via core-mw.redis)
  • Traefik as ingress controller (with traefik-cert-manager IngressClass)
  • A wildcard TLS secret in the traefik namespace
  • 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>