S3 Object Storage
Runway can provision a managed S3 bucket for your EKS workload and grant the workload’s pod identity (IRSA) object access to it. Runway owns the lifecycle of the bucket: it creates one bucket per environment in the Runway AWS accounts, attaches a bucket-scoped IAM policy to your workload’s role, and publishes the bucket name and region to both Vault and AWS Secrets Manager.
This guide assumes you already have a working Runway for Kubernetes deployment. If you do not, start with Getting Started first.
Unlike GCS on GKE, S3 on EKS is fully wired at deploy
time: declare the bucket in your Fairway manifest and Runway’s EKS deployment tooling handles secret
projection and mounting automatically — no manual Vault reads, no hand-written secretRef.
Overview
Section titled “Overview”Provisioning a bucket and wiring it into your deployment touches four files across two repositories:
- Define the bucket in
config/runtimes/eks/s3.yml(provisioner) — name, versioning, lifecycle, and labels. - Grant your workload access in
config/runtimes/eks/workloads.yml(provisioner) — references the bucket by itsnamevia ans3_bucketslist. - Declare the bucket in your Runway deployment manifest (
deployment-eks.yaml) — references the samenameviaspec.infrastructure.s3_object_store. - Declare the dependency in your Fairway manifest (
fairway.yaml) —spec.infrastructure.object_store.
A bucket belongs to a single service: each bucket may be referenced by at most one workload. One
bucket is provisioned per environment (staging and production), each in its own AWS account. The
physical bucket is named runway-<name>-<environment> — the runway- prefix and -<environment>
suffix are appended in Terraform so the bucket name is globally unique (S3 bucket names are unique
across every AWS account, not just GitLab’s own).
Step 1: Define the bucket
Section titled “Step 1: Define the bucket”File an MR to add your bucket to
config/runtimes/eks/s3.yml
in the Runway provisioner:
- name: example-bucket versioning: true abort_incomplete_multipart_upload_days: 7 lifecycle_rules: - id: expire-old-versions noncurrent_version_expiration: days: 90 - id: downgrade-storage-class transition: - days: 30 storage_class: STANDARD_IA labels: gl_service: example-service-eks # required owner_email_handle: my-team department: eng-infra department_group: eng-infra-my-group product_category: my-categoryField reference
Section titled “Field reference”| Field | Required | Default | Notes |
|---|---|---|---|
name |
yes | — | Must match ^[a-z0-9]([a-z0-9-]*[a-z0-9])?$ (max 45 chars). The physical bucket is runway-<name>-<environment>; the runway- prefix and -<environment> suffix are appended in Terraform. Must be unique across all buckets. |
labels |
yes | — | Required infrastructure labels. gl_service is required; owner_email_handle, department, department_group, and product_category are optional. See the Infrastructure Standards. |
versioning |
no | true |
Enable object versioning. |
abort_incomplete_multipart_upload_days |
no | 7 |
Abort incomplete multipart uploads after N days, so interrupted uploads don’t accumulate as unlisted, indefinitely-billed orphaned parts. Applied bucket-wide, independent of lifecycle_rules. |
lifecycle_rules |
no | [] |
Bucket lifecycle rules. Each rule has an id (required), enabled (default true), and any combination of expiration.days, noncurrent_version_expiration.days, transition (list of {days, storage_class}), and noncurrent_version_transition. storage_class is one of STANDARD_IA, ONEZONE_IA, INTELLIGENT_TIERING, GLACIER, DEEP_ARCHIVE, GLACIER_IR. |
Encryption is always SSE-S3 (AES256) and all public access is blocked — these are fixed and not configurable.
Create the MR and assign it to a Runway team member (see
CODEOWNERS),
and ensure the pipeline passes before requesting review.
Step 2: Grant your workload access
Section titled “Step 2: Grant your workload access”In config/runtimes/eks/workloads.yml,
add an s3_buckets key to your workload entry, referencing the bucket by its name:
- runway_service_id: example-service-eks s3_buckets: - example-bucketThis grants the workload’s IRSA role s3:ListBucket on the bucket, and
s3:GetObject/PutObject/DeleteObject/AbortMultipartUpload scoped to objects within it. Remember
that each bucket may be referenced by only one workload; the inventory validation fails if two
workloads claim the same bucket or if you reference a bucket that is not defined in s3.yml.
Step 3: Declare the bucket in your deployment manifest
Section titled “Step 3: Declare the bucket in your deployment manifest”In your service’s deployment-eks.yaml, add the bucket under spec.infrastructure.s3_object_store,
referencing it by the same name used in s3.yml:
apiVersion: runway/v2kind: RunwayManifestmetadata: name: example-service-eks
spec: infrastructure: s3_object_store: - name: example-bucketOnly one entry is supported today — additional entries are ignored with a warning at generate time.
Step 4: Declare the dependency in your Fairway manifest
Section titled “Step 4: Declare the dependency in your Fairway manifest”In your service’s fairway.yaml, declare that the service needs an object store, the same way you
would declare a Postgres or Redis dependency:
spec: infrastructure: object_store: presence: REQUIREDDo not set spec.values.infrastructure.objectStore yourself for a Runway-managed bucket —
runwayctl computes the entire block (bucket, region, and secretRef) from the deployment manifest’s
s3_object_store entry in Step 3, and overwrites anything you set here. See
Declaring an object store dependency
in the Fairway guide for the full field model, including the case of a bucket Runway does not manage.
What gets created
Section titled “What gets created”On apply, the provisioner creates, for each environment:
-
The bucket
runway-<name>-<environment>, with all public access blocked, SSE-S3 encryption, and the versioning/lifecycle configuration you declared. -
An IAM policy on the workload’s IRSA role (
eks-<runway_service_id>), scoped to the exact bucket and its objects. -
A secret published to both Vault (mount
runway, pathplatform/<runway_service_id>/s3/<bucket>/<environment>) and AWS Secrets Manager (same path), containing:{"bucket_name": "runway-example-bucket-production","region": "us-east-1"}
Consuming the bucket
Section titled “Consuming the bucket”Runway’s EKS deployment tooling:
- Creates an
ExternalSecretthat syncs the AWS Secrets Manager secret from What gets created into a Kubernetes Secret in your namespace. - Sets
values.infrastructure.objectStore.s3.secretRefto point at that Secret, with aCUSTOMprojection mapping the logicalbucket/regionfields to the Secret’sbucket_name/regionkeys. - No credentials are projected — your pod’s IRSA role supplies S3 credentials via the AWS SDK’s default credential chain.
Your service reads the bucket name and region through LabKit’s
objectstore package, the same way it would for any other object_store dependency — see
Declaring an object store dependency
in the Fairway guide for what gets mounted and where.
Next steps
Section titled “Next steps”- Review the S3 schema for the authoritative field definitions.
- See Secrets management for other secret needs your service may have.
- Ask the Runway team in
#f_runwayfor help.