Developing
General application development
- Do not create single-use helper functions. Keep code at its sole call site instead of wrapping a few statements in helpers such as
make_s3() or init_schema(db). Extract helpers for actual reuse, not speculative future reuse or merely to name a block. Framework-required handlers, callbacks and entrypoints are not helper wrappers.
- Before implementing authentication or API boundaries, ask the user to choose a BFF or direct OIDC/OAuth API access with custom scopes, explaining the pros and cons in
developing/architecture. Honor an existing choice and persist agreed decisions in the project's AGENTS.md or equivalent agent-instructions file. Delegate accounts and authentication to the selected external OIDC provider, and keep signed-request services such as MinIO out of application proxies.
- Generate readable, indented YAML using block mappings and block sequences. Do not use inline JSON or YAML flow mappings (
{...}) and sequences ([...]) to save space. Apply this to configuration files, manifests and documentation examples; retain JSON only where the consuming API explicitly requires JSON. Keep explicit empty collections ({} or []) when required to distinguish them from null.
- Configure applications through environment variables with consistent names across environments. Keep environment-specific values out of application code. Keep defaults in one layer: do not duplicate Go/Python defaults in Dockerfile ENV, or repeat Dockerfile ENV defaults in deployment manifests. Deployment overrides should express actual environment differences.
- For complex settings, use a library that combines file and environment configuration; for example Dynaconf.
- Handle SIGTERM for graceful shutdown. Keep each container focused on a single process and responsibility. That process may use multiple threads where the runtime and workload justify them, sharing one address space and a thread-safe Prometheus registry that covers all application work in the container. Let Kubernetes supervise and scale replicas instead of building an orchestrator inside the container.
- Ensure application images and Kubernetes listener configuration support IPv6 and dual-stack operation: listen on
::, with IPv4 acceptance enabled or a separate IPv4 listener where required. Local Compose may use IPv4-only networking and bindings; do not report that alone as an IPv6 gap. See deploying/networking for application and backing-service examples. Keep service names and ports consistent where practical.
- Never catch all exceptions, regardless of programming language. Do not add blanket handlers such as Python
except Exception/except BaseException/bare except, Java/C# catches of the root exception type, C++ catch (...), or JavaScript catch/rejection handlers that consume every failure. In languages with untyped catch syntax, immediately identify the specific expected error and rethrow every unrecognized one.
- Keep try blocks minimal: surround only the individual operation whose known failure can be handled, not a whole request, job, loop body, or application startup. Separate unrelated operations and handle their documented failure types locally. If no recovery is needed, do not add a try/catch at all.
- Let unexpected failures surface loudly through the runtime/framework: fail the request/job or terminate the process as appropriate. Do not turn unknown failures into successful acknowledgements, retries, empty results, success responses, or fallback values. Do not add generic catch-and-log-and-continue handlers to keep broken code running. Use
finally, defer, context managers, RAII, or equivalent cleanup constructs instead of broad catches.
- Do not add speculative database preflight queries (for example
SELECT 1 or an existence query before every job) to decide whether to attempt work or suppress arbitrary failures. Prefer the operation itself, database constraints/atomic SQL, and handling of specific expected outcomes. When processing needs an image or other record, fetch its required fields with a single SELECT ... LIMIT 1, use that result, and return/exit if there is no row. Do not issue a separate existence query first. A legitimate business EXISTS query is different from generic defensive preflight checks. Health checks must not query backing services.
- End text files with a newline and configure editors consistently across operating systems. See VS Code issue #141169: Trimming final newline is not POSIX standards compliant.
- Maintain
.gitignore for generated outputs, binaries, dependencies, caches, local secrets/configuration, and editor/OS files. Maintain .dockerignore independently to keep the build context small and free of host artifacts and secrets. Inspect tracked files and build inputs; an ignore pattern does not remove an already-tracked artifact. Keep required source assets, dependency manifests, and lockfiles committed and available to builds.
- Start quickly so new instances can accept work promptly during scaling and recovery.
- On SIGTERM, stop accepting new requests and jobs, drain in-flight requests, and finish or return queued work before the termination deadline.
- Design for abrupt termination as well as graceful shutdown. Make retried jobs idempotent or transactional so repeated delivery does not duplicate side effects.
- Configure backing-service endpoints and credentials independently of application code. Compatible local and hosted databases, queues, object stores, and SMTP services should be interchangeable through configuration.
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
- Do not build local user accounts, password storage, registration, password resets, or a parallel login system. Offload authentication and account lifecycle to the selected external identity provider.
- Discover the target cluster's advertised capabilities, applicable policy bindings, and supported issuer/client provisioning interfaces before relying on them. Candidate modern identity stacks include Passmower and authentik as OIDC providers, and Pomerium as an identity-aware access proxy paired with an OIDC provider. Pomerium fills a different role from the issuer. Treat these as candidates, not installed services; agree on and provision missing prerequisites rather than adding built-in accounts. For a selected Passmower deployment, consult
crds/oidcclient.codemowers.cloud.
- Application profile and domain data may refer to the verified
(iss, sub) identity. Keep tenant membership, object ownership, and business permissions in the application where needed; authentication does not grant access to every record.
- For direct API access, agree on resource audiences and a minimal scope vocabulary, such as
documents:read and documents:write. Verify how the selected provider configures and grants those scopes before implementation. An application's requested scope or a client configuration entry alone is not proof that a token grants it.
- APIs must validate access tokens using the issuer's supported mechanism, including issuer, intended audience, expiry, and required scopes, then enforce tenant and object authorization. Do not use ID tokens as API access tokens. A BFF must enforce the same business permissions and must not become an unrestricted proxy using broad service credentials.
Keep signed data transfers direct
- Do not proxy MinIO/S3 or other components using signature-based request authentication through the BFF or application API. A BFF handles application operations and authorization; it should not relay object bytes or rewrite signed requests to force everything under the application's origin.
- After authorizing the requested object operation, let the backend issue a short-lived, narrowly scoped presigned URL and let the client upload or download directly from the storage endpoint. Keep long-lived storage credentials on the server. Configure the storage endpoint's CORS for the actual browser origins and required operations.
- Discover a client-reachable HTTPS endpoint and sign for that exact endpoint. Preserve the signed method, host, path, query parameters, headers, and any signed payload constraints. Do not sign an internal URL and replace its host or path afterward. Signature-aware infrastructure routing must preserve the signed request; this is not a claim that all reverse proxies inherently break signatures. See S3 Signature Version 4 query authentication.
- If no suitable endpoint is available, resolve endpoint provisioning before implementing direct transfers. Do not add an application proxy as a workaround. For other signed-request services, use their supported direct-client authorization mechanism rather than assuming S3 presigning applies universally.
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
- Provide HTTP health/readiness endpoints such as
/health and /ready for local and Kubernetes use.
- Serve all readiness, liveness, and startup-check endpoints on an internal listening port separate from the application port used by Ingress. They may share the internal Prometheus metrics port. Do not register these endpoints on the public application listener; a catch-all Ingress must not expose them.
- Keep checks quick and inexpensive, without dependencies on external services.
- Use readiness to indicate whether incoming traffic can be handled.
- Avoid liveness probes by default. Add them only when there is a demonstrated need; Java is one workload where they have proved useful.
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 a Spring Boot HTTP service, include
spring-boot-starter-web, annotate the application with @SpringBootApplication, and start it with SpringApplication.run. Use @RestController and @GetMapping to define HTTP handlers.
- Add
spring-boot-starter-actuator and micrometer-registry-prometheus for Prometheus metrics. Keep dependency versions compatible with the chosen Spring Boot release.
- Configure an internal management listener separately from application traffic. Expose only the endpoints needed for monitoring, and keep management ports out of public ingress routes. Enable request latency histograms when required for monitoring.
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.
- PHP-FPM can run on Kubernetes, but its master/worker model is a poor fit for new services here. Even
pm.max_children = 1 leaves a master and a child. See the PHP-FPM process-manager configuration.
- Use one long-lived application process, with Kubernetes handling replicas and restarts. Do not bundle nginx, PHP-FPM, and supervisord into a miniature hosting platform. For unavoidable legacy process models, document the exception described in
building/general.
- Framework X is a concrete option that can run without a forking worker manager: its built-in ReactPHP HTTP server runs directly with
php public/index.php. Use that standalone CLI mode, launched with exec-form CMD, rather than its FPM integration.
- Keep I/O asynchronous throughout the request path. Blocking PHP libraries still block the event loop; choosing an async framework does not convert them automatically.
- Keep metrics in the long-lived process's shared registry so each scrape covers all application work in the container. Follow
developing/metrics for the internal metrics listener, and implement bounded SIGTERM shutdown as described in developing/general.
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.
- Never use
except Exception, except BaseException, or bare except. Match concrete, expected exception types at the operation that can handle them. Retry only known transient failures; let programming errors terminate the operation without acknowledging incomplete work. Use finally/context managers for resource cleanup.
- Prefer async Python for concurrent I/O, with async-compatible libraries throughout the request path. Do not call blocking database, object-storage, image-processing, or inference operations directly on the event loop.
- Do not use uWSGI master/worker pools, supervisord, or similar process-management stacks as the deployment model for new Python services in Kubernetes. Kubernetes already handles supervision and replicas; it does not need an understudy inside the container.
- Async frameworks such as Sanic fit this model when explicitly run without a worker manager. Use
sanic server:app --host=:: --port=8000 --single-process or app.run(host="::", port=8000, single_process=True). Sanic's default one-worker configuration still has a main process plus a worker; --single-process disables the manager and auto-reloader. See Running Sanic.
- Default to one process and one request/job execution thread per container. Scale with Kubernetes replicas instead of worker processes. A server configured with one worker may still create a supervisor process; inspect its process model.
- Do not assume Python threads parallelize CPU work. Additional threads need a documented, measured reason: the actual operations must release the GIL, as some NumPy, OpenCV, and Pillow operations do. Merely importing one of these libraries does not justify a larger thread pool.
- For existing synchronous workloads, retain a single execution thread until the I/O path can be converted correctly. Async-first is a preference, not a reason to wrap blocking calls in async functions or create unbounded executor pools.
- Bound concurrent tasks, library thread pools, connection pools, and shutdown time. Keep HTTP and queue workers in separate containers/workloads. Runtime housekeeping threads are not additional request/job execution concurrency.
- Keep TLS optional in software and configure it at runtime: disabled for local Compose, enabled with certificate and hostname verification in Kubernetes.
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.
- Build the application with
rocket::build() and mount related handlers under an explicit prefix. Mounting a #[get("/")] handler at /api/v1/rocket gives /api/v1/rocket/ as the application URL.
- Keep runtime settings in
Rocket.toml or environment variables. Set ROCKET_CONFIG when the file is outside the working directory. Configure container listeners to bind 0.0.0.0 on port 8000 so they are reachable outside the container, including debug builds in Compose. A host-only development listener can bind 127.0.0.1. Choose log verbosity for the environment.
rocket_prometheus::PrometheusMetrics supplies request instrumentation through a Rocket fairing. Attach a clone of the metrics object so instrumentation and export share the same registry. Serve /metrics on a separate internal listener sharing that registry, and keep it off the public application listener. Use bounded labels such as route templates and status codes; exclude personal data, credentials, and raw URLs. Declaring containerPort: 8080 alone does not create that listener.
- For local container development, configure a Compose service with
build: . and ports: ["8000:8000"]. Run docker compose up --build, then request http://localhost:8000/api/v1/rocket/ for the route above. Exclude the local target directory from the Docker build context.
- For Skaffold development, declare a build artifact whose image name matches the image in the Kubernetes workload manifest. Discover cluster capabilities and applicable policy bindings before configuring deployment; registry credentials, ingress settings, and domains are environment-specific.
TODO: Concrete Rust shutdown and async dependency integration examples.
Local development
- Prefer working through the project's Skaffold or Docker/Compose environment for development, dependency management, builds, and tests. Coding agents must not install application runtimes, package dependencies, backing services, or other components on the host for their own use. Install dependencies in container images. Edit dependency manifests and lockfiles directly; lockfile-only package-manager commands are acceptable when they do not install host components. Reuse existing host tools for orchestration and editing.
- Use Docker Compose for local development and integration testing. Mirror the Kubernetes service layout and environment-variable names; align names and ports where practical. IPv4-only Compose networking and explicit local listener overrides are acceptable, provided the image and Kubernetes runtime support IPv6. Do not repeat environment defaults already supplied by the image or application.
- Run local Compose without TLS. Keep TLS optional in software; enable and verify it in Kubernetes through deployment configuration.
- Use local
.env files without committing them or other secrets.
- Enable hot reload through development volume mounts.
- Keep application and base dependencies aligned across development, testing, and production. Development images may include debugging tools omitted from production.
- Document Compose startup and Kubernetes deployment steps.
Application logging
- Send logs to stdout or stderr; plain text and structured JSON are both acceptable. Choose the format appropriate to the application and its consumers. Do not report plain-text logging alone as a compliance gap. Let the container runtime collect, rotate, and retain logs.
- Do not maintain log files inside the container or add a timestamp already supplied by the runtime.
- Use standard severity levels where useful.
- Prefer a metric for information that can be measured efficiently instead of emitting repetitive log records.
Application metrics
- Expose Prometheus metrics, using the framework's integration when available.
- Keep all application execution in one process, optionally with multiple threads sharing its address space. Update one shared, thread-safe metrics registry so each scrape represents all application work in the container, rather than whichever forked worker happened to answer. Shared memory alone does not aggregate separate registries; every execution path must use the shared registry.
- Serve metrics on a separate internal listening port from public application traffic; readiness, liveness, and startup-check endpoints may share this port. Do not register metrics or health-check endpoints on the public application listener, where an Ingress catch-all route could expose them.
- Never include PII or sensitive information in metric names, labels, values, help text, or exemplars. Exclude user identities, email addresses, IP addresses, credentials, tokens, request/response bodies, and raw URLs or query strings. Use bounded, non-sensitive categories such as route templates, operation names, and status codes instead of individual identifiers.
- Measure request latency, request/response sizes, and useful business activity such as import throughput. Keep label cardinality manageable.
- Choose measurements that support alerting.
- Leave container memory and filesystem usage to runtime monitoring instead of duplicating it in the application exporter.
Deploying
General deployment guidance
Write Kubernetes, Helm and Compose YAML with indented block mappings and sequences, with each field or item on its own line. Do not compress YAML into inline JSON or flow collections to save space. Preserve explicit empty collections ({} or []) where their empty value is meaningful.
Use Kubernetes for production and Compose for local development, keeping their configuration and service structure aligned.
Version-control environment configuration and automate setup, tests, and deployment.
Never commit kind: Namespace resources to application repositories, Helm charts, Kustomize overlays, or GitOps manifests. Deleting or pruning a managed namespace can cascade-delete every workload and its data, including unrelated applications. Namespace provisioning and deletion belong to the platform lifecycle, outside the application release; do not adopt existing namespaces into application ownership.
Before removing an existing Namespace declaration, inspect the live namespace's Helm, Argo CD, Flux, Skaffold and other ownership/tracking metadata and the owning controller's release inventory. Remove the declaration only after safely detaching it from application ownership and pruning. Removing labels alone does not remove a namespace from Helm release history or a GitOps inventory. Preserve the namespace and its workloads; never delete/recreate it. If safe detachment cannot be established, keep the declaration until the owning release/controller can be migrated safely.
Use Kubernetes workload primitives instead of implementing an orchestrator inside the application.
Separate image building, release configuration, and runtime execution. Chart defaults on the main development branch may use latest; do not flag that alone as a defect. At tag/release time, substitute immutable image digests or release identifiers and record the source and configuration revisions. A deployed release combines that immutable image with its deployment configuration.
Roll back by selecting a previous release, rather than editing running containers. Check database schema compatibility before rolling back application code.
Use list_accessible_namespaces to discover deployment targets and list_admission_policies to inspect platform constraints. When advertised, describe_namespace_readiness reports namespace readiness and describe_cluster_capabilities reports platform capabilities.
Treat the target cluster's admission-policy descriptions and matching policy/binding specifications as the source of truth for platform-assigned settings and prerequisite resources. Omit policy-owned fields from application manifests instead of restating their defaults. Check applicability to the resource and namespace; policies can be disabled or cluster-specific. Where no applicable policy supplies a required setting, configure it explicitly. Keep cluster policy details in the discoverable policies rather than duplicating them in bundled advisories.
Use Helm capability checks for optional platform APIs instead of adding feature flags for infrastructure availability. In particular, conditionally render PodMonitor and PrometheusRule resources on their respective monitoring.coreos.com/v1/<Kind> capabilities; see deploying/observability. Application charts must not install the platform operator or its CRD definitions.
Parameterize chart namespaces with .Release.Namespace and environment-specific hostnames through values. Keep Service selectors aligned with Pod labels and Service target ports aligned with application listeners.
TODO: Rollout, rollback, and release-promotion procedures.
Deployment networking
Use service-name discovery in Compose and service.namespace discovery in Kubernetes.
Reuse service names and network ports across environments where practical. Container images and Kubernetes process listeners must support IPv6: listen on :: (the IPv6 unspecified address) and ensure the runtime also accepts IPv4, or configure separate listeners. Binding only to 0.0.0.0 does not provide IPv6 support in Kubernetes. Local Compose may intentionally use IPv4-only networking and bindings; that alone is not a finding. Inspect Dockerfile defaults and runtime configuration together before judging Kubernetes compatibility.
Configure application HTTP listeners for IPv6, for example Next.js HOSTNAME=::. Service configuration alone does not change the process listener. For operator-managed backing services, follow the target admission policies.
Bound inter-service calls with connection timeouts and retry with backoff.
Keep TLS optional in application software through runtime configuration. Local Docker Compose should run without TLS; Kubernetes must enable TLS for all component-to-component traffic, including application HTTP, databases, caches, queues, and object storage. Do not bake TLS enablement, environment-specific certificates, or trust paths into images.
Follow the target admission-policy guidance for certificate provisioning, issuer selection and workload references. Configure application servers to load the resulting TLS Secret.
Configure clients to trust the cluster CA and verify hostnames. In namespaces selected by the platform trust bundles, mount ConfigMap cluster-ca-certs key ca.crt, or trusted-ca-certs key ca.pem, and configure the application's CA trust. Check bundle availability in the target namespace; issuing a certificate alone does not configure client trust.
Arrange certificate reload or workload restart after renewal. Keep authentication enabled over TLS; encryption alone does not authenticate the calling component.
Before configuring TLS, use describe_cluster_capabilities to discover the cluster's existing ClusterIssuers, their descriptions and reported readiness. Prefer a suitable existing ClusterIssuer over creating an application-specific Issuer or ClusterIssuer. Select it for public or in-cluster TLS using its live description; only configure a new issuer when the available issuers cannot meet the application's requirements. When available, consult describe_namespace_readiness for namespace network posture and allowed hostnames.
Expose browser-facing routes through an Ingress. Keep the admitted certificate DNS names, Ingress TLS hosts, public application URL, and OIDC redirect URIs aligned; use the selected identity provider's provisioned configuration as described in deploying/security.
Route API paths to their own Services where appropriate, keeping background workers and backing-service administration endpoints internal.
TODO: Concrete Ingress and network-policy examples. Consult the live cluster's admission-policy catalog for platform-specific behavior.
Deployment observability
- Configure Compose healthchecks and Kubernetes readiness probes against lightweight application health endpoints.
- Point every configured Kubernetes readiness, liveness, and startup probe at an internal container port separate from the application port targeted by Ingress. Health checks may share the Prometheus metrics port. Keep this port out of Ingress backends and externally exposed Services; probes reach it directly on the Pod.
- Avoid liveness probes by default.
- Expose application Prometheus metrics and let the runtime supply container resource metrics.
- Scrape metrics through a separate internal port from the public application port. Never expose the metrics port through Ingress or externally exposed Services. A catch-all application Ingress must not make metrics reachable.
- Prefer
PodMonitor.monitoring.coreos.com to scrape the named metrics port directly on Pods. Do not add metrics-only Services just to use a ServiceMonitor. Use a ServiceMonitor only when monitoring through an existing Service is specifically needed. Keep selectors aligned with Pod labels and use bounded target labels to identify the application and component.
- Never populate
path, interval, scrapeTimeout, or scheme in PodMonitor spec.podMetricsEndpoints or ServiceMonitor spec.endpoints, including defaults such as /metrics, 15s, 5s, or http. Omit these fields from application manifests, Helm templates, values and generated examples. If they need configuration, set them through cluster admission policy; do not add application-level overrides.
- Do not add Helm monitoring enable/disable or discovery-label knobs. Render Prometheus Operator custom resources automatically when their APIs are available, using
.Capabilities.APIVersions.Has for each kind independently. The platform installs the operator and its CRD definitions; application charts create the supported PodMonitor and PrometheusRule resources, not the CRD definitions themselves. Keep the metrics listener available even when the monitoring APIs are absent. Platform Prometheus selectors must discover the application resources.
- Guard a PodMonitor with
{{- if .Capabilities.APIVersions.Has "monitoring.coreos.com/v1/PodMonitor" }} and its matching {{- end }}. Guard PrometheusRule separately with monitoring.coreos.com/v1/PrometheusRule. Do not require ServiceMonitor support to render a PodMonitor or require PodMonitor support to render alert rules. For offline rendering/tests, pass helm template --api-versions monitoring.coreos.com/v1/PodMonitor --api-versions monitoring.coreos.com/v1/PrometheusRule; also verify rendering without these APIs.
- Metrics must contain no PII or sensitive information, including in labels and exemplars. Review emitted metric data before enabling scraping; internal network access does not make sensitive metric content acceptable. See
developing/metrics for instrumentation guidance.
- Collect stdout/stderr logs through the platform; plain text and structured JSON are both acceptable. Leave timestamping, rotation, and storage to the runtime.
TODO: Complete metrics scraping manifest examples and log-pipeline configuration.
Runtime security
Use a read-only root filesystem and explicit writable mounts for application state and scratch data. See building/general for image file permissions and runtime identity compatibility, and deploying/general for admission-owned settings.
Keep secrets out of source control and images. Use Vault for secret storage/versioning, Docker secrets or environment variables locally, and Kubernetes Secrets in the cluster.
For components that authenticate to each other using shared passwords or tokens, use the platform's Mittwald secret generator. Declare StringSecret.secretgenerator.mittwald.de resources and consume the generated Kubernetes Secret from both components through Secret references or mounted files.
Commit the generator resource, never the generated credential. Give separate component relationships their own credentials and coordinate credential rotation with all consumers. Use an operator's existing generated credentials when that operator already owns authentication.
The secret generator is part of platform provisioning. Consult the provisioning guidance for resource setup, and use the advertised MCP capability and namespace-readiness tools to inspect the deployment environment.
Use internal TLS alongside generated credentials; see deploying/networking through get_engineering_advisory. Do not disable certificate or hostname verification.
Set seccompProfile.type: RuntimeDefault at Pod level.
Register the application with the selected OIDC provider, using authorization code flow with PKCE and explicit HTTPS redirect URIs for browser clients. Consume provisioned credentials through Secret references; configure API issuer and audience consistently. Keep confidential client credentials on the server.
Discover the installed provider and its provisioning interface before relying on them. When using Passmower OIDCClient.codemowers.cloud, read crds/oidcclient.codemowers.cloud for its requirements and generated Secret mappings.
Generate application session-signing secrets once through StringSecret.secretgenerator.mittwald.de and share the resulting Secret across replicas. Avoid regeneration on every deployment; rotate deliberately.
State and persistence
Keep application containers stateless where possible and store state in external databases or caches.
Use a read-only root filesystem to enforce that boundary.
Use list_offerings, when advertised, to discover available operator-backed databases, queues, caches, and object storage. Configure the application with the provisioned endpoint and credential references.
Give applications that need temporary files a bounded emptyDir volume mounted at /tmp or another explicit scratch path while keeping the root filesystem read-only. Point library caches and temporary-file settings there. Treat this data as disposable when the Pod is replaced.
Provision PostgreSQL with Cluster.postgresql.cnpg.io; manage databases and supported extensions with Database.postgresql.cnpg.io. Consume operator-generated connection Secrets instead of assembling or committing credentials.
Consume MinIO through Bucket.s3.onyxia.sh, with Policy.s3.onyxia.sh and S3User.s3.onyxia.sh for scoped access. Do not deploy MinIO instances manually. Restrict access to the required buckets and operations, and consume the generated credentials through Secret references.
Configure internal object-storage endpoints separately from browser-facing endpoints. Generate presigned URLs for the endpoint the browser will actually use; do not assume a signed URL remains valid after changing its host.
Keep MinIO/S3 transfers direct between the client and the storage endpoint; do not proxy signed requests or object bytes through a BFF or application API. Authorize the operation before issuing a scoped presigned URL, and configure storage CORS for the browser origins that need access. See developing/architecture for signed-request service boundaries.
Define retention and deletion behavior deliberately for persistent operator resources. A Helm keep annotation preserves a resource during relevant Helm deletion operations; it is not a backup. Keep backup and restore procedures explicit.
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
Keep one process per container; avoid application-level forking or multiprocessing to orchestrate workloads. A server master plus one worker is still two processes. Multiple threads may share that process's address space and metrics registry where justified, so Prometheus scrapes cover the whole container's application work. For Python, prefer async I/O and default to one request/job execution thread; additional threads require workload-specific evidence of GIL release (see developing/languages/python).
Use Kubernetes Lease objects for leader election.
Use CronJobs for schedules at a resolution of at least one minute.
Run one-off work as Jobs or separate Pods instead of inside long-lived application Pods.
Run HTTP servers and background workers as separate workload types so their replica counts can scale independently. Run the actual application directly in the foreground as PID 1 with exec-form ENTRYPOINT/CMD and let Kubernetes manage restarts. Avoid dumb-init, tini, s6, supervisord, and similar process wrappers for new services. Document narrowly scoped exceptions when porting legacy applications whose process model cannot reasonably be changed.
Set a termination grace period that accommodates bounded request draining and job completion or return to the queue. Workers must tolerate interrupted jobs and repeated delivery.
Package database migrations and maintenance commands with the application. Run them as one-off Jobs using the same image digest and appropriate release configuration as the application.
Coordinate migration execution so application replicas do not race to perform the same migration; plan schema changes to support overlapping application versions during rollouts.
Set CPU and memory requests and limits appropriate to each component, including heavier media-processing workers. Use describe_namespace_readiness, when advertised, to inspect quotas and defaults before sizing workloads.
Reuse an application image for its HTTP service and background worker when they share code, selecting separate startup commands and resource budgets. Workers that expose no network interface do not need a Service.
Declare messaging resources through the operator, such as Topic.cluster.redpanda.com, and choose partition counts and retention deliberately. Compacted topics suit keyed state updates; they do not preserve every historical event. Design replay and deletion behavior for the chosen retention policy.
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.
- Register browser authentication declaratively with
OIDCClient.codemowers.cloud, using authorization code flow with PKCE and explicit HTTPS redirect URIs. Consume the generated client credentials through Secret references; configure API issuer and audience consistently. Keep confidential client credentials on the server.
- Read the OIDC issuer from the operator-generated owner Secret
oidc-client-<OIDCClient-name>-owner-secrets, key OIDC_IDP_URI, through valueFrom.secretKeyRef. Map that key to the application's OIDC_ISSUER or equivalent environment variable; do not duplicate the issuer in Helm values or hardcode it in application manifests.
- Deploy Passmower 2.7.0 or later and source the public application origin from the same owner Secret's
OIDC_CLIENT_ORIGIN value. For a Next.js application using NextAuth, map it to NEXTAUTH_URL through valueFrom.secretKeyRef, rather than maintaining another Helm URL value. Use the origin, not the OIDC issuer URL or a redirect URI containing a callback path. If the field is missing, require the deployment to use Passmower 2.7.0 or later and verify Secret reconciliation; do not build application or manifest workarounds. Keep these values server-side.
- Verify how the installed Passmower version configures and grants custom scopes and resource audiences. Declaring
spec.availableScopes does not replace API-side token validation or business authorization.
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.