Quickstart
In this guide, you will create a bucket and an access key, then point an S3 client at the bucket and upload your first object.
You need:
- A Nirvana API key with
editonbucketandobject_storage_key, scoped to the project you will use. See Authentication. - The ID of that project.
Only owners and admins can create API keys, so ask one to create the key if you don’t already have one. Anyone can use a key with the permissions above, including members. When signed in through a user account, members can view buckets and keys but can’t create, change or delete them.
The examples read the API key from NIRVANA_LABS_API_KEY:
export NIRVANA_LABS_API_KEY="REPLACE_WITH_YOUR_API_KEY"1. Create a Bucket
Section titled “1. Create a Bucket”curl -X POST https://api.nirvanalabs.io/v1/storage/buckets \ -H "Authorization: Bearer $NIRVANA_LABS_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "project_id": "123e4567-e89b-12d3-a456-426614174000", "name": "my-bucket", "region": "us-sva-2", "tags": ["production"] }'The request returns 202 with an operation. The operation’s resource_id is
the bucket’s ID. Follow the operation until its status is done:
curl https://api.nirvanalabs.io/v1/operations/{operation_id} \ -H "Authorization: Bearer $NIRVANA_LABS_API_KEY"Requires nirvana-cli 0.57.0 or later.
nirvana storage:buckets create \ --project-id 123e4567-e89b-12d3-a456-426614174000 \ --name my-bucket \ --region us-sva-2 \ --tag productionThe command returns an operation. Its resource_id is the bucket’s ID. Follow
the operation until its status is done:
nirvana operations get --operation-id <operation_id>Requires the Nirvana provider 1.52.41 or later. Terraform waits for the bucket to be ready.
resource "nirvana_storage_bucket" "bucket" { project_id = "123e4567-e89b-12d3-a456-426614174000" name = "my-bucket" region = "us-sva-2" tags = ["production"]}See Buckets for the naming rules.
2. Get the Endpoint and Signing Region
Section titled “2. Get the Endpoint and Signing Region”An S3 client needs two values from the bucket: the endpoint of its region and
the signing_region, the region S3 requests are signed for. The signing
region is us-east-1 today, but read it from the bucket rather than hard-coding
it.
curl https://api.nirvanalabs.io/v1/storage/buckets/{bucket_id} \ -H "Authorization: Bearer $NIRVANA_LABS_API_KEY"nirvana storage:buckets get --bucket-id <bucket_id>output "endpoint" { value = nirvana_storage_bucket.bucket.endpoint}
output "signing_region" { value = nirvana_storage_bucket.bucket.signing_region}{ "id": "123e4567-e89b-12d3-a456-426614174000", "name": "my-bucket", "project_id": "123e4567-e89b-12d3-a456-426614174000", "region": "us-sva-2", "status": "ready", "endpoint": "https://s3.sva2.nirvanacloud.io", "signing_region": "us-east-1", "url": "https://s3.sva2.nirvanacloud.io/my-bucket", "size_bytes": 0, "object_count": 0, "tags": ["production"], "created_at": "2025-01-01T00:00:00Z", "updated_at": "2025-01-01T00:00:00Z"}3. Create an Access Key
Section titled “3. Create an Access Key”Create the key in the same project and region as the bucket. The response is the only time you see the secret, so store it straight away.
curl -X POST https://api.nirvanalabs.io/v1/storage/keys \ -H "Authorization: Bearer $NIRVANA_LABS_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "project_id": "123e4567-e89b-12d3-a456-426614174000", "name": "ci-uploads", "region": "us-sva-2" }'{ "id": "123e4567-e89b-12d3-a456-426614174000", "name": "ci-uploads", "project_id": "123e4567-e89b-12d3-a456-426614174000", "region": "us-sva-2", "status": "ready", "access_key": "0555B35654AD1656D804", "secret": "h7GhxuBLTrlhVUyxSPUKUV8r/2EI4ngqJxD7iBdBYLhwluN30JaT3Q==", "created_at": "2025-01-01T00:00:00Z", "updated_at": "2025-01-01T00:00:00Z"}nirvana storage:keys create \ --project-id 123e4567-e89b-12d3-a456-426614174000 \ --name ci-uploads \ --region us-sva-2The output includes access_key and secret.
resource "nirvana_storage_key" "key" { project_id = nirvana_storage_bucket.bucket.project_id name = "ci-uploads" region = nirvana_storage_bucket.bucket.region}
output "access_key" { value = nirvana_storage_key.key.access_key}
output "secret" { value = nirvana_storage_key.key.secret sensitive = true}Read the secret with terraform output -raw secret. The provider keeps it in
state, so treat the state as sensitive.
4. Configure an S3 Client
Section titled “4. Configure an S3 Client”Use the endpoint and signing_region from step 2 and the access_key and
secret from step 3. The examples use path-style addressing, which we
recommend: it works for every bucket name, including names with dots. See
URL Styles.
Create a nirvana profile:
aws configure set aws_access_key_id <access_key> --profile nirvanaaws configure set aws_secret_access_key <secret> --profile nirvanaaws configure set region us-east-1 --profile nirvanaaws configure set endpoint_url https://s3.sva2.nirvanacloud.io --profile nirvanaaws configure set s3.addressing_style path --profile nirvanaThe AWS CLI reads endpoint_url from the profile only from version 2.13. On
older versions, pass --endpoint-url on every command, or the request goes to
AWS instead. Check your version with aws --version. The examples pass the
flag so they work on any version.
Upload, list and download:
aws s3 cp ./hello.txt s3://my-bucket/hello.txt \ --endpoint-url https://s3.sva2.nirvanacloud.io --profile nirvanaaws s3 ls s3://my-bucket/ \ --endpoint-url https://s3.sva2.nirvanacloud.io --profile nirvanaaws s3 cp s3://my-bucket/hello.txt ./hello-copy.txt \ --endpoint-url https://s3.sva2.nirvanacloud.io --profile nirvanaimport boto3from botocore.config import Config
s3 = boto3.client( "s3", endpoint_url="https://s3.sva2.nirvanacloud.io", region_name="us-east-1", aws_access_key_id="<access_key>", aws_secret_access_key="<secret>", config=Config(s3={"addressing_style": "path"}),)
s3.upload_file("hello.txt", "my-bucket", "hello.txt")
for obj in s3.list_objects_v2(Bucket="my-bucket").get("Contents", []): print(obj["Key"], obj["Size"])
s3.download_file("my-bucket", "hello.txt", "hello-copy.txt")In production, read the key from the environment or a secrets manager rather than from code.
Create a nirvana remote:
rclone config create nirvana s3 \ provider=Other \ access_key_id=<access_key> \ secret_access_key=<secret> \ endpoint=https://s3.sva2.nirvanacloud.io \ region=us-east-1 \ force_path_style=trueThe remote is saved in your rclone config file:
[nirvana]type = s3provider = Otheraccess_key_id = <access_key>secret_access_key = <secret>endpoint = https://s3.sva2.nirvanacloud.ioregion = us-east-1force_path_style = trueUpload, list and download:
rclone copy ./hello.txt nirvana:my-bucket/rclone ls nirvana:my-bucketrclone copy nirvana:my-bucket/hello.txt ./downloads/Other S3 clients and SDKs need the same four values: endpoint, region (the signing region), access key and secret, with path-style addressing turned on.
Troubleshooting
Section titled “Troubleshooting”AWS CLI Prints argument of type 'NoneType' is not iterable
Section titled “AWS CLI Prints argument of type 'NoneType' is not iterable”This message means the request was refused. The AWS CLI doesn’t show the reason. Common causes:
- The bucket name is wrong.
- The access key belongs to another project or region. A key works only for buckets in its own project and region. See Scope.
- The access key was deleted.
To see the reason, rerun the command with --debug and look for <Code> in
the response body:
aws s3 ls s3://my-bucket/ \ --endpoint-url https://s3.sva2.nirvanacloud.io --profile nirvana \ --debug 2>&1 | grep '<Code>'The code names the error, for example NoSuchBucket, InvalidAccessKeyId,
SignatureDoesNotMatch or AccessDenied.
Alternatively, run the same operation with rclone, which prints the error. With
the nirvana remote from step 4:
rclone ls nirvana:my-bucketNext Steps
Section titled “Next Steps”- Buckets: tags, and deleting a bucket.
- Access Keys: rotating and deleting keys.
- Billing and Limits: what you pay and the storage quota.