Skip to content

Connections

Modules on a Dedicated tenant sometimes talk to each other, or to the tenant’s GitLab instance. Your module declares the peer modules it initiates connections to, and Runway delivers to your pods what each connection needs. Today that is one secret per peer, which Dedicated’s Instrumentor publishes for the modules it has been configured for: a service token that both ends must agree on, for example. When Runway deploys your module it copies the secret of each peer into your module’s namespace, mounts it into your pods at a well-known path, and tells your service where to find it.

Your module also always receives its own secret, without declaring it. That’s the other end of the connections that peers initiate to your module: your service uses it to verify the token a caller presents.

Runway doesn’t know what the connection is for. It copies the secret as it is and leaves it to the two ends to agree on what the token authorises.

List the peer modules under spec.connections.modules.set in the module manifest, by their Runway service ID. The module manifest is the runway/v3 Runway manifest of your module on Dedicated, in .runway/ in a file named module.yaml or module-<suffix>.yaml (.yml works too), and names your Dedicated service ID in metadata.name:

.runway/module.yaml
apiVersion: runway/v3
kind: RunwayManifest
metadata:
name: example-service-dedicated
spec:
connections:
modules:
set:
- name: iam-data-dedicated

Each entry of modules.set needs a name, the peer’s Runway service ID. Names must be unique, and a name can’t start with s3-, rds- or redis-: Runway reserves those prefixes for the Secrets of your module’s own infrastructure. A module can’t list itself: Runway always delivers its own secret, so there’s nothing to declare.

Leave connections out if your module needs no peers; it still receives its own secret. runwayctl catalog create validates the declaration in the merge request pipeline and copies it into the Module Catalog entry of each release. The catalog entry is the allow-list: on a tenant, Runway copies the secrets of the peers it names, your module’s own, and nothing else. catalog create reads every module*.yaml file under .runway/ to find yours, so if several modules share the directory, an invalid manifest of another module fails your pipeline too.

When Runway deploys a release to a tenant, it reads the secret of each peer, and then your module’s own, from the tenant’s AWS Secrets Manager at runway/shared/<name>, where Instrumentor stores it as a JSON object of strings. For each of them, it then:

  1. creates a Secret named <name> in your module’s namespace, with the source’s keys as they are, today the single token;
  2. adds a secretVolumes entry to your Helm values that mounts the Secret at /secrets/connections/modules/<name>;
  3. sets the environment variable MODULE_CONNECTION_SECRETS_PATH to /secrets/connections/modules.

For the manifest above, your service reads the peer’s token from $MODULE_CONNECTION_SECRETS_PATH/iam-data-dedicated/token, that is /secrets/connections/modules/iam-data-dedicated/token, and its own token from $MODULE_CONNECTION_SECRETS_PATH/example-service-dedicated/token. The directory is fixed, like /secrets/infrastructure for the backend secrets; read the variable rather than hard-coding the path.

The Secrets are created together with the other Runway resources, before the module’s Helm chart is installed, so they exist when your pods start. Runway copies them on every deployment: a token that Instrumentor changes reaches your module at the next deployment of the module on that tenant. Runway doesn’t force a restart of your pods when it copies a changed token: your module is expected to detect the change at runtime, by re-reading the file, which the kubelet refreshes when the Secret changes.

A secret, your module’s own or a peer’s, that doesn’t exist on the tenant, or isn’t a JSON object of strings with at least one key, fails the deployment before anything is changed in the cluster. The error names the secret, never its value. A missing secret usually means that Instrumentor hasn’t been configured to publish it on that tenant. Because every module receives its own secret, Instrumentor must publish one for every module deployed to the tenant, even a module without peers.

A tenant can change a module’s connections with values_override.connections in its Tenant Model. It has the same shape as the manifest’s connections, with one more option: modules.add appends peers to the release’s list instead of replacing it.

Tenant Model
{
"modular_features": {
"modules": {
"example-service-dedicated": {
"enabled": true,
"version": "v1.25.0",
"values_override": {
"connections": {
"modules": { "add": [{ "name": "artifact-registry-dedicated" }] }
}
}
}
}
}
}
Override Effect
modules absent The release’s peers are kept.
modules.set Replaces the release’s peers.
modules.add Appends to the release’s peers. Names must stay unique.
modules: {} Removes every peer. Your module still receives its own secret.

As with Helm value overrides, this is an escape hatch for one tenant. Peers that every tenant needs belong in your manifest, where they are released and reviewed with the module.