Use buckets from Cloider apps and Kubernetes
Apps and Kubernetes workloads on Cloider reach buckets the same way any other program does: with the S3 endpoint and an access key, passed in as environment variables. This page shows how to create a key for a workload, hand it to an App Engine app or a Kubernetes cluster, and use it from code.
The environment variables
Every AWS SDK, the AWS CLI and most S3 libraries read these variables on their own, so your code doesn't need any Cloider specific configuration:
AWS_ACCESS_KEY_ID=CLDRZ8Q4M2X7KD1PLW0B
AWS_SECRET_ACCESS_KEY=7hT2kV9qWm4LxR8zNc3PbY6sJd1FgA5eUo0iKlMn
AWS_REGION=eu-central-2
AWS_ENDPOINT_URL_S3=https://s3.eu-central-2.cloider.com
AWS_REQUEST_CHECKSUM_CALCULATION=when_required
AWS_RESPONSE_CHECKSUM_VALIDATION=when_required
S3_BUCKET=acme-assets
| Variable | Read by | Purpose |
|---|---|---|
AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY | All AWS SDKs and the AWS CLI | The access key |
AWS_REGION | All AWS SDKs and the AWS CLI | The region requests are signed for |
AWS_ENDPOINT_URL_S3 | Recent AWS SDKs and the AWS CLI | Sends S3 requests to Cloider. AWS_ENDPOINT_URL does the same for every AWS service |
AWS_REQUEST_CHECKSUM_CALCULATION, AWS_RESPONSE_CHECKSUM_VALIDATION | AWS SDKs and CLI from 2025 | Keeps uploads compatible, see Troubleshooting |
S3_BUCKET | Your code | The bucket name. A convention, not read by SDKs |
Frameworks often use their own names (Laravel's AWS_BUCKET and AWS_ENDPOINT, for example). See SDKs for framework configurations.
Create a key for the workload
Give each workload its own key, scoped to the buckets it uses, so you can rotate or revoke it on its own:
cloider bucket key create --label api-production --bucket acme-assets
Use a read only key (--read-only) for workloads that only read, such as a service that serves images. See Access keys.
Use a bucket from an App Engine app
An app gets the key as environment variables. Store the secret key as a secret variable, so it is never listed back. Changing variables restarts the app.
- Dashboard
- CLI
- Terraform
- API
- MCP
- Create an access key for the bucket and copy its access key id and secret.
- Open Apps in the sidebar, click the app, and select the Environment tab.
- Add the variables from the list above, with
AWS_SECRET_ACCESS_KEYas a secret. - Save. The app restarts with the new variables.
Create a key and import it straight into the app as secret variables. --print-env prints export lines, which cloider app env import reads from standard input:
cloider bucket key create --label api-production --bucket acme-assets --print-env \
| cloider app env import api - --secret
Then add the plain settings:
cloider app env set api \
S3_BUCKET=acme-assets \
AWS_REQUEST_CHECKSUM_CALCULATION=when_required \
AWS_RESPONSE_CHECKSUM_VALIDATION=when_required
The imported variables are AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_ENDPOINT_URL and AWS_REGION.
The Cloider provider manages the bucket and the key. Set the app's variables from their outputs with the CLI, for example in the same CI job:
resource "cloider_bucket" "assets" {
name = "acme-assets"
}
resource "cloider_bucket_key" "api" {
label = "api-production"
buckets = [cloider_bucket.assets.id]
}
output "s3_access_key" {
value = cloider_bucket_key.api.access_key
}
output "s3_secret_key" {
value = cloider_bucket_key.api.secret_key
sensitive = true
}
cloider app env set api --secret \
AWS_ACCESS_KEY_ID="$(terraform output -raw s3_access_key)" \
AWS_SECRET_ACCESS_KEY="$(terraform output -raw s3_secret_key)"
Create the key with POST /api/bucket-keys (see Access keys), then set the app's variables with the apps API. See Apps API.
Create an access key for the bucket acme-assets and give it to the app api as secret environment variables, with S3_BUCKET set too.
The agent calls create_bucket_key, then set_app_variables:
{
"app": "api",
"variables": {
"AWS_ACCESS_KEY_ID": "CLDRZ8Q4M2X7KD1PLW0B",
"AWS_SECRET_ACCESS_KEY": "7hT2kV9qWm4LxR8zNc3PbY6sJd1FgA5eUo0iKlMn"
},
"secret": true
}
The secret passes through the conversation this way. For production keys, consider the CLI pipe shown in the CLI tab.
Use a bucket from a Kubernetes cluster
In a Kubernetes cluster, store the key in a Secret and load it into your pods as environment variables. Pods reach the bucket on its public HTTPS endpoint, like any other client.
apiVersion: v1
kind: Secret
metadata:
name: s3
namespace: default
type: Opaque
stringData:
AWS_ACCESS_KEY_ID: CLDRZ8Q4M2X7KD1PLW0B
AWS_SECRET_ACCESS_KEY: 7hT2kV9qWm4LxR8zNc3PbY6sJd1FgA5eUo0iKlMn
AWS_REGION: eu-central-2
AWS_ENDPOINT_URL_S3: https://s3.eu-central-2.cloider.com
AWS_REQUEST_CHECKSUM_CALCULATION: when_required
AWS_RESPONSE_CHECKSUM_VALIDATION: when_required
S3_BUCKET: acme-assets
Or create it without writing the secret to a file:
kubectl create secret generic s3 \
--from-literal=AWS_ACCESS_KEY_ID=CLDRZ8Q4M2X7KD1PLW0B \
--from-literal=AWS_SECRET_ACCESS_KEY="$S3_SECRET_KEY" \
--from-literal=AWS_REGION=eu-central-2 \
--from-literal=AWS_ENDPOINT_URL_S3=https://s3.eu-central-2.cloider.com \
--from-literal=AWS_REQUEST_CHECKSUM_CALCULATION=when_required \
--from-literal=AWS_RESPONSE_CHECKSUM_VALIDATION=when_required \
--from-literal=S3_BUCKET=acme-assets
Load every key of the Secret into a Deployment's containers with envFrom:
apiVersion: apps/v1
kind: Deployment
metadata:
name: api
spec:
replicas: 2
selector:
matchLabels:
app: api
template:
metadata:
labels:
app: api
spec:
containers:
- name: api
image: registry.cloider.com/acme/api:1.0
envFrom:
- secretRef:
name: s3
kubectl apply -f s3-secret.yaml -f deployment.yaml
Check from inside a pod that the bucket is reachable:
kubectl run s3-check --rm -it --restart=Never --image=amazon/aws-cli \
--overrides='{"spec":{"containers":[{"name":"s3-check","image":"amazon/aws-cli","args":["s3","ls","s3://acme-assets"],"envFrom":[{"secretRef":{"name":"s3"}}]}]}}'
With Terraform
The cloider_bucket_key outputs can feed a kubernetes_secret_v1 directly, so the secret never leaves Terraform:
resource "cloider_bucket" "assets" {
name = "acme-assets"
}
resource "cloider_bucket_key" "api" {
label = "api-production"
buckets = [cloider_bucket.assets.id]
}
resource "kubernetes_secret_v1" "s3" {
metadata {
name = "s3"
namespace = "default"
}
data = {
AWS_ACCESS_KEY_ID = cloider_bucket_key.api.access_key
AWS_SECRET_ACCESS_KEY = cloider_bucket_key.api.secret_key
AWS_REGION = cloider_bucket.assets.region
AWS_ENDPOINT_URL_S3 = cloider_bucket.assets.endpoint
AWS_REQUEST_CHECKSUM_CALCULATION = "when_required"
AWS_RESPONSE_CHECKSUM_VALIDATION = "when_required"
S3_BUCKET = cloider_bucket.assets.name
}
}
Configure the kubernetes provider for your cluster as shown in Kubernetes and Helm with Terraform.
Use the bucket from code
With the variables in place, a client created without options talks to Cloider:
- JavaScript
- Python
- Go
import { S3Client, PutObjectCommand } from "@aws-sdk/client-s3";
const s3 = new S3Client({}); // reads AWS_* from the environment
await s3.send(new PutObjectCommand({
Bucket: process.env.S3_BUCKET,
Key: "uploads/hello.txt",
Body: "Hello from my app",
}));
import os
import boto3
s3 = boto3.client("s3") # reads AWS_* from the environment
s3.put_object(Bucket=os.environ["S3_BUCKET"], Key="uploads/hello.txt", Body=b"Hello from my app")
cfg, err := config.LoadDefaultConfig(ctx) // reads AWS_* from the environment
if err != nil {
log.Fatal(err)
}
client := s3.NewFromConfig(cfg)
_, err = client.PutObject(ctx, &s3.PutObjectInput{
Bucket: aws.String(os.Getenv("S3_BUCKET")),
Key: aws.String("uploads/hello.txt"),
Body: strings.NewReader("Hello from my app"),
})
This relies on SDK versions that read AWS_ENDPOINT_URL_S3 and the checksum variables. With older versions, set the endpoint in code as shown in SDKs.
Good practices
- One key per workload and environment.
api-stagingandapi-productionwith their own keys, scoped to their own buckets (for exampleacme-assets-stagingandacme-assets). - Keep the secret out of images and repositories. Use secret app variables or Kubernetes Secrets, never a
DockerfileENVor a committed.envfile. - Rotate without downtime. Create the new key, update the variables or the Secret (apps restart on their own, Deployments need
kubectl rollout restart deployment/api), then delete the old key. See Rotate an access key. - Serve public files by URL. If the app only hands files to browsers, a public bucket or presigned URLs avoid streaming them through the app.
Next steps
- Back up a database to a bucket.
- Build a full stack app with PostgreSQL.
- Deploy a workload to Kubernetes.
Frequently asked questions
How do I connect a Cloider app to a bucket?
Create an access key for the bucket, then set AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY (as a secret), AWS_REGION=eu-central-2, AWS_ENDPOINT_URL_S3=https://s3.eu-central-2.cloider.com and the two checksum variables as the app's environment variables. The AWS SDKs read them on their own.
How do I give a Kubernetes pod access to a Cloider bucket?
Store the access key, region, endpoint and checksum settings in a Kubernetes Secret and load it into the container with envFrom and secretRef. Pods reach the bucket on its public HTTPS endpoint.
Do apps and clusters get access to buckets automatically?
No. Every workload needs an access key, which you pass in as environment variables. Give each workload its own key, scoped to the buckets it uses.