How-to guide / Adopt existing resources
Connect external databases, caches, mail, and single sign-on
Catalog applications such as grafana, harbor, immich, keycloak, metabase, n8n, plausible, and vaultwarden connect to databases, Redis caches, mail servers, and OpenID Connect (OIDC) providers through shared dependency contracts (kix.contracts.*). When one of those backing services runs outside Kix (such as Amazon RDS, ElastiCache, Mailgun, or an external OIDC provider), declare a kix.mkImport instance that publishes the contract’s out fields.
Because Kubernetes pods can only read Secret objects in their own namespace, place any import that carries a login (out.credentials) in the consuming application’s namespace beside its secret and set scope = "namespace". Include { ref, ... }: in the cluster module header so ref.<namespace>.<instance> is in scope.
Connect an external PostgreSQL database (database)
Section titled “Connect an external PostgreSQL database (database)”Applications that require PostgreSQL take a database dependency checked against kix.contracts.postgresDatabase. Declare a secret or secret-ref instance with keys username and password, and reference it from out.credentials on a namespaced database import:
217 instances.contracts-rds = {218 rds-login = {219 package = packages.secret-ref;220 config.keys = [221 "username"222 "password"223 ];224 };225
226 database.package = kix.mkImport {227 scope = "namespace";228 out = {229 host = "app.abc123.eu-west-1.rds.amazonaws.com";230 port = "5432";231 database = "metabase";232 tls = true;233 credentials = ref.contracts-rds.rds-login;234 };235 };236
237 encryption-key = {238 package = packages.secret-ref;239 aliases = [ "encryptionKey" ];240 config.keys = [ "key" ];241 };242
243 metabase.package = packages.metabase;244 };port must be a string (such as "5432"). If the consuming package requires specific PostgreSQL extensions (for example, immich), list the extensions installed on the external server in out.extensions.
Connect an external Redis server (valkey)
Section titled “Connect an external Redis server (valkey)”Applications that use Redis (harbor, immich, and n8n) take a valkey dependency checked against kix.contracts.redis. Declare a secret with key password and reference it from out.credentials on a namespaced valkey import:
248 instances.contracts-redis = {249 redis-login = {250 package = packages.secret-ref;251 config.keys = [ "password" ];252 };253
254 valkey.package = kix.mkImport {255 scope = "namespace";256 out = {257 host = "app-cache.abc123.euw1.cache.amazonaws.com";258 port = "6379";259 tls = true;260 credentials = ref.contracts-redis.redis-login;261 };262 };Omit credentials and scope = "namespace" when the Redis server takes no password. Set tls = true when the server requires TLS (n8n connects over TLS when tls = true, while harbor and immich refuse a TLS-only Redis server).
Connect an SMTP mail server (smtp)
Section titled “Connect an SMTP mail server (smtp)”Applications that send email (grafana, immich, keycloak, plausible, and vaultwarden) take an optional smtp dependency checked against kix.contracts.smtp.
Unauthenticated cluster-wide relay
Section titled “Unauthenticated cluster-wide relay”When an SMTP relay requires no login, a single kix.mkImport with the default scope = "cluster" can serve every namespace in the cluster:
294 instances.mail.smtp.package = kix.mkImport {295 out = {296 host = "relay.internal.example.org";297 port = "587";298 tls = "starttls";299300 };301 };An external server (external = true, the default) must set tls to "starttls" or "implicit". Set external = false only when the relay runs inside the cluster.
Authenticated SMTP server
Section titled “Authenticated SMTP server”When the SMTP server requires a login, declare a secret with keys username and password in the application’s namespace, and place the smtp import beside it with scope = "namespace":
330 smtp-login = {331 package = packages.secret-ref;332 config.keys = [333 "username"334 "password"335 ];336 };337
338 smtp.package = kix.mkImport {339 scope = "namespace";340 out = {341 host = "smtp.mailgun.org";342 port = "465";343 tls = "implicit";344345 credentials = ref.vault.smtp-login;346 };347 };348
349 vaultwarden = {350 package = packages.vaultwarden;351 config = {352 domain = "https://vault.example.org";353354 };355 };Set config.mail.from on the application instance when you want to override the default out.from address declared on the smtp import. For plausible and immich, an authenticated smtp server must use tls = "implicit" (usually port "465"). For grafana, tls = "implicit" is only supported on port "465".
Connect OpenID Connect single sign-on (oidc)
Section titled “Connect OpenID Connect single sign-on (oidc)”Applications that support OpenID Connect (grafana, harbor, and immich) take an optional oidc dependency checked against kix.contracts.oidc. Single sign-on is enabled automatically when oidc is bound.
Multi-realm provider (keycloak with oidc-client)
Section titled “Multi-realm provider (keycloak with oidc-client)”When a multi-realm provider such as keycloak runs in the cluster (or is imported via kix.contracts.oidcProvider), add an oidc-client instance and an oidcClient secret (keys = [ "clientId" "clientSecret" ]) in the application’s namespace:
389 grafana-oidc = {390 package = packages.secret-ref;391 aliases = [ "oidcClient" ];392 config.keys = [393 "clientId"394 "clientSecret"395 ];396 };397
398 oidc = {399 package = packages.oidc-client;400 deps.oidcProvider = ref.auth.keycloak;401 config.realm = "main";402 };403
404 grafana = {405 package = packages.grafana;406 config = {407 ingress.host = "grafana.example.org";408 oidc.autoLogin = true;409 };410 };packages.oidc-client has meta.scope = "namespace" and meta.defaultAliases = [ "oidc" ], so the application in that namespace binds to it automatically.
Single-issuer external provider
Section titled “Single-issuer external provider”When your external identity provider has a single issuer URL, declare a kix.mkImport directly in the application’s namespace with scope = "namespace" and credentials = ref.<namespace>.<secret>:
415 instances.photos = {416 immich-oidc = {417 package = packages.secret-ref;418 config.keys = [419 "clientId"420 "clientSecret"421 ];422 };423
424 oidc.package = kix.mkImport {425 scope = "namespace";426 out = {427 issuerUrl = "https://login.example.org";428 credentials = ref.photos.immich-oidc;429 };430 };431 };If the consuming application does not use OIDC discovery (such as grafana), also set out.endpoints = { authorizationUrl = "..."; tokenUrl = "..."; userinfoUrl = "..."; }; on the oidc import.
Check the contract wiring
Section titled “Check the contract wiring”Run kix check to verify that every imported out attribute set and referenced secret satisfies the consumer’s contract:
❱ kix check doc-how-tos If a required contract field or secret key is missing, or if a TLS or scope constraint is violated, evaluation stops and prints the instance name, the contract name, and the missing field or key.