Set up TLS
Serve the team server and the runner over HTTPS: Caddy in compose, an Ingress, Gateway or the pod itself in Kubernetes.
The desktop app only talks to a team server or a runner over HTTPS (plain HTTP is accepted only to
the same computer). Both programs refuse to listen on a non-loopback address without a certificate,
unless they are told a TLS proxy is in front (--allow-plain-http).
Where TLS can end
| Setup | TLS ends at | Program setting |
|---|---|---|
| docker compose | Caddy | servers on plain HTTP on the internal backend network |
| Kubernetes with an Ingress or a Gateway | the controller or gateway | pod on plain HTTP (OXYSDB_SERVER_ALLOW_PLAIN_HTTP) |
Kubernetes, tls.enabled | the pod | --tls-cert / --tls-key |
| a plain host | the program | --tls-cert FILE --tls-key FILE (or OXYSDB_SERVER_TLS_CERT/_KEY, OXYSDB_RUNNER_TLS_CERT/_KEY) |
Certificates are reloaded when their files change (checked at most once a second, for new connections): a renewed certificate is served without a restart. A renewed pair that does not load is logged and the previous one kept.
docker compose (Caddy)
Set in .env:
OXYSDB_TLS | Certificate |
|---|---|
internal (default) | Caddy’s own CA. Export its root (/data/caddy/pki/authorities/local/root.crt in the caddy container) and give it to the clients as CA certificate file |
an e-mail address, with OXYSDB_SITE_ADDRESS=oxysdb.example.com | ACME (Let’s Encrypt or ZeroSSL). The name must resolve to the host and port 80 be reachable: OXYSDB_HTTP_PUBLISH=80, OXYSDB_HTTPS_PUBLISH=443 |
To use your own certificate, replace the tls line of caddy/Caddyfile.single or caddy/Caddyfile.ha
with tls /certs/fullchain.pem /certs/key.pem and mount the files read-only into the caddy service.
To encrypt the hop between Caddy and the servers too, give the servers a certificate
(OXYSDB_SERVER_TLS_CERT, OXYSDB_SERVER_TLS_KEY: paths of mounted secrets) and point Caddy at
https://server-1:8443 with a tls_trust_pool.
Kubernetes
- Ingress (
ingress.enabled): needsingress.tls(oringress.allowInsecurewhen TLS ends in front of the controller) andserver.trustedProxiesset to the controller’s pod CIDR; the chart refuses either missing.ingress.certManager.clusterIssuer(orissuer) annotates it for cert-manager. - Gateway API (
httpRoute.enabled,parentRefs,hostnames): TLS on the Gateway; setserver.trustedProxiesto the gateway’s pods. For HTTPS up to the pod, add aBackendTLSPolicyfor your Gateway implementation. - On the pod (
tls.enabled):tls.existingSecret, or a cert-managerCertificatewithtls.certManager.enabled,issuerRef,dnsNames(duration2160h andrenewBefore360h by default). Probes switch to HTTPS and the nginx Ingress getsbackend-protocol: HTTPS(ingress.nginxBackendHttps).
The runner subchart has the same tls.* and ingress.* values.
Post-quantum key exchange
Both programs and the desktop offer the hybrid key exchange X25519MLKEM768 (ML-KEM-768 with
X25519) first, and accept a classical one from an older client. Once every desktop is upgraded,
require it: --tls-pq require (or OXYSDB_SERVER_TLS_PQ=require, OXYSDB_RUNNER_TLS_PQ=require)
accepts only X25519MLKEM768 over TLS 1.3, and the desktops’ network setting pqTls: require
(network.configure) refuses a server without it, and a team server reached over http://
other than on loopback. Where TLS ends at a proxy, the proxy chooses the key exchange: use one that
offers X25519MLKEM768 (nginx or Caddy built with OpenSSL 3.5 or Go 1.24 and later, a recent Envoy).
--tls-pq needs --tls-cert and --tls-key: a program that speaks plain HTTP behind the proxy
refuses to start with it rather than ignore it.
Clients with a private CA
In the desktop app, give the CA’s PEM file in CA certificate file on the Team Sharing sign-in form and on Edit › Preferences › External tools › Remote Task Runner. The app reads a CA file of at most 1 MiB.
Troubleshooting
| Symptom | Fix |
|---|---|
The program refuses to start on 0.0.0.0 | give --tls-cert/--tls-key, or --allow-plain-http behind a TLS proxy |
| Every user shares one sign-in throttle | server.trustedProxies (or OXYSDB_SERVER_TRUSTED_PROXIES) does not name the proxy |
| ACME fails | the DNS name does not point to the host, or port 80 is not reachable |
| Certificate error in the app | the CA is private: give its PEM file to the app |