Skip to content
Specific Docs
Esc
navigateopen⌘Jpreview
On this page

Object storage

S3-compatible object storage that works with any S3 client.

Managed S3-compatible object storage, powered by Tigris in production. Works with any S3 SDK or tool.

service "api" {
  build = build.api
  command = "./api"

  endpoint {
    public = true
  }

  env = {
    S3_ENDPOINT   = storage.uploads.endpoint
    S3_ACCESS_KEY = storage.uploads.access_key
    S3_SECRET_KEY = storage.uploads.secret_key
    S3_BUCKET     = storage.uploads.bucket
  }
}

storage "uploads" {}

You can declare multiple buckets; each storage block is its own bucket with its own credentials.

Connection attributes

Attribute Description
endpoint S3-compatible endpoint URL (for example, http://127.0.0.1:5000 in dev).
access_key Access key for authentication.
secret_key Secret key for authentication.
bucket Bucket name.
public_url HTTPS origin for anonymous reads. Available only when public = true.

Public buckets

Public buckets are opt-in:

storage "assets" {
  public = true
}

service "web" {
  build = build.web

  env = {
    ASSETS_PUBLIC_URL = storage.assets.public_url
  }
}

public_url has no trailing slash. Append the object key to it, for example ${ASSETS_PUBLIC_URL}/images/logo.png.

Public access allows anonymous object reads. Uploads and deletes still require credentials, and directory listing is disabled. CORS headers, cache-control metadata, and custom domains are separate concerns.

Removing public = true makes an existing bucket private again on the next deployment. Preview buckets are forked from their parent, so making a preview bucket public can expose copied parent data.

Example: AWS SDK (JavaScript)

import { S3Client, PutObjectCommand } from "@aws-sdk/client-s3";

const s3 = new S3Client({
  endpoint: process.env.S3_ENDPOINT,
  region: "us-east-1", // Required but ignored locally
  credentials: {
    accessKeyId: process.env.S3_ACCESS_KEY,
    secretAccessKey: process.env.S3_SECRET_KEY,
  },
  forcePathStyle: true, // Required for local S3-compatible servers
});

await s3.send(
  new PutObjectCommand({
    Bucket: process.env.S3_BUCKET,
    Key: "myfile.txt",
    Body: "Hello, world!",
  })
);

Local development

During specific dev, a local S3-compatible server runs automatically, and the same storage.* references resolve to it. Public bucket URLs use the local server and support anonymous reads. Browse bucket contents from the local dashboard.

Production

Browse bucket contents and manage stored files from the dashboard.

If a service outside of Specific needs to read from or write to a bucket, generate direct S3-compatible credentials (read/write or read-only) from the storage resource’s Access tab in the dashboard.

In preview environments, object storage is forked from the parent environment.

Was this page helpful?