Skip to content

S3

Runway can provision an Amazon S3 bucket in the AWS account of every Dedicated tenant that enables your module, and wire it into your Helm chart through the Fairway infrastructure.objectStore contract. Runway owns the bucket: runwayctl dedicated provision creates it, and runwayctl dedicated deploy tells your workload where to find it. You don’t create the bucket or its secret yourself.

This page assumes your module is set up for Dedicated. If it isn’t, start with Getting Started.

A bucket is configured in three places, and each setting comes from the first one that sets it:

  1. The tenant’s override in the Tenant Model, under modular_features.modules.<module-name>.values_override.infrastructure.s3.<name>.
  2. Your default in the module manifest, under spec.infrastructure.s3.<name>.defaultConfig, which catalog create copies into the Module Catalog entry.
  3. The built-in default in the field reference.

<name> is the bucket’s logical name, a name of your choice that the module manifest and the Tenant Model share. Entries are merged by logical name and then field by field, so a tenant only sets what differs. Whether your module needs a bucket at all is its object_store presence in the Fairway manifest.

Step 1: Declare the dependency in your Fairway manifest

Section titled “Step 1: Declare the dependency in your Fairway manifest”
.runway/fairway.yaml
spec:
infrastructure:
object_store:
presence: REQUIRED
Presence Module manifest Tenant Model, after the merge
REQUIRED Needs at least one enabled default. Needs exactly one enabled bucket.
OPTIONAL Any defaults, enabled or not, or none. At most one enabled bucket.
UNUSED Must have no defaults, not even disabled ones. Must have no enabled bucket.

catalog create checks the module manifest against the presence when you release the module. provision and deploy check the merged result on every tenant.

Don’t set spec.values.infrastructure.objectStore.s3.secretRef yourself. Runway sets it, and overwrites anything you put there with a warning. It also replaces a GCS backend, with a warning. Other objectStore settings, such as name, are yours to set and are kept.

Step 2: Set the defaults in your module manifest

Section titled “Step 2: Set the defaults in your module manifest”
.runway/module.yaml
apiVersion: runway/v3
kind: RunwayManifest
metadata:
name: example-service-dedicated
spec:
infrastructure:
s3:
uploads:
defaultConfig:
abortIncompleteMultipartUploadDays: 3
lifecycleRules:
set:
- id: expire-tmp
expiration:
days: 30

A defaultConfig must be valid on its own, without any override, including when it is disabled. defaultConfig: {} is valid and gives the built-in defaults. Lifecycle rules must use set: add is only meaningful in the Tenant Model. Field names can be in camelCase or snake_case.

A tenant that is happy with your defaults needs no infrastructure block. To change a setting for one tenant, set only that field:

Tenant Model
{
"modular_features": {
"modules": {
"example-service-dedicated": {
"enabled": true,
"version": "v1.26.0",
"values_override": {
"infrastructure": {
"s3": {
"uploads": { "versioning": false }
}
}
}
}
}
}
}

The override is partial: it only has to parse, with the right types and no unknown fields. The merged bucket is validated as a complete configuration, where an unset field stands for its built-in default. A tenant can also add a bucket that has no default, which must then be complete on its own.

For this tenant, the bucket gets versioning: false from the Tenant Model, abort_incomplete_multipart_upload_days: 3 and the expire-tmp rule from the module manifest, and enabled: true from the built-in defaults.

InfrastructureConfigS3 in runwayctl’s s3.proto. Unknown fields are rejected.

Field Type Default Description
enabled boolean true Provisions the bucket. See Enabling and disabling.
versioning boolean true Enables object versioning.
abort_incomplete_multipart_upload_days integer 7 Aborts incomplete multipart uploads after this many days. At least 1.
lifecycle_rules object none Lifecycle rules of the bucket.

Logical names are lowercase letters, digits, and hyphens, starting and ending with a letter or digit, at most 45 characters.

lifecycle_rules has two lists, set and add. Setting both is an error.

Override Result
lifecycle_rules unset The default rules are kept.
set with rules The default rules are replaced.
set: [], add: [], or lifecycle_rules: {} The bucket has no rules.
add with rules The rules are appended to the default rules, or are the only rules if there are no defaults.
set and add both with rules Error.

Rules are replaced or appended as a whole, never merged by id. To change one default rule, replace the whole list with set. Rule IDs must be unique in the merged result, so an appended rule can’t reuse the ID of a default rule.

An empty add: [] clears the list, just like set: []: an empty list can’t be told apart from an unset one.

Append a rule to the defaults
{
"s3": {
"uploads": {
"lifecycle_rules": {
"add": [
{
"id": "archive",
"transitions": [
{ "days": 30, "storage_class": "STANDARD_IA" },
{ "days": 90, "storage_class": "GLACIER_IR" }
]
}
]
}
}
}
}

Each rule:

Field Type Description
id string Required. Rule ID. abort-incomplete-multipart-uploads is reserved for the rule that abort_incomplete_multipart_upload_days adds.
enabled boolean Enables the rule. Defaults to true.
expiration.days integer Deletes current-version objects after this many days. days is required if expiration is set.
noncurrent_version_expiration.days integer Deletes noncurrent (superseded) versions after this many days. days is required if noncurrent_version_expiration is set.
transitions array Moves current-version objects to another storage class.
noncurrent_version_transitions array Moves noncurrent versions to another storage class.

Each entry of transitions and noncurrent_version_transitions needs both fields:

Field Type Description
days integer Moves objects after this many days.
storage_class string One of STANDARD_IA, ONEZONE_IA, INTELLIGENT_TIERING, GLACIER, DEEP_ARCHIVE, GLACIER_IR.

Every bucket has an enabled field. Unset means enabled.

  • Off by default: set enabled: false in the defaultConfig. The defaults are still checked, so that they are sane when a tenant turns the bucket on with enabled: true, without repeating the other settings. A REQUIRED object store can’t be off by default.
  • Off for one tenant: the tenant sets enabled: false. Runway doesn’t provision the bucket, deploys no Secret and no Helm values for it, and doesn’t check its other settings. For a REQUIRED object store, this is an error.

provision creates, in the tenant’s AWS account and region:

  • The bucket <tenant-id>-<module-name>-<name>-<digest>. The digest is the first 8 hex characters of the SHA-256 of <account>/<tenant-id>/<module-name>/<name>, so two tenants or modules never collide. If the name exceeds 63 characters, <module-name>-<name> is shortened, keeping the tenant prefix and the digest. The bucket is encrypted with the tenant’s customer-managed S3 key from the Tenant Model, or with SSE-S3 if it has none.
  • The secret runway/<module-name>/object_store/<name> in AWS Secrets Manager, with the keys bucket and region.

deploy then:

  • writes the Kubernetes Secret s3-<name> in the module’s namespace, with the keys bucket and region;
  • points infrastructure.objectStore.s3.secretRef in your Helm values at it.

The Secret holds no credentials: workloads use the AWS SDK default credential chain.