> ## Documentation Index
> Fetch the complete documentation index at: https://voyant.travel/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Storage

> Object storage and KV for apps you deploy on Voyant, provisioned for you and accessed through standard Worker bindings.

This page is about **your own apps deployed on Voyant**, not the Voyant deployment itself. Those apps run as Workers, so storage is exposed the way Workers expose it: as **bindings**. You declare the storage you need, Voyant provisions it and wires the binding into your deployment, and your code calls the standard API through `env`.

<Note>
  The Voyant deployment resolves storage differently. Its modules resolve logical stores such as `media` and `documents` through a provider, never a vendor bucket or a binding. See [Adapters and providers](/docs/platform/extending/adapters-and-providers).
</Note>

## Object storage (R2)

[R2](https://developers.cloudflare.com/r2/) is object storage for files: uploads, generated documents, exports, and media.

Provision a bucket from the CLI, or declare it and let the deploy provision it for you:

```bash theme={null}
voyant storage buckets list
voyant storage buckets create my-bucket
voyant storage buckets delete <id> --yes
```

Declare the binding in your `wrangler.jsonc`. On deploy, Cloud resolves it to a provisioned bucket scoped to your organization and environment:

```jsonc theme={null}
{
  "r2_buckets": [{ "binding": "R2", "bucket_name": "my-bucket" }]
}
```

Then use the bucket through the binding. The methods (`put`, `get`, `list`, `delete`, multipart, conditional reads) are Cloudflare's R2 Workers API:

```ts theme={null}
await env.R2.put("invoices/inv_123.pdf", pdfBytes);
const object = await env.R2.get("invoices/inv_123.pdf");
```

See Cloudflare's [R2 Workers API reference](https://developers.cloudflare.com/r2/api/workers/workers-api-reference/) for the full surface.

## Key-value (KV)

KV namespaces are a separate provisioned resource with their own dashboard page, and they share this binding model: you bind a namespace through `kv_namespaces` in `wrangler.jsonc` and use it with the Cloudflare KV API. See [KV](/docs/services/kv) for provisioning, binding, and usage.

## Binding names

Cloud derives the binding name by upper-casing the resource name and turning dashes into underscores, so `my-cache` becomes `MY_CACHE`. Reference your storage in code as `env.<BINDING>` using that name. The platform owns the `r2_buckets`, `kv_namespaces`, and `d1_databases` keys in the merged wrangler config, so the resolved ids always point at your provisioned resources.

<Tip>
  Access is a binding, not an API token, so your app never holds storage credentials. Keep buckets private and serve files by streaming through your own authorized route or a signed URL, rather than making a whole bucket public.
</Tip>

## Next steps

<CardGroup cols={2}>
  <Card title="Database" icon="database" href="/docs/services/database">
    Managed Postgres and D1 for relational data.
  </Card>

  <Card title="CLI commands" icon="terminal" href="/docs/cli/commands">
    Every `storage` subcommand and flag.
  </Card>
</CardGroup>
