Skip to content

Storage providers

S3 Lens works with any storage that exposes an S3-compatible API. Add providers under the providers key in s3lens.yaml. See Configuration for the full field reference.

S3 Lens is tested locally against Garage and SeaweedFS. The following providers are supported through the generic S3 client or GCS-specific settings:

Provider Config type Notes
AWS S3 generic Standard regional endpoints
Google Cloud Storage gcp HMAC keys via S3-compatible XML API
Hetzner Object Storage generic S3-compatible endpoint
Garage generic Self-hosted; path_style: true
SeaweedFS generic Self-hosted; path_style: true
MinIO generic Optional local testing target
Other S3-compatible generic Custom endpoint and credentials

Most providers use the same shape:

providers:
- name: "my-storage"
type: generic
endpoint: "https://s3.example.com"
region: "us-east-1"
path_style: true
credentials:
access_key_id: "${S3_ACCESS_KEY_ID}"
secret_access_key: "${S3_SECRET_ACCESS_KEY}"
providers:
- name: "aws-prod"
endpoint: "https://s3.amazonaws.com"
region: "eu-west-1"
credentials:
access_key_id: "${AWS_ACCESS_KEY_ID}"
secret_access_key: "${AWS_SECRET_ACCESS_KEY}"

Use the regional endpoint that matches your buckets. Virtual-hosted-style URLs (path_style: false or omitted) typically work with AWS; self-hosted providers usually need path_style: true.

See Provider credentials and permissions for the IAM actions your access key needs.

Garage is a lightweight self-hosted object store. Example local setup:

providers:
- name: "garage-local"
endpoint: "http://127.0.0.1:3900"
region: "garage"
path_style: true
credentials:
access_key_id: "${GARAGE_ACCESS_KEY_ID}"
secret_access_key: "${GARAGE_SECRET_ACCESS_KEY}"

Create an access key and grant bucket permissions with the Garage CLI:

Terminal window
garage key create s3lens
garage bucket create my-bucket
garage bucket allow --read --write my-bucket --key s3lens

The repository’s docker-compose.yml includes a Garage profile for local testing (just compose-garage-up).

SeaweedFS exposes an S3 API on port 8333 by default:

providers:
- name: "seaweed-local"
endpoint: "http://127.0.0.1:8333"
region: "us-east-1"
path_style: true
credentials:
access_key_id: "${SEAWEED_ACCESS_KEY_ID}"
secret_access_key: "${SEAWEED_SECRET_ACCESS_KEY}"

Use the S3 credentials configured in your SeaweedFS filer setup. The Compose profile in this repository provides a ready-made local instance.

providers:
- name: "minio-local"
endpoint: "http://127.0.0.1:9000"
region: "us-east-1"
path_style: true
credentials:
access_key_id: "${MINIO_ACCESS_KEY}"
secret_access_key: "${MINIO_SECRET_KEY}"

MinIO is optional for local compatibility testing. Start it with docker compose --profile minio up -d (console at http://localhost:9001).

providers:
- name: "hetzner"
endpoint: "https://fsn1.your-objectstorage.com"
region: "fsn1"
path_style: true
credentials:
access_key_id: "${HETZNER_ACCESS_KEY}"
secret_access_key: "${HETZNER_SECRET_KEY}"

Use the endpoint and region from your Hetzner project dashboard.

Each provider entry includes an access key and secret that S3 Lens uses on the server to call the storage API. These credentials are not shown to users in the UI or API responses.

S3 Lens has two separate permission layers:

  1. Provider credentials (this section) — what the storage backend allows the S3 Lens server to do
  2. Access policies — what each signed-in user is allowed to do in S3 Lens

Both must align. If a user has write access in S3 Lens but the provider key is read-only, uploads and deletes fail with errors from the storage backend.

Scope provider credentials to the buckets S3 Lens should manage. Prefer keys or IAM policies limited to those buckets rather than account-wide admin access.

Feature S3 API call Permission needed
List buckets ListBuckets List all buckets in the account/project
Create bucket CreateBucket Create buckets
Delete bucket DeleteBucket Delete empty buckets
List objects / folders ListObjectsV2 List objects in a bucket
Download / bulk ZIP GetObject Read objects
Upload PutObject Write objects
Copy CopyObject Read source object; write destination object
Rename CopyObject + DeleteObject Read, write, and delete objects
Delete / bulk delete DeleteObject, DeleteObjects Delete objects
Presigned URL Presigned GetObject Read objects (used to sign URLs)

On AWS and Hetzner Object Storage, map the table above to IAM Action values:

Capability IAM actions Resource
Browse buckets and objects s3:ListAllMyBuckets, s3:ListBucket, s3:GetObject, s3:GetBucketLocation * for list-buckets; bucket ARN for list; object ARN for get
Upload, copy, rename s3:PutObject, s3:GetObject (source for copy/rename) arn:aws:s3:::bucket-name/*
Delete objects s3:DeleteObject arn:aws:s3:::bucket-name/*
Create / delete buckets s3:CreateBucket, s3:DeleteBucket bucket ARNs as needed

Read-only example (single bucket):

{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": ["s3:ListAllMyBuckets", "s3:GetBucketLocation"],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": ["s3:ListBucket"],
"Resource": "arn:aws:s3:::my-bucket"
},
{
"Effect": "Allow",
"Action": ["s3:GetObject"],
"Resource": "arn:aws:s3:::my-bucket/*"
}
]
}

Read/write example (single bucket, including copy, rename, and presigned URLs):

{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": ["s3:ListAllMyBuckets", "s3:GetBucketLocation"],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": ["s3:ListBucket", "s3:CreateBucket", "s3:DeleteBucket"],
"Resource": "arn:aws:s3:::my-bucket"
},
{
"Effect": "Allow",
"Action": ["s3:GetObject", "s3:PutObject", "s3:DeleteObject"],
"Resource": "arn:aws:s3:::my-bucket/*"
}
]
}

For multiple buckets, add one statement block per bucket or use wildcard bucket names if your provider supports them. Managed policies such as AmazonS3ReadOnlyAccess work for read-only browsing; for writes, prefer a custom policy scoped to specific buckets over AmazonS3FullAccess.

Temporary credentials (STS) are supported via credentials.session_token when your deployment supplies them.

GCS HMAC keys inherit permissions from the service account or user they belong to. Grant IAM on the buckets or project:

Capability Example IAM role
Browse buckets and objects roles/storage.objectViewer on the bucket or project
Upload, delete, copy, rename roles/storage.objectAdmin on the bucket
Create and delete buckets roles/storage.admin at project level, or bucket-level admin as appropriate

For read-only access to specific buckets, bind roles/storage.legacyBucketReader and roles/storage.objectViewer on those buckets rather than granting project-wide admin.

Garage uses per-key bucket ACLs. After creating a key, grant access on each bucket:

Terminal window
garage bucket allow --read my-bucket --key s3lens # browse and download
garage bucket allow --read --write my-bucket --key s3lens # upload, copy, rename, delete

To create or delete buckets through S3 Lens, the key needs permission to manage buckets in your Garage layout (typically the same key with write access, plus bucket-admin rights in your Garage setup).

These accept standard S3 access keys. Configure the key in your filer or MinIO policy with the same action set as the AWS table above — at minimum ListBucket, GetObject, PutObject, and DeleteObject on the buckets S3 Lens uses. MinIO policy documents use the same s3:* action names as AWS.

For local development, admin credentials from the Compose profiles are sufficient. In production, create a dedicated user or policy scoped to the required buckets.

Use case Provider credentials
Read-only file browser List + get only
Full object management (no bucket admin) List + get + put + delete on target buckets
Bucket create/delete in the UI Above plus create/delete bucket
Presigned URL sharing Get on objects users may link to

Match provider credentials to the most capable access policy you assign to any user. You can use a narrower provider key if all users are read-only, or a broader key if some users need write access — S3 Lens policies then restrict who can trigger each operation.

S3 Lens connects to GCS through Google’s S3-compatible XML API using HMAC keys — not OAuth or service-account JSON files.

  1. In Cloud Console → Storage → Settings → Interoperability, create an HMAC key for a service account (preferred) or user account.
  2. Grant that principal IAM roles on the buckets you need — see Provider credentials and permissions for role guidance.
  3. Add a provider block:
providers:
- name: "gcp-playground"
type: gcp
project_id: "${GCS_PROJECT_ID}"
endpoint: "https://storage.googleapis.com"
region: "auto"
path_style: true
credentials:
access_key_id: "${GCS_ACCESS_KEY_ID}"
secret_access_key: "${GCS_SECRET_ACCESS_KEY}"

When type is gcp, S3 Lens defaults endpoint to https://storage.googleapis.com and path_style to true if omitted. project_id is required and is sent as x-amz-project-id on S3 XML API calls.

Setting Value Notes
Endpoint https://storage.googleapis.com Global XML API endpoint
Region auto Google’s S3 SDK guidance recommends auto; bucket location (e.g. europe-north2) also works
Path-style true Avoids virtual-hosted certificate issues
Auth HMAC access key + secret Session tokens are not used for GCS HMAC
  • HMAC only — OAuth and Application Default Credentials are not supported through this path
  • Uniform bucket-level access — works normally; ACL headers on upload are ignored when uniform access is enabled
  • Organization policyconstraints/storage.restrictAuthTypes can block HMAC keys; contact your GCP admin if you see 403 Forbidden
  • List page size — GCS returns at most 500 keys per list call via the S3 API; S3 Lens paginates automatically
  • Bucket create/delete — may require location constraints and project permissions beyond object CRUD
  • Unsupported S3 features — bucket policies, event notifications, and requester-pays differ from AWS (see Google’s migration docs)
Symptom Things to check
SignatureDoesNotMatch Verify credentials, region, and path_style; for GCS, confirm HMAC keys are active
Cannot list buckets Confirm the access key has list permissions; check endpoint URL
SSL/certificate errors Try path_style: true for self-hosted providers
Provider missing in UI Check the provider name in config; verify credentials env vars are set
403 on GCS HMAC keys may be blocked by org policy; verify IAM roles on buckets

After changing providers in s3lens.yaml, restart S3 Lens. Provider credentials are never returned by the API — use GET /api/providers to confirm a provider is configured without exposing secrets.

You can connect several backends at once. Use access policies to control which users see which providers:

policy:
policies:
garage-readonly:
allow:
- actions: [read]
resource: { provider: "garage-local", bucket: "*", prefix: "*" }

Users with this policy can browse Garage but not other configured providers (unless additional policies grant access).