supabase-rb-rb
Storage

Upload a file

Upload an object to a bucket. Accepts a File/IO, a Pathname, or a raw String body.

Upload bytes to path inside the bucket scoped by from(bucket_id). The bytes source can be a File/IO, a Pathname (read from disk), or a raw String payload — whichever fits the call site best.

Signature

supabase.storage.from(bucket_id).upload(path,file,content_type: nil,cache_control: nil,upsert: false,metadata: nil,headers: nil)

Parameters

NameTypeRequiredDescription
pathStringRequiredDestination path within the bucket (e.g. "folder/avatar.png"). A leading "/" is stripped.
fileString, IO, PathnameRequiredBytes source. A String is treated as raw bytes (NOT a filesystem path — see the callout below). An IO/File or Pathname is read.
content_typeStringOptionalMIME type sent as part of the multipart body. Defaults to text/plain;charset=UTF-8 when omitted — set it explicitly for binary uploads.
cache_controlString, IntegerOptionalCache lifetime in seconds. Sent as Cache-Control: max-age=<n>. Defaults to "3600" (raw, no max-age wrapper) when omitted.
upsertBooleanOptionaltrue to overwrite an existing object at path. Sent as the x-upsert header. Defaults to false.
metadataHashOptionalArbitrary metadata. JSON-encoded then base64-encoded into the x-metadata header AND repeated as a form field.
headersHashOptionalExtra HTTP headers to merge into the multipart request (e.g. custom auth bearer overrides).

Returns

Returns
Supabase::Storage::Types::UploadResponse

Struct with path (the relative path you uploaded to), full_path (alias fullPath) carrying the bucket-prefixed key returned by storage-api, and key (same value as full_path).

Example — File.open body

File.open("./ada.png", "rb") do |io|
  response = supabase.storage.from("avatars").upload(
    "people/ada.png",
    io,
    content_type: "image/png",
    cache_control: 31_536_000, # one year
    upsert: true
  )

  response.path      # => "people/ada.png"
  response.full_path # => "avatars/people/ada.png"
end

Example — raw String body

# A String is uploaded as raw bytes — it is NOT treated as a file path.
supabase.storage.from("notes").upload(
  "scratch/hello.txt",
  "hello world\n",
  content_type: "text/plain"
)

Example — Pathname body

require "pathname"

supabase.storage.from("avatars").upload(
  "people/grace.png",
  Pathname("./grace.png"),
  content_type: "image/png"
)

Example — with custom metadata

supabase.storage.from("invoices").upload(
  "2026/Q2/INV-0001.pdf",
  File.binread("INV-0001.pdf"),
  content_type: "application/pdf",
  metadata: { customer_id: 42, locked: true }
)

Strings are bytes, not paths

A String is always raw bytes/text. Disk reads are spelled File.open(path), File.binread(path), or Pathname(path). This matches supabase-js (Blob/Buffer/File, never a path string) and avoids the silent footgun where upload("a.png", "./a.png") would upload the literal path string.

On this page