Architecture area

Service bindings

A service asks for a database by name. The platform provisions it, publishes where its connection details live, and mounts them into the service's pods. The service never learns where the database came from.

Watch it end to end: Onboard a Database, 3 min →

The shape

Producer row: a Database resource is provisioned by Crossplane as RDS, and its connection details are pushed to AWS Secrets Manager. The Database publishes only the location. Consumer row: a Release names the Database; release-operator resolves its published location into the service's Helm values; the chart renders an ExternalSecret that pulls the details from Secrets Manager; the pod reads them as files under /bindings/database. PRODUCER · the database CONSUMER · the service Database componentRef · dbName · size platform/environments/dev/ Crossplane RDS · security group · subnets connection Secret AWS Secrets Manager /bindings/dev/databases/ payments-db push Release bindings.database.ref: payments-db release-operator writes generic Helm values: type · provider · remoteRef ExternalSecret rendered by the service's chart only when the slot is on Pod /bindings/database/ host · port · user · … publishes where, not what pulls
Scroll sideways to see all of it. The highlighted arrow is the only path the credentials take to the service. The dashed arrow carries only a location.

How it works

  1. The producer publishes where, not what

    A Database asks for a size tier, not an instance class. Its Crossplane composition provisions RDS with its network pieces, pushes the connection details (endpoint, port, username, password, database name) to Secrets Manager, and records only that location in its status.

  2. Intent references by name

    The service's Release for an environment names the database to bind. Dev and prod bind different databases.

    kind: Release
    metadata: {name: payments-dev}
    spec:
      componentRef: {name: payments}
      environment: dev
      bindings:
        database: {enabled: true, ref: payments-db}
  3. Translated into generic values

    release-operator reads the database's published location and writes it into the service's Helm values. The chart only ever sees a type, a provider and a location; it never learns there was a Database resource.

    bindings:
      database:
        type: secret
        provider: aws-secrets-manager
        remoteRef: /bindings/dev/databases/payments-db
  4. Consumed as files

    With that value present, the chart's binding slot renders an ExternalSecret, mounts the resulting Secret at /bindings/database and sets SERVICE_BINDING_ROOT=/bindings, following the Service Binding for Kubernetes convention. The service reads plain files.

Design choices

Where, not what

Credentials go from the database to Secrets Manager to the pod, and nowhere else. Git, resource status and Helm values only ever carry a location. A service's schema migrations read the same location. See Database schemas.

Generic at the edge

The chart's contract is type, provider and location. A cache or a queue can reuse the same shape, and the chart never depends on how the platform models a database.

Off by default, on per environment

Every chart carries the slot, switched off. Turning it on is one field on one environment's Release, with no change to the service's code or chart.

Files, not environment variables

Mounted files follow a standard layout and update in place when the Secret changes. The scaffolded service treats a missing binding as "no database", not as a failure.

Trade-off: the scaffolded service reads the files at startup, so picking up a rotated password still needs a restart today.

Sizes, not instance classes

A Database asks for small, medium or large. The composition decides what each tier means, and region, network and engine settings aren't exposed, so they can change without changing the API.

The same pattern as clusters

A producer publishes where to find it and a consumer picks it up. Workload clusters join the hub the same way. See Hub and spoke.

Not built yet

  • Other binding types. Databases are the only one; the values contract is ready for more.
  • More than one database per Release. A Release binds a single database today.

Code