cloider_bucket resource (Terraform)
The cloider_bucket resource creates and manages a Cloider object storage bucket: an S3 compatible container for files that any S3 client, SDK or tool can read and write. You choose its name, its region, whether anyone may read its files without a key, whether it keeps earlier versions of files, whether it is served as a static website, and which websites can use it from a browser with CORS rules. To read and write the bucket, create a cloider_bucket_key.
At a glance
| Required arguments | name |
| Changes in place | public, versioning, website, index_document, error_document, cors_rule |
| Changes that replace the bucket | name, region |
| Sensitive attributes | None |
| Timeouts | create, delete, 20 minutes each by default |
| Import ID | The bucket id, such as b7r2xw |
| API | /api/buckets |
| CLI equivalent | cloider bucket |
Example usage
Basic private bucket
resource "cloider_bucket" "assets" {
name = "acme-assets"
}
The bucket is created in the default region (the first one cloider_bucket_regions lists), is private, and has versioning off.
Public bucket for website assets
A public bucket lets anyone read its files over HTTPS without a key. Writing always needs a key.
resource "cloider_bucket" "assets" {
name = "acme-assets"
public = true
}
output "assets_url" {
description = "Files are readable at <url>/<key>"
value = cloider_bucket.assets.url
}
assets_url = "https://acme-assets.s3.eu-central-2.cloider.com"
See Public and private buckets.
Versioned bucket for backups
resource "cloider_bucket" "backups" {
name = "acme-backups"
versioning = true
lifecycle {
prevent_destroy = true
}
}
With versioning on, overwriting or deleting a file keeps the earlier version. See Versioning.
Static website on your domain
resource "cloider_bucket" "site" {
name = "acme-site"
public = true
website = true
error_document = "404.html"
}
resource "cloider_domain" "docs" {
name = "docs.example.com"
}
resource "cloider_target" "docs" {
domain = cloider_domain.docs.name
bucket_id = cloider_bucket.site.id
}
output "website_url" {
value = cloider_bucket.site.website_url
}
website_url = "https://acme-site.s3-website.eu-central-2.cloider.com"
The website needs public = true. The cloider_target also serves it on docs.example.com. For a single-page app, set error_document = "index.html". See Static websites.
CORS rules for a web app
Only these websites can use the bucket from a browser: the app uploads with presigned URLs and reads each part's ETag, and the other sites under example.com read files. Without cors_rule blocks any website can.
resource "cloider_bucket" "uploads" {
name = "acme-uploads"
cors_rule {
id = "web-app"
allowed_origins = ["https://app.example.com"]
allowed_methods = ["GET", "PUT", "POST"]
allowed_headers = ["*"]
expose_headers = ["ETag"]
max_age_seconds = 3000
}
cors_rule {
allowed_origins = ["https://*.example.com"]
allowed_methods = ["GET", "HEAD"]
}
}
The first rule whose origin matches answers a preflight, so the rule for the app comes first. The Cloider dashboard keeps working whatever the rules say. See CORS.
Choose the region
data "cloider_bucket_regions" "all" {}
resource "cloider_bucket" "assets" {
name = "acme-assets"
region = data.cloider_bucket_regions.all.regions[0].code
}
Or write the code directly, such as region = "eu-central-2".
A bucket with its own key
resource "cloider_bucket" "uploads" {
name = "acme-uploads"
}
resource "cloider_bucket_key" "uploads" {
label = "web uploads"
buckets = [cloider_bucket.uploads.id]
}
output "s3_endpoint" {
value = cloider_bucket.uploads.endpoint
}
output "s3_access_key" {
value = cloider_bucket_key.uploads.access_key
}
output "s3_secret_key" {
value = cloider_bucket_key.uploads.secret_key
sensitive = true
}
Several buckets with for_each
variable "buckets" {
type = map(object({
public = bool
versioning = bool
}))
default = {
"acme-assets" = { public = true, versioning = false }
"acme-backups" = { public = false, versioning = true }
}
}
resource "cloider_bucket" "this" {
for_each = var.buckets
name = each.key
public = each.value.public
versioning = each.value.versioning
}
Argument reference
| Argument | Type | Required | Default | Changing it | Description |
|---|---|---|---|---|---|
name | String | Yes | Replaces the bucket | 3 to 63 lower case letters, digits and -, starting and ending with a letter or digit, no dots. Bucket names are shared by every Cloider account, so it must be one nobody else has. See Names | |
region | String | No | The first region cloider_bucket_regions lists | Replaces the bucket | The region code, such as eu-central-2 |
public | Bool | No | false | In place | Whether anyone may read the bucket's files without a key |
versioning | Bool | No | false | In place | Whether the bucket keeps earlier versions of its files. Once on, versioning can only be suspended: false after true stops keeping new versions but keeps those already kept |
website | Bool | No | false | In place | Whether the bucket is served as a static website at website_url, and on the domains a cloider_target with bucket_id routes to it. Needs public = true |
index_document | String | No | "index.html" | In place | The file the website serves for a path ending in a slash, a name without a slash |
error_document | String | No | "" | In place | The key the website serves, with a 404, for anything missing. "" is a built-in page |
cors_rule | Block list | No | None, any website | In place | Up to 99 rules for the websites that can use the bucket from a browser, in order. See cors_rule |
timeouts | Object | No | 20 minutes each | create and delete durations |
cors_rule
| Argument | Type | Required | Description |
|---|---|---|---|
allowed_origins | List of String | Yes | Origins in lower case without a path, like https://app.example.com. https://*.example.com matches its subdomains, * any website |
allowed_methods | List of String | Yes | Any of GET, HEAD, PUT, POST and DELETE |
allowed_headers | List of String | No | Request headers a page may send, like Content-Type. * for any, or a prefix like x-amz-* |
expose_headers | List of String | No | Response headers a page's script can read, like ETag |
max_age_seconds | Number | No | How long browsers may reuse a preflight answer, 1 to 86400 |
id | String | No | A name for the rule |
Rules set through the S3 API or the dashboard show as a diff on the next plan, and apply puts the configuration's rules back.
Attribute reference
| Attribute | Type | Description |
|---|---|---|
id | String | The bucket's id |
status | String | Ready, Failed or Terminating. See Status values |
endpoint | String | The S3 endpoint of the bucket's region, such as https://s3.eu-central-2.cloider.com. Give this to S3 clients as the endpoint URL |
url | String | The bucket's own URL, such as https://acme-assets.s3.eu-central-2.cloider.com, where a public bucket's files are read |
website_url | String | The website's own address, such as https://acme-site.s3-website.eu-central-2.cloider.com, while website is on |
created_at | String | When the bucket was created, in RFC 3339 |
region | String | The region in use, also when you didn't set it |
What replaces a bucket?
Changing name or region deletes the bucket, with every file and every version in it, and creates a new, empty one. Terraform shows # forces replacement next to the attribute in the plan. A bucket's name can't be changed, and a bucket can't move between regions. To rename or move one, create the new bucket, copy the files with an S3 tool such as rclone, then remove the old one from the configuration.
public, versioning, the website settings and cors_rule change in place, without touching the files. When public and website change together, the provider turns the website off before it makes the bucket private, and on after it makes the bucket public, so either direction applies in one run. Turning the website off is refused while a cloider_target still serves it: Domains still point at this website, remove their targets first.
Create and delete behavior
- Create is fast: a bucket is
Readyas soon as the API answers. If the create request times out (a502or504), the provider doesn't assume failure: it looks for a bucket with the same name in your account for up to 4 minutes and adopts it once it isReady. Bucket names are unique across Cloider, so the bucket it finds is the one it created. - Name taken. If another account already has the name, the create fails with the API's message. Choose another name.
- Delete removes the bucket with all its contents, then waits until it is gone (
Terminated). You don't need to empty it first. See Delete a bucket. - Deleted outside Terraform. The next refresh removes the bucket from state and the plan offers to create it again.
What the provider doesn't manage on a bucket
Files, bucket policies, lifecycle rules and data protection aren't part of cloider_bucket (CORS rules are, with cors_rule). Manage them in the dashboard, with cloider bucket, or with an S3 tool. To upload files from Terraform, use the aws provider's aws_s3_object against the bucket's endpoint, see Object storage with the AWS provider.
Timeouts
| Operation | Default | What it waits for |
|---|---|---|
create | 20 minutes | Covers the create call and, after a timed out create, the search for the new bucket |
delete | 20 minutes | The bucket and its contents to be gone |
There is no update timeout: changing public, versioning, the website settings or cors_rule takes effect immediately, and website changes show on the website within about 30 seconds.
Import
Import an existing bucket by its id. cloider bucket list -o wide shows the ids:
cloider bucket list -o wide
NAME ID REGION STATUS ACCESS SIZE CREATED
acme-assets b7r2xw eu-central-2 Ready public 1.2 GiB 2026-08-11 10:02
acme-backups m4t9cs eu-central-2 Ready private 38.0 GiB 2026-08-11 10:05
With an import block (Terraform 1.5 or later):
import {
to = cloider_bucket.assets
id = "b7r2xw"
}
resource "cloider_bucket" "assets" {
name = "acme-assets"
public = true
}
With the terraform import command:
terraform import cloider_bucket.assets b7r2xw
After the import, set public, versioning and the website settings in the configuration to what the bucket has, or the next apply changes them back to the defaults (false, index.html and "").
Related
cloider_bucket_key: S3 credentials for the bucketcloider_bucket_regions: region codes and endpointscloider_target: serve the bucket's website on a domain- Object storage with the AWS provider: upload files and use S3 tools
- Store Terraform state in a bucket
- Create a bucket with the dashboard, CLI or API
Frequently asked questions
How do I create an S3 bucket on Cloider with Terraform?
Add a cloider_bucket resource with a name nobody else on Cloider uses, and run terraform apply. Then create a cloider_bucket_key with buckets = [cloider_bucket.<name>.id] to get an access key and secret key for S3 clients.
Can I rename a bucket or change its region with Terraform?
No. Changing name or region replaces the bucket, which deletes it with all its files and creates a new empty one. Copy the files to a new bucket with an S3 tool instead.
Can I turn versioning off with Terraform?
You can set versioning = false after true, which suspends versioning: new versions stop being kept, but versions already kept stay until you delete them.
How do I host a static website on a Cloider bucket with Terraform?
Set public = true and website = true on the cloider_bucket, optionally with index_document and error_document, and read the address from website_url. To serve it on your domain, add a cloider_target with bucket_id = cloider_bucket.<name>.id.
How do I set CORS rules on a Cloider bucket with Terraform?
Add a cors_rule block to the cloider_bucket for each rule, with allowed_origins and allowed_methods, and optionally allowed_headers, expose_headers and max_age_seconds. Without cors_rule blocks any website can use the bucket from a browser.
Does destroying a cloider_bucket delete its files?
Yes. Deleting a bucket deletes every file and every version in it, and its name becomes free for anyone to take. Use lifecycle { prevent_destroy = true } on buckets that hold data you need.
Can Terraform upload files to a Cloider bucket?
Not with the Cloider provider. Use the aws provider's aws_s3_object resource pointed at the bucket's endpoint, with a cloider_bucket_key's access key and secret key.