Skip to content

Provision S3 Storage End-to-End

This guide walks through the complete lifecycle of setting up S3-compatible object storage: create an RGW user, inspect the credentials, create a bucket, apply a quota, and upload a file via a presigned URL.

Time to complete: ~10 minutes
Prerequisites: An organization, a project with object storage enabled, and a bearer token with rgw:create and rgw:update permissions.


Step 1 — Create an RGW user

POST /rgw/users
Authorization: Bearer <token>
Content-Type: application/json

{
  "user": {
    "name": "my-app-storage",
    "project_id": "<project-uuid>"
  }
}

The response includes the new user's id, access_key_id, and the unmasked secret_key. Store the secret key immediately — subsequent calls return it masked.

{
  "id": "b3f1c2d4-...",
  "name": "my-app-storage",
  "project_id": "<project-uuid>",
  "access_key_id": "AKIAIOSFODNN7EXAMPLE",
  "secret_key": "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY",
  "locked": false,
  "quota": {
    "max_size_kb": 524288000,
    "enabled": true
  }
}

A default quota of 500 GB is applied automatically.


Step 2 — Create a bucket

Use any S3-compatible client with the credentials from step 1. Here is an example with the AWS CLI:

aws configure --profile my-app \
  --region de-muc1 \
  --endpoint-url https://storage.muc1.de.bnerd.com

# Enter credentials when prompted:
# AWS Access Key ID: AKIAIOSFODNN7EXAMPLE
# AWS Secret Access Key: wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY

aws --profile my-app s3 mb s3://my-app-assets

Alternatively, create a bucket through the platform API via the buckets endpoint or the dashboard file browser.


Step 3 — Adjust quota (admin only)

If the default 500 GB is insufficient, an admin can raise it:

PATCH /rgw/users/<user-id>
Authorization: Bearer <admin-token>
Content-Type: application/json

{
  "quota": {
    "max_size_kb": 1073741824,
    "enabled": true
  }
}

max_size_kb: 1073741824 = 1 TB. Use -1 to remove the size limit entirely.

Admin-only

Quota changes require platform-admin privileges. Non-admin requests with a quota field receive 403 Forbidden.


Step 4 — Generate a presigned upload URL

To allow a browser or client to upload directly to RGW without routing through your backend:

POST /rgw/users/<user-id>/buckets/my-app-assets/objects/upload_url
Authorization: Bearer <token>
Content-Type: application/json

{ "key": "uploads/logo.png" }

Response:

{
  "url": "https://storage.muc1.de.bnerd.com/my-app-assets/uploads/logo.png?X-Amz-Signature=..."
}

The client can PUT the file body directly to url — no additional headers required. The URL expires in 15 minutes.

curl -X PUT "<presigned-url>" \
  -H "Content-Type: image/png" \
  --data-binary @logo.png

Step 5 — Verify the upload

aws --profile my-app \
  --endpoint-url https://storage.muc1.de.bnerd.com \
  s3 ls s3://my-app-assets/uploads/

Or via the platform API:

GET /rgw/users/<user-id>/buckets/my-app-assets/objects?prefix=uploads/
Authorization: Bearer <token>

Rotating the secret key

POST /rgw/users/<user-id>/renew_key
Authorization: Bearer <token>

The response includes the new unmasked secret_key. Update your application's environment variables or secrets manager before the old key is invalidated.

Locked users cannot rotate keys

If locked: true, renew_key is a no-op. An admin must unlock the user first.


Clean up

To delete the user and all its data:

DELETE /rgw/users/<user-id>?purge_objects=true
Authorization: Bearer <token>

Irreversible

purge_objects=true deletes every object in every bucket before removing the user. The platform records an audit event with the bucket names and total size before the purge runs. There is no undo.


Using the dashboard

All user and key management is available in the dashboard at app.bnerd.cloud:

  • Create an RGW user: Services → Object Storage → New User
  • View / rotate keys: User detail → Access Keys tab → Rotate Key
  • Browse buckets: User detail → Buckets tab — file browser included
  • Request quota increase: contact your account manager or open a support ticket

Using the CLI

# List RGW users
bnerd rgw users list

# Create a user
bnerd rgw users create my-app-storage

# Show user (includes access_key_id)
bnerd rgw users get <user-id>

# Rotate the S3 access key
bnerd rgw users renew-key <user-id>

# List buckets
bnerd rgw buckets list --user-id <user-id>

Using Terraform

resource "bnerd_rgw_user" "app" {
  name       = "my-app-storage"
  project_id = var.project_id
}

output "s3_access_key" { value = bnerd_rgw_user.app.access_key_id }
output "s3_secret_key" {
  value     = bnerd_rgw_user.app.secret_key
  sensitive = true
}

quota and locked are computed/read-only — managed by platform admins, not Terraform. See Terraform Provider reference.


Troubleshooting

Symptom Likely cause Fix
secret_key shows as masked (***) The key was already returned once and is now masked Rotate the key with POST /rgw/users/{id}/renew_key to get a new unmasked value
403 on renew_key User is locked (locked: true) Request an admin unlock before rotating
S3 client 403 AccessDenied Wrong endpoint URL or wrong credentials Verify endpoint is https://storage.muc1.de.bnerd.com, and check the access key and secret key
Bucket create fails Quota reached Check quota.max_size_kb vs current usage — request a quota increase if needed
purge_objects irreversible Expected — deletion is permanent Ensure you have a backup before deleting a user with purge_objects=true