Zen of Kubernetes

Using this server

Cluster: Not configured
Kubernetes API: Not configured — confirm the target context before using kubectl

Claude Code

claude mcp add --transport http --scope user --client-id 'kube-system.driftmower-mcp' driftmower-driftmower-mcp-codemowers-io 'https://driftmower-mcp.codemowers.io/mcp'

Run /mcp in Claude Code and select this server to sign in.

Codex

codex mcp add driftmower-driftmower-mcp-codemowers-io --url 'https://driftmower-mcp.codemowers.io/mcp' --oauth-client-id 'kube-system.driftmower-mcp'

Claude Code project config — .mcp.json

{
  "mcpServers": {
    "driftmower-driftmower-mcp-codemowers-io": {
      "type": "http",
      "url": "https://driftmower-mcp.codemowers.io/mcp",
      "oauth": {
        "clientId": "kube-system.driftmower-mcp"
      }
    }
  }
}

Other MCP clients

Transport: Streamable HTTP
URL: https://driftmower-mcp.codemowers.io/mcp
OAuth client ID: kube-system.driftmower-mcp

Developing

General application development

Application architecture and identity

Before implementing authentication or API boundaries, ask the user which architecture to follow: a backend for frontend (BFF), or direct OIDC/OAuth API access with custom scopes? Explain the tradeoffs below and record the chosen clients, trust boundaries, token ownership, and authorization model. If the user has already chosen, follow that decision without asking again. Otherwise wait for the choice before implementing the dependent authentication design; continue independent work meanwhile.

Persist the agreed architecture in the user's project AGENTS.md, or the existing equivalent agent-instructions file (for example agent.md). Preserve unrelated instructions and use the file that governs the affected application; create a project AGENTS.md if no equivalent exists. Record the chosen BFF/direct API pattern, rationale, identity provider or access proxy, clients, audiences/scopes, token/session ownership, and direct signed-transfer boundary. Record decisions, not credentials or unconfirmed assumptions. Read and honor these decisions in later work, updating them when the user changes the architecture instead of repeatedly reopening settled choices.

Choose the authentication boundary

Both options use an external OIDC provider for sign-in. The distinction is where tokens live and which component calls the application API; a BFF can also use scoped access tokens for downstream APIs.

Pattern Advantages Costs and responsibilities
BFF: the browser uses a session cookie; the backend handles OIDC and calls application APIs with server-held tokens. Keeps OAuth tokens out of browser JavaScript; centralizes session handling and browser-specific API composition; suits a first-party web UI. Adds a backend hop and session lifecycle management; requires secure, HttpOnly cookies and CSRF protection. XSS can still perform actions through the user's session.
Direct OIDC/OAuth with custom scopes: clients obtain access tokens and call independently protected APIs. Supports browser, mobile, CLI, and integration clients with a common API contract; allows explicit delegated permissions and independent API deployment. Browser-held tokens increase exposure to malicious JavaScript; each API must validate tokens and enforce scopes. Requires deliberate audience, token lifecycle, CORS, and scope design. Public clients cannot keep a client secret.

Use Authorization Code with PKCE for interactive sign-in. The IETF browser-based applications guidance describes the BFF and browser-client models and their security tradeoffs.

Recommend a BFF for an application primarily serving its own browser UI. Recommend direct scoped API access when independent clients and delegated API access are actual requirements. Present this recommendation to the user as part of the choice, rather than silently committing to it. Keep business logic and authorization in the application backend, independent of UI composition; split services only where ownership, scaling, or isolation warrants it.

Delegate identity, retain application authorization

Keep signed data transfers direct

The recommended shape is a browser UI with the user-selected BFF or scoped API boundary, externally managed OIDC identity, backend-enforced business authorization, and direct authorized transfers to signed-request services. Keep HTTP services and background workers independently scalable, with the process and internal health/metrics listener requirements in deploying/workloads, developing/health-checks, and developing/metrics.

Application health checks

TODO: Specify startup-check behavior and concrete response contracts.

Developing Go applications

Follow the language-independent failure-handling rules in developing/general: minimal protected blocks, only explicitly expected failures handled locally, and no blanket catches or generic error-to-success fallbacks. Unexpected failures must propagate visibly.

TODO: Go configuration, shutdown, networking, logging/metrics integrations, and local development.

Developing Java applications

Follow the language-independent failure-handling rules in developing/general: handle only explicitly expected failures locally and let unexpected failures propagate visibly.

For an application listening on port 8080, put this configuration in src/main/resources/application.properties:

server.port=8080
management.server.port=8081
management.endpoints.web.exposure.include=prometheus
management.metrics.distribution.percentiles-histogram.http.server.requests=true

Scrape /actuator/prometheus on port 8081. Use bounded metric labels such as route templates and status codes; never label metrics with request parameters, user identities, or other sensitive values.

Developing Node.js applications

Follow the language-independent failure-handling rules in developing/general: minimal protected blocks, only explicitly expected failures handled locally, and no blanket catches or generic error-to-success fallbacks. Unexpected failures must propagate visibly.

TODO: Node.js configuration, shutdown, networking, logging/metrics integrations, and hot reload. Follow the shared developing sections until this is documented.

Developing PHP applications

PHP-FPM brought its own orchestrator to the orchestrator party. Its master forks, sizes pools, and recycles workers inside the container, giving Kubernetes a process tree to babysit and application metrics several separate memory spaces to hide in. Putting that in a container does not make the process model fit this platform's single-process design.

Developing Python applications

Follow the language-independent failure-handling rules in developing/general: minimal protected blocks, only explicitly expected failures handled locally, and no blanket catches or generic error-to-success fallbacks. Unexpected failures must propagate visibly.

Virtual environments remain appropriate for local Python development outside containers. For container packaging, see building/languages/python.

TODO: Concrete configuration, shutdown, and local hot-reload examples.

Developing Rust applications

Follow the language-independent failure-handling rules in developing/general: minimal protected blocks, only explicitly expected failures handled locally, and no blanket catches or generic error-to-success fallbacks. Unexpected failures must propagate visibly.

TODO: Concrete Rust shutdown and async dependency integration examples.

Local development

Application logging

Application metrics

Building

General container builds

TODO: A concrete build-time credential injection mechanism.

Continuous integration

TODO: Registry publishing, signing, and concrete release-promotion commands.

Building Go containers

Use multiple build stages and ship a static binary where applicable, with only necessary runtime files in the final image.

Sample Dockerfile

The original sample below is preserved verbatim. Choose a maintained Go toolchain compatible with the application when adapting it.

FROM golang:1.23-alpine AS build
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY cmd ./cmd/
COPY templates ./templates/
COPY static ./static/
RUN CGO_ENABLED=0 go build -ldflags="-s -w" -o /app/server ./cmd

FROM scratch
WORKDIR /
COPY --from=build /app/server /server
COPY --from=build /app/templates /templates
COPY --from=build /app/static /static
ENV GIN_MODE=release
EXPOSE 8000 8080
ENTRYPOINT ["/server"]

Building Java containers

Use a Maven build stage to compile and package the application, then copy the executable JAR into a Java runtime image.

Sample Dockerfile

The original sample is preserved verbatim, including its Java 17 image choices and artifact filename.

#
# Build stage
#
FROM maven:3-eclipse-temurin-17 AS build
COPY src /home/app/src
COPY pom.xml /home/app
RUN mvn -f /home/app/pom.xml clean package
#
# Package stage
#
FROM amazoncorretto:17
COPY --from=build /home/app/target/hello-spring-0.0.1-SNAPSHOT.jar /usr/local/lib/hello-spring.jar
EXPOSE 8080
ENTRYPOINT ["java","-jar","/usr/local/lib/hello-spring.jar"]

Building Node.js containers

TODO: Detailed dependency installation, compilation, and runtime-image selection examples.

Building Python containers

TODO: Exact uv dependency-installation/lockfile commands, compilation needs, and runtime-image selection.

Building Rust containers

Use multiple build stages and ship a static binary where applicable, with only necessary runtime files in the final image. For a statically linked Rocket service, use a Rust builder followed by a scratch runtime containing the server binary, Rocket.toml, and any required runtime assets.

Sample Dockerfile

The original sample below builds the hello-rocket binary for x86-64 and copies it into scratch. It retains the historical Rust version and dependency-cache steps verbatim; apply the toolchain, lockfile, and runtime requirements above when adapting it.

FROM rust:1.67.1 AS build
ENV RUSTFLAGS='-C target-feature=+crt-static'
RUN cargo new /app
WORKDIR /app
RUN cargo update
COPY Cargo.toml /app/
RUN cargo fetch
RUN cargo build --release --target x86_64-unknown-linux-gnu
COPY src/main.rs /app/src/main.rs
RUN cargo build --release --target x86_64-unknown-linux-gnu

FROM scratch
WORKDIR /
ENV ROCKET_CONFIG=/app/Rocket.toml
COPY Rocket.toml /app/
COPY --from=build /app/target/x86_64-unknown-linux-gnu/release/hello-rocket /server
ENTRYPOINT ["/server"]

Deploying

General deployment guidance

TODO: Rollout, rollback, and release-promotion procedures.

Deployment networking

TODO: Concrete Ingress and network-policy examples. Consult the live cluster's admission-policy catalog for platform-specific behavior.

Deployment observability

TODO: Complete metrics scraping manifest examples and log-pipeline configuration.

Runtime security

State and persistence

TODO: PVC lifecycle, storage-class selection, backup, and restore procedures. For this cluster's provisioning instructions and storage settings, consult list_storage_classes; consult list_admission_policies for admission defaults.

PostgreSQL and Dragonfly examples

These Helm fragments adapt the lolcatz application pattern. Replace example consistently, use .Release.Namespace, and inspect the target cluster's CRDs, admission defaults, issuers and trust bundles first. Do not copy a cluster's replica counts or storage classes into another cluster. TLS server configuration, client verification, authentication and certificate reload are separate requirements.

CloudNativePG: operator-managed TLS

CNPG supplies the PostgreSQL listener configuration and TLS certificates; do not try to override its fixed listen_addresses parameter. Verify IPv6 connectivity on the target cluster rather than inferring it from a Service alone. Use the operator-generated application credentials and its PostgreSQL CA, which is distinct from the platform CA used by Dragonfly below.

apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
  name: example-database
spec:
  instances: 1 # The target platform may assign replicas automatically.
  bootstrap:
    initdb:
      database: example
      owner: example
  postgresql:
    pg_hba:
      - hostnossl all all 0.0.0.0/0 reject
      - hostnossl all all ::/0 reject
  storage:
    size: 10Gi
  resources:
    requests: {cpu: 100m, memory: 256Mi}
    limits: {cpu: "1", memory: 1Gi}

Application Pod fragment (for PostgreSQL clients that honor libpq-compatible environment settings):

volumes:
  - name: postgres-ca
    secret:
      secretName: example-database-ca
      items: [{key: ca.crt, path: ca.crt}]
containers:
  - name: app
    # image, ports and other application settings omitted
    volumeMounts:
      - {name: postgres-ca, mountPath: /etc/postgres-ca, readOnly: true}
    env:
      - name: DATABASE_URL
        valueFrom:
          secretKeyRef: {name: example-database-app, key: uri}
      - {name: PGSSLMODE, value: verify-full}
      - {name: PGSSLROOTCERT, value: /etc/postgres-ca/ca.crt}

Check the specific client library: a connection URL can override environment settings, and some clients ignore PGSSLMODE. Require certificate and hostname verification in the actual client. CNPG renews and reloads its operator-managed certificates. For cert-manager-managed certificates, configure serverTLSSecret and serverCASecret, and label the generated Secret cnpg.io/reload: "true" through the Certificate's secretTemplate. See CNPG certificates.

Dragonfly: IPv6, generated password and server TLS

apiVersion: secretgenerator.mittwald.de/v1alpha1
kind: StringSecret
metadata:
  name: example-redis-password
spec:
  forceRegenerate: false
  fields:
    - {fieldName: password, encoding: hex, length: "32"}
---
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: example-redis-internal-tls
spec:
  secretName: example-redis-internal-tls
  dnsNames:
    - example-redis.{{ .Release.Namespace }}
    - example-redis.{{ .Release.Namespace }}.svc.cluster.local
  issuerRef:
    name: cluster-tls-issuer
    kind: ClusterIssuer
    group: cert-manager.io
---
apiVersion: dragonflydb.io/v1alpha1
kind: Dragonfly
metadata:
  name: example-redis
spec:
  replicas: 1
  authentication:
    passwordFromSecret: {name: example-redis-password, key: password}
  tlsSecretRef:
    name: example-redis-internal-tls
  args:
    - --bind=::
    - --admin_bind=::
  resources:
    requests: {cpu: 50m, memory: 128Mi}
    limits: {cpu: 500m, memory: 512Mi}

Use the Service DNS name covered by the certificate, the generated password Secret, and the platform CA bundle. For Node/ioredis, configure REDIS_URL=rediss://example-redis.<namespace>.svc.cluster.local:6379/0, pass REDIS_PASSWORD explicitly as the client's password option, and set NODE_EXTRA_CA_CERTS to a mounted cluster-ca-certs ConfigMap's ca.crt. For Go clients, configure TLS roots and hostname verification; REDIS_TLS and SSL_CERT_FILE are application conventions, not automatic Redis protocol settings. Never use rejectUnauthorized: false or InsecureSkipVerify.

Operator shortcoming: certificate renewal is not propagated to Dragonfly. cert-manager renews the Secret and Kubernetes updates its mounted files, but Dragonfly v1.37.0 reloads them only on CONFIG SET tls true (or process restart). The operator must handle renewal for every instance; do not add application CronJobs, sidecars or reload loops to compensate. Report this limitation when evaluating TLS readiness. A changed Secret is not evidence that the server is serving the new certificate. Restarting an ephemeral single instance can discard its data.

This behavior was checked against Dragonfly v1.37.0 TLS configuration and operator revision 9441192. That operator does not watch TLS Secrets or issue TLS reload commands. Check the installed version before assuming later releases behave identically.

The same operator deliberately disables TLS on its administrative port and uses that port for management and replication. tlsSecretRef therefore encrypts application connections, not every operator channel. Keep the admin port private under operator-managed NetworkPolicy; do not override its TLS flags without verifying operator compatibility. A requirement to encrypt administrative/replication traffic needs operator support, not just this application manifest. See the operator's TLS resource construction.

Kubernetes workload design

TODO: Deployment rollout strategy, job concurrency/retries, and detailed leader-election examples.

Crds

S3 object storage — Bucket.s3.onyxia.sh

Qualified kind: Bucket.s3.onyxia.sh. CRD: buckets.s3.onyxia.sh. Example API version: s3.onyxia.sh/v1alpha1. Scope: namespaced.

These are Helm fragments: replace example consistently and use .Release.Namespace. Resource sizes are illustrative. These fragments omit platform-assigned fields and assume the relevant admission policies apply. Follow their guidance for prerequisite resources and use describe_cluster_capabilities, list_offerings, describe_namespace_readiness, list_storage_classes and list_admission_policies as available to inspect the target cluster. CRD presence alone does not prove operator health or permission. Check the served API version and live CRD schema when the installed version differs; the examples and client wiring below are included so routine provisioning does not require an online documentation lookup. Read deploying/general, deploying/storage, deploying/networking and deploying/security for shared requirements.

Provision a bucket on the platform's existing MinIO instance. Do not deploy an application-owned MinIO server. The namespace-qualified bucket name avoids collisions in shared object storage. Set quota and retention deliberately.

apiVersion: s3.onyxia.sh/v1alpha1
kind: Bucket
metadata:
  name: example-images
spec:
  name: {{ .Release.Namespace }}-images
  quota:
    default: 100000000000

Create scoped access with crds/policy.s3.onyxia.sh and crds/s3user.s3.onyxia.sh. Discover the S3 endpoint separately; the Bucket resource does not make an endpoint available at its Kubernetes name. Require HTTPS and verified platform trust in Kubernetes.

Keep the internal endpoint separate from the browser-facing endpoint. Sign presigned URLs using the endpoint the browser actually uses: changing the host after signing invalidates the signature. Public downloads do not require an anonymously writable bucket. Prefer scoped access and presigned URLs over broad anonymous policy.

Inspect reconciliation status and actual quota/deletion behavior before treating the bucket as ready. A keep annotation is not a backup; document object lifecycle and backup/restore separately.

PostgreSQL — Cluster.postgresql.cnpg.io

Qualified kind: Cluster.postgresql.cnpg.io. CRD: clusters.postgresql.cnpg.io. Example API version: postgresql.cnpg.io/v1. Scope: namespaced.

These are Helm fragments: replace example consistently and use .Release.Namespace. Resource sizes are illustrative. These fragments omit platform-assigned fields and assume the relevant admission policies apply. Follow their guidance for prerequisite resources and use describe_cluster_capabilities, list_offerings, describe_namespace_readiness, list_storage_classes and list_admission_policies as available to inspect the target cluster. CRD presence alone does not prove operator health or permission. Check the served API version and live CRD schema when the installed version differs; the examples and client wiring below are included so routine provisioning does not require an online documentation lookup. Read deploying/general, deploying/storage, deploying/networking and deploying/security for shared requirements.

CNPG supplies the PostgreSQL listener configuration; do not try to override its fixed listen_addresses parameter. Verify IPv6 connectivity on the target cluster rather than inferring it from a Service alone. Use the operator-generated application credentials. Follow the target admission policies for certificate provisioning and the server CA; do not assume it is the CNPG-generated CA.

apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
  name: example-database
spec:
  bootstrap:
    initdb:
      database: example
      owner: example
  postgresql:
    pg_hba:
      - hostnossl all all 0.0.0.0/0 reject
      - hostnossl all all ::/0 reject
  storage:
    size: 10Gi
  resources:
    requests:
      cpu: 100m
      memory: 256Mi
    limits:
      cpu: '1'
      memory: 1Gi

Operator-provisioned application Secret

With the default generated credentials for bootstrap.initdb, CNPG creates <Cluster-name>-app in the Cluster's namespace (for example, example-database-app). Consume its keys through secretKeyRef or a Secret volume instead of constructing connection strings or generating a second password. The keys below describe decoded values; Kubernetes represents them as base64 under Secret data.

Key Decoded value and use
dbname Application database name from bootstrap.initdb.database.
user Application database role from bootstrap.initdb.owner.
username The same application role as user; useful for clients expecting this key name.
password Generated application-role password.
host Short read/write Service name, <Cluster-name>-rw; resolves within the same namespace.
port PostgreSQL port as a string, normally 5432.
uri PostgreSQL connection URI containing credentials, database and the namespace-qualified read/write Service host (<Cluster-name>-rw.<namespace>).
fqdn-uri PostgreSQL connection URI using the fully qualified Service host (<Cluster-name>-rw.<namespace>.svc.<cluster-domain>).
jdbc-uri JDBC PostgreSQL URL using the namespace-qualified Service host, with user and password query parameters.
fqdn-jdbc-uri JDBC PostgreSQL URL using the fully qualified Service host, with user and password query parameters.
pgpass PostgreSQL password-file entry: host:port:dbname:user:password, using the short Service host. Mount with permissions accepted by libpq and ensure the connection host matches the entry.

Use fqdn-uri or fqdn-jdbc-uri when a fully qualified address is required; the cluster domain is commonly cluster.local. URI, JDBC and pgpass values contain credentials: reference them directly rather than logging or copying them into manifests. These connection strings do not by themselves enable verified TLS; configure the client's TLS mode and trust roots. Custom bootstrap credentials can change which Secret is used; check the configured Secret and its keys.

Application environment fragment (for PostgreSQL clients that honor libpq-compatible environment settings). Mount the CA selected by the target certificate configuration at the path used by PGSSLROOTCERT:

env:
  - name: DATABASE_URL
    valueFrom:
      secretKeyRef:
        name: example-database-app
        key: uri
  - name: PGSSLMODE
    value: verify-full
  - name: PGSSLROOTCERT
    value: /etc/postgres-ca/ca.crt

Check the specific client library: a connection URL can override environment settings, and some clients ignore PGSSLMODE. Require certificate and hostname verification in the actual client.

Databases and extensions

Use crds/database.postgresql.cnpg.io to declare database ownership and SQL extensions. The Cluster owns the PostgreSQL runtime, storage, roles/bootstrap and available extension libraries; the Database resource enables supported extensions in a specific database. Match runtime and extension images to the same PostgreSQL major version and base distribution. Do not copy extension-image versions between clusters without checking compatibility.

PostgreSQL databases — Database.postgresql.cnpg.io

Qualified kind: Database.postgresql.cnpg.io. CRD: databases.postgresql.cnpg.io. Example API version: postgresql.cnpg.io/v1. Scope: namespaced.

These are Helm fragments: replace example consistently and use .Release.Namespace. Resource sizes are illustrative. These fragments omit platform-assigned fields and assume the relevant admission policies apply. Follow their guidance for prerequisite resources and use describe_cluster_capabilities, list_offerings, describe_namespace_readiness, list_storage_classes and list_admission_policies as available to inspect the target cluster. CRD presence alone does not prove operator health or permission. Check the served API version and live CRD schema when the installed version differs; the examples and client wiring below are included so routine provisioning does not require an online documentation lookup. Read deploying/general, deploying/storage, deploying/networking and deploying/security for shared requirements.

Provisioning

Create the backing Cluster using crds/cluster.postgresql.cnpg.io. Its bootstrap creates the application role and initial database. A Database resource manages the named database and its supported extensions; it does not provision a PostgreSQL server or supply missing extension binaries.

apiVersion: postgresql.cnpg.io/v1
kind: Database
metadata:
  name: example-database-app
spec:
  name: example
  owner: example
  cluster:
    name: example-database
  extensions:
    - name: vector
    - name: postgis

The example assumes that the Cluster has compatible pgvector and PostGIS libraries available. Omit extensions the application does not use. For CNPG image-volume extensions, configure spec.postgresql.extensions on the Cluster as well as spec.extensions on this resource. SQL extension names and image extension names can differ (vector versus pgvector). PostGIS image extensions that depend on system GEOS libraries need an ld_library_path list containing system in the Cluster's extension declaration. Select compatible extension images from the target platform rather than inventing a tag.

Read the Database status before treating extension setup as successful; an accepted Kubernetes object is not proof of successful SQL reconciliation. Applications still own their fresh-install table/schema creation. Consume the Cluster's application connection Secret and CA as described in crds/cluster.postgresql.cnpg.io; do not embed database passwords in this resource or application code.

Choose deletion and retention behavior deliberately. Do not blindly copy a Helm keep annotation: keeping a resource is not a backup, and retained resources remain outside later release cleanup.

Redis-compatible cache — Dragonfly.dragonflydb.io

Qualified kind: Dragonfly.dragonflydb.io. CRD: dragonflies.dragonflydb.io. Example API version: dragonflydb.io/v1alpha1. Scope: namespaced.

These are Helm fragments: replace example consistently and use .Release.Namespace. Resource sizes are illustrative. These fragments omit platform-assigned fields and assume the relevant admission policies apply. Follow their guidance for prerequisite resources and use describe_cluster_capabilities, list_offerings, describe_namespace_readiness, list_storage_classes and list_admission_policies as available to inspect the target cluster. CRD presence alone does not prove operator health or permission. Check the served API version and live CRD schema when the installed version differs; the examples and client wiring below are included so routine provisioning does not require an online documentation lookup. Read deploying/general, deploying/storage, deploying/networking and deploying/security for shared requirements.

apiVersion: secretgenerator.mittwald.de/v1alpha1
kind: StringSecret
metadata:
  name: example-redis-password
spec:
  forceRegenerate: false
  fields:
    - fieldName: password
      encoding: hex
      length: '32'
---
apiVersion: dragonflydb.io/v1alpha1
kind: Dragonfly
metadata:
  name: example-redis
spec:
  authentication:
    passwordFromSecret:
      name: example-redis-password
      key: password
  resources:
    requests:
      cpu: 50m
      memory: 128Mi
    limits:
      cpu: 500m
      memory: 512Mi

Use the Service DNS name covered by the certificate, the generated password Secret, and the platform CA bundle. For Node/ioredis, configure REDIS_URL=rediss://example-redis.<namespace>.svc.cluster.local:6379/0, pass REDIS_PASSWORD explicitly as the client's password option, and set NODE_EXTRA_CA_CERTS to a mounted cluster-ca-certs ConfigMap's ca.crt. For Go clients, configure TLS roots and hostname verification; REDIS_TLS and SSL_CERT_FILE are application conventions, not automatic Redis protocol settings. Never use rejectUnauthorized: false or InsecureSkipVerify.

Certificate provisioning does not ensure runtime reload. cert-manager renews the Secret and Kubernetes updates its mounted files, but Dragonfly v1.37.0 reloads them only on CONFIG SET tls true (or process restart). Provide a platform renewal controller or scheduled maintenance Job that reloads every instance after Secret projection, with authentication, verified TLS, bounded timeouts and visible failures. A load-balanced Service reaches only one instance; it is insufficient for replica-wide reload. Verify the served certificate fingerprint after rotation and before expiry. Do not restart an ephemeral single instance as an unnoticed renewal strategy: that can discard its data.

This behavior was checked against Dragonfly v1.37.0 TLS configuration and operator revision 9441192. That operator does not watch TLS Secrets or issue TLS reload commands. Check the installed version before assuming later releases behave identically.

The same operator deliberately disables TLS on its administrative port and uses that port for management and replication. tlsSecretRef therefore encrypts application connections, not every operator channel. Keep the admin port private under operator-managed NetworkPolicy; do not override its TLS flags without verifying operator compatibility. A requirement to encrypt administrative/replication traffic needs operator support, not just this application manifest. See the operator's TLS resource construction.

Passmower OIDC clients — OIDCClient.codemowers.cloud

Qualified kind: OIDCClient.codemowers.cloud. CRD: oidcclients.codemowers.cloud. Example API version: codemowers.cloud/v1. Scope: namespaced.

Register an application with Passmower for delegated authentication instead of implementing application-owned accounts. Discover the advertised capability, served API version, live schema, operator health, namespace permissions, and applicable policy bindings before provisioning this resource. Do not assume another identity provider or optional platform component is installed.

Operator-provisioned OIDCClient Secret

For OIDCClient.codemowers.cloud, the operator creates oidc-client-<OIDCClient-name>-owner-secrets in the OIDCClient's namespace. Reference its fields directly instead of duplicating issuer endpoints or client credentials in application configuration. The table describes decoded values; Kubernetes stores them as base64 under Secret data.

Key Decoded value and use
OIDC_CLIENT_ID Client identifier, <namespace>.<OIDCClient-name>.
OIDC_CLIENT_SECRET Operator-generated client secret for client authentication; keep it on the server.
OIDC_CLIENT_ORIGIN Public application origin, for example https://app.example.com, without a callback path. Requires Passmower 2.7.0 or later.
OIDC_IDP_URI Issuer base URL; map to the application's issuer setting.
OIDC_IDP_WELL_KNOWN_URI OpenID Connect discovery document URL.
OIDC_IDP_AUTH_URI Authorization endpoint URL.
OIDC_IDP_TOKEN_URI Token endpoint URL.
OIDC_IDP_USERINFO_URI UserInfo endpoint URL.
OIDC_GRANT_TYPES Comma-separated grant types from spec.grantTypes.
OIDC_RESPONSE_TYPES Comma-separated response types from spec.responseTypes.
OIDC_TOKEN_ENDPOINT_AUTH_METHOD Client authentication method from spec.tokenEndpointAuthMethod.
OIDC_ID_TOKEN_SIGNED_RESPONSE_ALG ID token signing algorithm from spec.idTokenSignedResponseAlg.
OIDC_REDIRECT_URIS Comma-separated callback URIs from spec.redirectUris.
OIDC_AVAILABLE_SCOPES Scopes from spec.availableScopes, comma-separated by default; spec.availableScopesDelimiter controls the separator.
OIDC_ALLOWED_GROUPS Comma-separated group allowlist from spec.allowedGroups.
OIDC_ALLOWED_USERS Comma-separated account-ID allowlist from spec.allowedUsers.

List-valued fields are strings, not JSON arrays. If a client expects space-separated scopes, set spec.availableScopesDelimiter to a single space. Use explicit environment-variable mappings when the application uses different names. To inspect available keys without displaying credentials:

kubectl get secret oidc-client-example-frontend-owner-secrets -n <namespace> \
  -o go-template='{{range $key, $value := .data}}{{$key}}{{"\n"}}{{end}}'

Example mapping for an OIDCClient named example-frontend (requires Passmower 2.7.0 or later):

env:
  - name: OIDC_CLIENT_ID
    valueFrom:
      secretKeyRef:
        name: oidc-client-example-frontend-owner-secrets
        key: OIDC_CLIENT_ID
  - name: OIDC_CLIENT_SECRET
    valueFrom:
      secretKeyRef:
        name: oidc-client-example-frontend-owner-secrets
        key: OIDC_CLIENT_SECRET
  - name: OIDC_ISSUER
    valueFrom:
      secretKeyRef:
        name: oidc-client-example-frontend-owner-secrets
        key: OIDC_IDP_URI
  - name: NEXTAUTH_URL
    valueFrom:
      secretKeyRef:
        name: oidc-client-example-frontend-owner-secrets
        key: OIDC_CLIENT_ORIGIN

S3 object storage — Policy.s3.onyxia.sh

Qualified kind: Policy.s3.onyxia.sh. CRD: policies.s3.onyxia.sh. Example API version: s3.onyxia.sh/v1alpha1. Scope: namespaced.

These are Helm fragments: replace example consistently and use .Release.Namespace. Resource sizes are illustrative. These fragments omit platform-assigned fields and assume the relevant admission policies apply. Follow their guidance for prerequisite resources and use describe_cluster_capabilities, list_offerings, describe_namespace_readiness, list_storage_classes and list_admission_policies as available to inspect the target cluster. CRD presence alone does not prove operator health or permission. Check the served API version and live CRD schema when the installed version differs; the examples and client wiring below are included so routine provisioning does not require an online documentation lookup. Read deploying/general, deploying/storage, deploying/networking and deploying/security for shared requirements.

Declare access to the application's bucket, not all buckets on the MinIO instance. The Policy's spec.name is the name referenced by S3User; it is distinct from Kubernetes metadata.name. Use the same instance reference and bucket name as crds/bucket.s3.onyxia.sh.

apiVersion: s3.onyxia.sh/v1alpha1
kind: Policy
metadata:
  name: example-images
spec:
  name: {{ .Release.Namespace }}-images
  policyContent: |
    {
      "Version": "2012-10-17",
      "Statement": [
        {
          "Effect": "Allow",
          "Action": [
            "s3:ListBucket",
            "s3:GetObject",
            "s3:PutObject",
            "s3:DeleteObject"
          ],
          "Resource": [
            "arn:aws:s3:::{{ .Release.Namespace }}-images",
            "arn:aws:s3:::{{ .Release.Namespace }}-images/*"
          ]
        }
      ]
    }

Bucket-level actions such as ListBucket apply to the bucket ARN; object-level actions apply to the /* ARN. The example lists both. Remove PutObject/DeleteObject for read-only consumers and omit ListBucket if it is unnecessary. Attach this named policy through crds/s3user.s3.onyxia.sh; a Policy object alone does not grant an application access. Verify permissions with that application's principal, including rejection of access outside its bucket.

Kafka-compatible broker — Redpanda.cluster.redpanda.com

Qualified kind: Redpanda.cluster.redpanda.com. CRD: redpandas.cluster.redpanda.com. Example API version: cluster.redpanda.com/v1alpha2. Scope: namespaced.

These are Helm fragments: replace example consistently and use .Release.Namespace. Resource sizes are illustrative. These fragments omit platform-assigned fields and assume the relevant admission policies apply. Follow their guidance for prerequisite resources and use describe_cluster_capabilities, list_offerings, describe_namespace_readiness, list_storage_classes and list_admission_policies as available to inspect the target cluster. CRD presence alone does not prove operator health or permission. Check the served API version and live CRD schema when the installed version differs; the examples and client wiring below are included so routine provisioning does not require an online documentation lookup. Read deploying/general, deploying/storage, deploying/networking and deploying/security for shared requirements.

Provisioning

The Redpanda resource delegates broker lifecycle to the platform operator. Do not add a handwritten broker StatefulSet alongside it. This example uses internal listeners only; choose storage capacity and resource budgets for the workload.

apiVersion: cluster.redpanda.com/v1alpha2
kind: Redpanda
metadata:
  name: example-redpanda
spec:
  chartRef: {}
  clusterSpec:
    auth:
      sasl:
        enabled: true
    console:
      enabled: false
    external:
      enabled: false
    resources:
      requests:
        cpu: 500m
        memory: 1Gi
      limits:
        cpu: "1"
        memory: 1Gi
    serviceAccount:
      create: true
      name: example-redpanda
    storage:
      persistentVolume:
        size: 10Gi

chartRef: {} uses the operator's configured chart defaults; inspect the installed operator's resolved chart version and readiness. The operator's chart schema and defaults are version-dependent. Broker TLS does not configure client trust or turn a client into an authenticated principal. Keep the broker's administrative endpoints internal. Check actual listener addresses, ports and TLS modes in the reconciled broker configuration; a Service alone does not guarantee IPv6 or TLS.

Application wiring

Create topics with crds/topic.cluster.redpanda.com and distinct application identities/ACLs with crds/user.cluster.redpanda.com. Discover the internal bootstrap endpoint from the reconciled broker Service. In the illustrated platform pattern it is example-redpanda.<namespace>.svc.cluster.local:9093; verify the port instead of assuming every chart exposes the same listener.

Use SASL over verified TLS (SASL_SSL, SCRAM-SHA-512), the application's generated password Secret, and the namespace platform CA bundle (trusted-ca-certs key ca.pem, or the target cluster's advertised equivalent). Mount the bundle and configure the actual Kafka library's CA and hostname verification. For confluent-kafka, set security.protocol, sasl.mechanism, sasl.username, sasl.password and ssl.ca.location. For kafka-go, configure the Dialer's TLS roots and SASL mechanism. Names such as KAFKA_TLS are application conventions, not automatic Kafka client settings.

Verify broker and certificate readiness before producing traffic. Inspect how the installed operator handles certificate and credential renewal; projected Secret changes alone do not prove that long-running brokers or clients reload them. Do not disable authentication or certificate verification to bypass a provisioning error.

S3 object storage — S3User.s3.onyxia.sh

Qualified kind: S3User.s3.onyxia.sh. CRD: s3users.s3.onyxia.sh. Example API version: s3.onyxia.sh/v1alpha1. Scope: namespaced.

These are Helm fragments: replace example consistently and use .Release.Namespace. Resource sizes are illustrative. These fragments omit platform-assigned fields and assume the relevant admission policies apply. Follow their guidance for prerequisite resources and use describe_cluster_capabilities, list_offerings, describe_namespace_readiness, list_storage_classes and list_admission_policies as available to inspect the target cluster. CRD presence alone does not prove operator health or permission. Check the served API version and live CRD schema when the installed version differs; the examples and client wiring below are included so routine provisioning does not require an online documentation lookup. Read deploying/general, deploying/storage, deploying/networking and deploying/security for shared requirements.

Bind an application identity to the policy from crds/policy.s3.onyxia.sh on the same MinIO instance as crds/bucket.s3.onyxia.sh. The operator owns the resulting access credentials; consume them through Secret references.

apiVersion: s3.onyxia.sh/v1alpha1
kind: S3User
metadata:
  name: example-images
spec:
  accessKey: {{ .Release.Namespace }}-images
  policies:
    - {{ .Release.Namespace }}-images

For this operator's illustrated pattern, the generated Secret is named after the S3User resource (example-images) and contains accessKey and secretKey. Verify the generated Secret metadata and keys against the installed operator before wiring a different version; do not print the credentials while inspecting them.

env:
  - name: S3_ACCESS_KEY
    valueFrom:
      secretKeyRef:
        name: example-images
        key: accessKey
  - name: S3_SECRET_KEY
    valueFrom:
      secretKeyRef:
        name: example-images
        key: secretKey
  - name: S3_BUCKET
    value: {{ .Release.Namespace }}-images

Supply deployment-specific internal/public HTTPS endpoints separately, mount the platform CA and configure the SDK's verification. S3_ACCESS_KEY/S3_SECRET_KEY are application conventions; map them explicitly into the chosen SDK. Rotate credentials through their owning operator and restart clients that read credentials only from environment variables. Never copy generated credentials into Helm values, source code or images.

Kafka topics — Topic.cluster.redpanda.com

Qualified kind: Topic.cluster.redpanda.com. CRD: topics.cluster.redpanda.com. Example API version: cluster.redpanda.com/v1alpha2. Scope: namespaced.

These are Helm fragments: replace example consistently and use .Release.Namespace. Resource sizes are illustrative. These fragments omit platform-assigned fields and assume the relevant admission policies apply. Follow their guidance for prerequisite resources and use describe_cluster_capabilities, list_offerings, describe_namespace_readiness, list_storage_classes and list_admission_policies as available to inspect the target cluster. CRD presence alone does not prove operator health or permission. Check the served API version and live CRD schema when the installed version differs; the examples and client wiring below are included so routine provisioning does not require an online documentation lookup. Read deploying/general, deploying/storage, deploying/networking and deploying/security for shared requirements.

Provisioning

Create the backing broker with crds/redpanda.cluster.redpanda.com. Declare application topics through the operator instead of relying on broker auto-creation.

apiVersion: cluster.redpanda.com/v1alpha2
kind: Topic
metadata:
  name: example-images
spec:
  cluster:
    clusterRef:
      name: example-redpanda
  partitions: 3
  additionalConfig:
    cleanup.policy: compact

Partition count bounds consumer-group parallelism. Key related updates and deletes by the same stable entity ID so they route to the same partition. Consider the ordering implications before changing partition counts.

Compacted topics represent keyed state, not a complete historical event log. Produce tombstones for deletion and design projections to tolerate replay and repeated delivery. Compaction is asynchronous, and tombstones are subject to the broker's delete-retention settings; do not assume every deletion remains available forever to a long-offline consumer. For an event history, choose deliberate time/size retention instead of treating compaction as an audit log.

Give each independent projection its own consumer group and ACLs (crds/user.cluster.redpanda.com). Commit offsets only after processing and any required downstream publication succeeds. A failed job must remain uncommitted. Check topic reconciliation status, partition count and replication before treating the resource as ready. Keep maintenance/replay operations separate from ordinary application startup.

Kafka identities and ACLs — User.cluster.redpanda.com

Qualified kind: User.cluster.redpanda.com. CRD: users.cluster.redpanda.com. Example API version: cluster.redpanda.com/v1alpha2. Scope: namespaced.

These are Helm fragments: replace example consistently and use .Release.Namespace. Resource sizes are illustrative. These fragments omit platform-assigned fields and assume the relevant admission policies apply. Follow their guidance for prerequisite resources and use describe_cluster_capabilities, list_offerings, describe_namespace_readiness, list_storage_classes and list_admission_policies as available to inspect the target cluster. CRD presence alone does not prove operator health or permission. Check the served API version and live CRD schema when the installed version differs; the examples and client wiring below are included so routine provisioning does not require an online documentation lookup. Read deploying/general, deploying/storage, deploying/networking and deploying/security for shared requirements.

Provisioning

Create the broker using crds/redpanda.cluster.redpanda.com and topics using crds/topic.cluster.redpanda.com. Give each application component its own principal and minimum topic/group permissions. The example is a consumer; producers need Write permission on their output topics, and a component that consumes and produces needs both sets of permissions.

apiVersion: secretgenerator.mittwald.de/v1alpha1
kind: StringSecret
metadata:
  name: example-worker-kafka
spec:
  forceRegenerate: false
  fields:
    - fieldName: password
      encoding: hex
      length: '32'
---
apiVersion: cluster.redpanda.com/v1alpha2
kind: User
metadata:
  name: example-worker
spec:
  cluster:
    clusterRef:
      name: example-redpanda
  authentication:
    type: scram-sha-512
    password:
      valueFrom:
        secretKeyRef:
          name: example-worker-kafka
          key: password
  authorization:
    acls:
      - type: allow
        resource:
          type: topic
          name: example-images
          patternType: literal
        operations:
          - Read
          - Describe
      - type: allow
        resource:
          type: group
          name: example-worker
          patternType: literal
        operations:
          - Read
          - Describe

Consume the same generated Secret in the application; do not commit the generated password or grant all topics/groups as a shortcut. Use example-worker as the SASL username, SCRAM-SHA-512 as the mechanism, and verified broker TLS as described in crds/redpanda.cluster.redpanda.com. Check reconciliation status and authentication/authorization with the intended principal. Password rotation must reconcile both the broker identity and application clients; environment-backed credentials require client restart after the Secret changes.