Migrating production infrastructure? Get up to ₹10,000 in migration credits. Apply now
voroaDocs
Object Storage

Object Storage: S3-compatible buckets for files and uploads

How Voroa Object Storage works: create a bucket, connect with any S3 tool or SDK, read your usage, and what happens above your plan, when unpaid, and on delete.

What it is

A bucket is S3-compatible storage for files: user uploads, images, exports and backups your application makes. Any tool or SDK that speaks the S3 API works with it - the AWS CLI, rclone, the AWS SDK for JavaScript, and boto3 for Python.

Buckets are stored in the Asia-Pacific region, best effort. India is not guaranteed. Files in a bucket are not backed up by Voroa - keep your own copy of anything you cannot lose.

Finding your way around

Object Storage in the dashboard sidebar opens its own sidebar. Overview lists your buckets with their status, plan and storage used. Usage and billing shows every bucket’s use against its plan this month. Open a bucket and the sidebar switches to that bucket’s pages:

Page What it is for
Objects Browse, upload, share and delete files
Metrics This month’s storage, transfer and requests against the plan
Connect Credentials, code examples, and adding the bucket to a service
API keys Extra keys (read-only, or one folder) and short-lived credentials
Custom domains Serve a public bucket on your own domain
Event notifications Webhooks on upload and delete, and the delivery log
Import Copy files in from another provider
Lifecycle rules Delete files on their own after some days or on a date
Access logs Every request in the last 7 days
Settings Main access key, public access, CORS, retention lock, delete the bucket

A page for a feature that is not available on your account is not shown.

How Object Storage works in three steps: create a bucket and pick a plan, connect your service with the S3 endpoint and keys as environment variables, then upload and share files with any S3 tool or SDK

Create a bucket

  1. Open New and choose Object Storage.
  2. Pick a name: 3 to 63 lowercase letters, numbers and single hyphens, starting and ending with a letter or number. It must be unique in your workspace and cannot be changed later.
  3. Pick a storage plan. Each plan includes an amount of storage, an amount of storage transfer, and a monthly number of upload and download requests. The price is shown before you pay.
  4. Pay. The bucket is created as soon as the payment is confirmed, usually within a minute.

After creation you see the secret access key once. Copy it then. If you lose it, regenerate the key - the old key stops working within about a minute.

Connect from your service

Add these as environment variables on the service that uses the bucket, then redeploy it:

S3_ENDPOINT=https://storage.voroafiles.com
S3_BUCKET=<your bucket name>
S3_ACCESS_KEY_ID=<access key id>
S3_SECRET_ACCESS_KEY=<secret access key>

Use region auto and path-style addressing (in the AWS SDK for JavaScript, forcePathStyle: true; in rclone, force_path_style = true). The bucket’s Connect page has copy-ready examples for each tool.

Per-bucket endpoint

Every bucket also answers on its own address, https://<bucket id>-s3.voroafiles.com, where the bucket id is the one in the dashboard URL. On that address the path is just the file key: https://<bucket id>-s3.voroafiles.com/photos/a.jpg. Use it with tools that take a full bucket URL, such as curl with SigV4 signing or a presigned link. The bucket’s own key works there, and a key for another bucket is refused.

Path-style on https://storage.voroafiles.com keeps working, and stays the setting to use with SDKs. An SDK in virtual-hosted mode builds <bucket name>.<endpoint>, and that address does not exist.

Short-lived credentials

To give a job, a CI run or a browser access that expires on its own, request temporary credentials for one bucket:

POST /api/buckets/<bucket id>/session-credentials
{ "ttlSeconds": 3600, "access": "read", "prefix": "reports/" }
  • ttlSeconds: from 900 (15 minutes) to 43200 (12 hours).
  • access: read (download and list only) or readwrite.
  • prefix (optional): limits every file key, every listing and every copy source to keys that start with it. A listing must pass a prefix inside it. Deleting many files in one request is refused with a prefix (delete them one at a time).

The response carries an access key id, a secret access key and a session token, shown once. Set all three in your tool (AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_SESSION_TOKEN for most SDKs and the AWS CLI). Requests then count against the bucket’s own rate limits and plan.

Temporary credentials stop working when they expire, when you regenerate the bucket’s key, or when the bucket is frozen, suspended or deleted. One set cannot be cancelled early on its own: to cut every set off at once, regenerate the bucket’s key.

What the S3 API supports

Works Notes
Upload, download (with byte ranges), HEAD, delete, delete many Single upload up to 100 MB; larger files use multipart (up to 10,000 parts)
List (ListObjects v1 and v2), multipart upload, presigned GET and PUT links Presigned links last up to 7 days
Copy, rename and move inside a bucket (CopyObject, UploadPartCopy) Copy from another bucket is refused
Encryption with your own key (SSE-C) Send the same key on every read; we never store it
Reading versioning, ACL, encryption and lifecycle settings Versioning is never enabled; the bucket is private to its keys

Files are always encrypted at rest. The plain x-amz-server-side-encryption: AES256 header some tools send is accepted. Standard is the storage class (the default). Infrequent Access is not available yet - see below. Other classes are refused.

Not supported: presigned POST (browser form upload - use a presigned PUT instead), bucket policies and ACL changes, versioning, replication, object tagging, website hosting settings, and changing CORS or lifecycle through the S3 API. Terraform’s AWS S3 bucket resource is not supported, because it reads several of these.

Limits: object metadata up to 8 KB, key names up to 1,024 bytes, about one write per second to the same key. With rclone set upload_cutoff = 90M (its default of 200M sends 100-200 MB files as one upload, which is refused).

Files

The Objects page lists what is in the bucket, folder by folder. You can upload files straight from the dashboard (up to 100 MB each - larger files with any S3 tool), open a new folder to upload into, delete files, and make a download link for any file with an expiry of 1 hour, 1 day or 7 days - anyone with the link can download that file until it expires.

Import from another provider

Move files you already have into a Voroa bucket from Amazon S3, Google Cloud Storage or any other S3-compatible store. Open the bucket’s Import page and give:

  • where the files are now, the source bucket name and, optionally, a folder prefix to copy only that folder;
  • for Amazon S3, the region the bucket lives in (as shown in its console); for another S3-compatible store, its https:// endpoint (a public address - private networks and IP addresses are refused);
  • read-only credentials: an access key ID and secret for S3 and S3-compatible stores, or a service account email and private key (from its JSON key file) for Google Cloud Storage.

What happens next:

  1. We check that the credentials can read the source, then count its files and their size.
  2. If the source is larger than the space left on your plan, or holds more files than the import allowance (1,000 files per GB your plan includes, each month - 20,000 on a 20 GB plan), the import does not start. Pick a larger plan and import again.
  3. The copy runs in the background. The page shows how many files were copied, skipped and failed. You can stop it at any time; files already copied stay.

Good to know:

  • Existing files are never overwritten. A file whose name is already in the bucket is skipped, so you can run an import again to pick up what was missed.
  • Skipped files: files larger than 1 TB, and files in archive storage classes (S3 Glacier Flexible Retrieval, Glacier Deep Archive, and Intelligent-Tiering’s Deep Archive tier). Restore or copy those separately. The page lists the names of skipped and failed files.
  • Cost: importing is free - the copy does not count toward your upload requests (up to the import allowance above). The imported files do count toward your storage, like any upload.
  • Once a year per bucket: each bucket gets one free import every 12 months. An import that copied any files, or finished, uses it; one that was refused or failed before copying anything does not. If the bucket has used it, the import is refused with the date the next one can start.
  • Credentials: stored encrypted only while the import runs and deleted when it ends. They are never shown again. Create a read-only key for the import and delete it at your provider afterwards.
  • Your source provider may charge you for reading the data out (egress). Check its pricing first.
  • An import cannot be started from an AI assistant, because it needs your other provider’s credentials; get_bucket_import_status and abort_bucket_import show and stop one.

Public buckets

A bucket can be made public in its Settings: every file in it can then be downloaded or viewed by anyone who has its URL, https://<bucket id>.<public domain>/<file name> - for example images on your website. Nobody can upload, change or delete files without a key; a public bucket is read-only to the world.

  • Images, audio, video, PDF, plain text, CSV and JSON open in the browser. HTML, SVG, JavaScript and other types always download, so a public bucket cannot host a web page.
  • Files are cached for about a minute. A change you make - a new upload over an old name, making the bucket private - reaches every visitor within about a minute.
  • Every public download counts toward your plan’s download requests and storage transfer, as with private access - including a download served from the one-minute cache. A refused request (file not found, bucket private, or too many requests) does not count.
  • Make the bucket private again at any time; every public URL stops working within about a minute.
  • Abuse of a public file can be reported to legal@getvoroa.com. A bucket that serves illegal content is frozen.

The Objects page shows a Copy public URL button for each file while the bucket is public. The set_bucket_public tool does the same as the switch.

Browser access (CORS)

To let a web page call the bucket directly - for example to upload from the browser with a presigned PUT link your server creates - add a CORS rule in the bucket’s Settings: the sites (origins) allowed, such as https://example.com, https://*.example.com or *, the methods (GET, PUT, POST, DELETE, HEAD), and optionally the request headers allowed and the response headers the page may read (add ETag for multipart uploads). Up to 10 rules; a change reaches browsers within about a minute.

CORS does not make files public. Every request still needs a valid key or presigned link; CORS only tells the browser that the page may read the answer. Rules can also be set with the set_bucket_cors tool.

Deleting files automatically (lifecycle rules)

Lifecycle rules delete files for you: after a number of days since upload (1 to 3,650), or on a date you choose, either for the whole bucket or only for keys that start with a prefix such as logs/ or tmp/. A bucket can have up to 50 rules. Set them on the bucket’s Lifecycle rules page or with the set_bucket_lifecycle tool; a new set replaces the old one, and an empty list removes them all.

Unfinished multipart uploads are always cleaned up - after 7 days by default, or any number of days from 1 to 30 that you choose. This cannot be turned off, so abandoned uploads never fill the bucket.

Deletion runs in the background, usually within a day of a file becoming due, and a deleted file cannot be recovered. Rules can only delete files; moving files to a cheaper storage class is not offered. Tools that read the bucket’s lifecycle settings over the S3 API (aws s3api get-bucket-lifecycle-configuration) see your rules; changing them through the S3 API is not supported.

Custom domains for public buckets

A public bucket can also serve its files on your own domain, such as cdn.example.com, instead of its Voroa public URL. Public buckets and custom domains are not available in every workspace yet; when they are not, the bucket has no Custom domains page.

  1. Make the bucket public in its Settings. A private bucket cannot have a custom domain.
  2. Open the bucket’s Custom domains page, enter the domain and add it. Up to 5 domains per bucket, one name each - a wildcard such as *.example.com is not accepted.
  3. Prove you own the domain: add the TXT record Voroa shows, named _voroa-verify.<your domain> with the value shown. Until it is found the domain shows Unverified, nothing is served on it, and it does not stop anyone else who can prove ownership from adding it. A domain not proven within 7 days is removed; you can add it again.
  4. Add the DNS record Voroa shows at your DNS provider: a CNAME from your domain to the bucket’s public host (<bucket id>.<public domain>). For a root domain such as example.com, use an ALIAS, ANAME or CNAME flattening record, because a root domain cannot have a CNAME.
  5. Wait for the domain to show Active. Voroa checks both records and issues the HTTPS certificate on its own, usually within a few minutes of them being visible; Check now re-checks immediately. The TXT record can be deleted once the domain is active.

A file at https://cdn.example.com/images/logo.png is the same file as on the bucket’s public URL, with the same rules: anyone can download or view it, nobody can upload or delete without a key, and a bucket made private again, frozen or deleted stops serving on every domain at once. Removing a domain stops it serving right away; your DNS record can then be deleted. If your domain already sits behind another proxy or CDN that answers for it, the record cannot be verified - point the name straight at the bucket’s public host.

Price: a custom domain is billed per domain for each month it is connected to a bucket, from the first domain - no domains are included in a plan. The price is shown next to the add form, and the domain is added only after you tick that you agree to it. A later price change never raises what you pay for a domain you already confirmed. It is billed after the month ends, on your next invoice, with your other usage above plan. A domain added while no price was shown is never billed.

A domain can be added with the add_bucket_domain tool when no price applies; an AI assistant cannot agree to a price for you, so a priced domain is added from the dashboard. Removing one through an AI assistant is a request you approve in the dashboard.

Access keys

Every bucket has a main key, created with the bucket. It can read, write and delete everything in the bucket. You can add up to four more keys per bucket (five in total), each limited to what one app or person needs:

  • Read-only: downloads and listing only. Uploads, deletes, copies and multipart uploads are refused.
  • One folder: every request must stay inside a folder (prefix) such as uploads/ or users/42/. Listing the bucket shows only that folder. A delete of several files at once is refused as a whole if even one file is outside the folder, and nothing is deleted. A copy is refused if its source is outside the folder. The folder always ends in /, so uploads/ never matches uploads-private/.
  • Exact files: up to 100 named files. Only those files can be read or written. Listing and deleting several files at once are refused for such a key.

A key can be read-only and limited to a folder or files at the same time. A key cannot be limited to a folder and a list of files together.

The secret of a new key is shown once, when you create it. Voroa cannot show it again; if it is lost, revoke the key and create a new one. Revoking a key stops it within about a minute and cannot be undone. The main key cannot be revoked; use Regenerate key to replace it. Freezing a bucket, a paused upload limit or an overdue payment applies to every key of the bucket, and request rate limits are shared by all of a bucket’s keys.

The list_bucket_keys tool lists a bucket’s keys (never their secrets), and revoke_bucket_key asks you to approve a revoke in the dashboard. Keys can only be created in the dashboard.

Event notifications (webhooks)

A bucket can send an HTTPS POST to your server whenever a file is uploaded or deleted. Set it on the bucket’s Event notifications page: the URL, which events (upload, delete or both), and optionally a name prefix such as images/ so only files under it are reported.

  • The URL must start with https:// and use a public host name on the standard port. IP addresses, local names and Voroa’s own addresses are refused.
  • When you first save a URL - or change it, or ask for a new secret - Voroa shows a signing secret once. Copy it then: it cannot be shown again, only replaced. Changing only the events or the prefix keeps it.
  • Your endpoint is verified first. When you save a new URL (or a new secret), Voroa sends it one signed request with "type": "ping". No upload or delete events are sent until your endpoint answers that ping with a 2xx status; until then the setting shows Not verified. If your endpoint was not ready, fix it and press Verify to send the ping again. Changing only the events or the prefix keeps the verification.
  • Each request carries a JSON body and an X-Voroa-Signature header of the form t=<unix time>,v1=<hex>, where v1 is HMAC-SHA256 of <t>.<body> with your secret. Recompute it, compare in constant time, and reject a t older than a few minutes.
json
{ "id": "evt...", "type": "object.created", "bucket": { "id": "...", "name": "photos" },
  "object": { "key": "images/a.png", "size": 1024, "etag": "..." }, "time": "2026-11-12T10:00:00Z" }

type is object.created (upload, multipart complete, copy), object.deleted, or ping (the verification request, which has no object). Answer with any 2xx status within 10 seconds. Redirects are not followed. A failed delivery is retried three times with growing gaps (about 10 seconds, 1 minute, 5 minutes) and then marked failed. Deliveries can arrive more than once and out of order, so use id to skip duplicates. The Event notifications page lists the last 100 deliveries with their status and HTTP code. The set_bucket_notifications and get_bucket_notification_deliveries tools do the same from an AI assistant; the tool never shows the signing secret.

Access logs

When available on your account, a bucket’s Access logs list each request made to it over the last 7 days: time, method, file name, status, bytes, the client’s IP address and user agent. Up to 500 lines are shown, newest first. The client IP addresses are personal data of your own users - handle them under your privacy policy. The get_bucket_access_logs tool reads the same list.

<!-- “billed after the month” wali line tab tak sach hai jab object_storage_extras_billing ON hai; OFF karo to wo bhi badlo (pricing page apne aap “see the docs” dikhata hai). -->

Infrequent Access storage

<!-- review MED 3: “not available yet” tab tak jab tak dono IA rate admin par set + object_storage_extras_billing ON (gateway ka cfg:ia). Tab yeh note hatao. --> Infrequent Access is not available yet. Until it is, an upload that asks for STANDARD_IA is refused with NotImplemented and nothing is stored or charged; upload without a storage class (Standard). The rest of this section describes how it will work once it is offered.

For files you keep but rarely read (backups, archives, old logs), upload them with the storage class STANDARD_IA, for example aws s3 cp backup.tar s3://my-bucket/ --storage-class STANDARD_IA. The Objects page shows each file’s class.

  • Infrequent Access files count against your plan’s storage exactly like Standard files.
  • Above your plan, Infrequent Access storage costs less per GB-month than Standard (see the pricing page). When a month has both, your plan’s included storage is used for the more expensive class first.
  • Reading an Infrequent Access file costs a retrieval fee per GB from the first byte; your plan includes no retrieval. Use Standard for anything read often.
  • Each request on an Infrequent Access file counts twice against your plan’s monthly upload and download request allowance.
  • Minimum 30 days. An Infrequent Access file deleted or replaced before it is 30 days old is billed as if it had been stored for 30 days. We measure this from daily storage totals and charge the lowest amount they allow.
  • The class is sent with the upload and must be part of the signed request; most S3 tools do this for you.

Retention lock

A retention lock stops files from being deleted or overwritten until they reach an age you choose, from 1 to 365 days, or indefinitely while the lock stays. Set it in the bucket’s Settings (or through the API with PUT /api/buckets/{id}/lock), per prefix (for example invoices/) or for the whole bucket. Up to 10 rules.

  • While any lock rule exists, the bucket cannot be deleted. Remove the lock rules first; removing a rule is always allowed.
  • A locked bucket’s data is kept even if a payment is not received. Uploads stop as for any unpaid bucket, but locked files are never deleted while the lock stands.
  • Retention lock may not be available on your account yet; the Settings page says so when it is not.

Usage and limits

The bucket’s Metrics page (and Usage and billing for all buckets) shows this month’s storage, storage transfer, and upload and download requests against your plan. It is updated hourly.

  • We email you at 80% and 100% of each.
  • Storage and storage transfer above your plan (and Infrequent Access storage above your plan and its retrieval) are billed after the month ends, on your next invoice, at the rates on the pricing page. The Metrics page shows an estimate during the month. If our measurement for any day of a month is missing, we do not charge extra usage for that month.
  • Requests above the monthly allowance are refused for the rest of the month (the S3 error code is FairUseExceeded), or until you choose a larger plan.
  • At five times your plan’s storage, uploads stop (the error code is UsageLimitExceeded). Listing, downloading and deleting keep working, so you can free space. Downloads are never stopped because of storage transfer.

If a payment is not received

New uploads are refused (the error code is PaymentRequired). You can still list, download and delete your files. Your data is kept, and we write to you before anything is deleted.

Delete a bucket

Open the bucket’s Settings and delete it (a bucket with a retention lock cannot be deleted until you remove its lock rules). Every file in it is deleted permanently - there is no backup and no undo. The plan ends with no refund for the rest of the month.

Last updated November 13, 2026