Create a bucket
Create a new storage bucket. Pick public (anonymous reads OK) or private (auth required).
Create a new bucket. The id you pass becomes the slug used in object URLs (e.g. /object/public/<id>/path/to/file.png), so prefer URL-safe lowercase identifiers.
The single most important decision on this call is the public: flag:
public: true— anonymous reads are allowed via the public URL. Writes still require auth. Use for avatars, marketing images, anything you'd serve from a CDN.public: false(the default if omitted, treated as private by storage-api) — every read goes through a signed URL or an authenticated session. Use for private uploads, paywalled assets, anything that needs RLS.
You can flip public: later with update_bucket, but the bucket cannot be re-created with a different id without first emptying and deleting the old one.
Service-role key required
Bucket admin endpoints reject the publishable / anon key with 401 Unauthorized. Construct the client with the service-role JWT.
Signature
supabase.storage.create_bucket(id,name: nil,public: nil,file_size_limit: nil,allowed_mime_types: nil)Parameters
| Name | Type | Required | Description |
|---|---|---|---|
id | String | Required | Bucket identifier. Becomes the slug used in public/signed URLs. Prefer URL-safe lowercase characters. |
name | String | Optional | Display name. Defaults to id when omitted (the wire payload always carries name; storage-api uses id when name is missing). |
public | Boolean | Optional | true allows anonymous reads via the public URL. false (or omitted) keeps reads behind auth/signed URLs. |
file_size_limit | Integer | Optional | Maximum object size in bytes. Uploads exceeding this size are rejected. Omit (or pass nil) for unbounded. |
allowed_mime_types | Array<String> | Optional | Allowlist of object content types (e.g. ["image/png", "image/jpeg"]). Uploads with other content types are rejected. Omit for any. |
Returns
The raw storage-api response body, typically { "name" => "<id>" } echoing the new bucket's id. The body is intentionally not wrapped in a Bucket struct — call get_bucket afterwards if you need the full record.
Example — public bucket (CDN-style)
supabase.storage.create_bucket("avatars", public: true)
# => { "name" => "avatars" }
# Anyone can now read /object/public/avatars/<file>.
supabase.storage.from("avatars").upload("ada.png", File.binread("ada.png"))
supabase.storage.from("avatars").get_public_url("ada.png")
# => "https://project.supabase.co/storage/v1/object/public/avatars/ada.png"Example — private bucket with size + MIME guards
supabase.storage.create_bucket(
"private-uploads",
public: false,
file_size_limit: 10 * 1024 * 1024, # 10 MB
allowed_mime_types: ["image/png", "image/jpeg", "application/pdf"]
)
# Reads now require an authenticated session or a signed URL.
supabase.storage.from("private-uploads").create_signed_url("contract.pdf", 60)Example — minimal call
# Defaults to private, no size or MIME guard.
supabase.storage.create_bucket("scratch")