# Storage

> S3-compatible object storage on Tarout

# Storage

Tarout provides object storage for files, images, backups, and static assets. Apps reach it through an S3-compatible gateway that supports the core object operations (see [What the S3 gateway supports](#what-the-s3-gateway-supports)).

## Creating a Storage Bucket

1. Navigate to **Storage** in the dashboard.
2. Click **New Bucket**.
3. Enter a bucket name (optional; Tarout generates one if you leave it blank) and an optional description.
4. Leave **Public Access** off unless anyone on the internet should be able to read the files.
5. Click **Create**.

You do not pick a storage plan per bucket. The bucket follows your subscription:

- **Free plan** - one bucket with 50 MB of space.
- **Paid plans** - up to 3 buckets per project. They share the storage capacity you add to your plan, billed at 0.12 SAR per GB per month. Add capacity before creating a paid bucket.

From the CLI: `tarout storage create [name]` (add `--public` for a public bucket).

## Uploading Files

### Dashboard and CLI

Drag and drop files in the bucket detail page, or click **Upload** to select files. From a terminal, use `tarout storage put <bucket> <key> <file>`. Both send the file straight to storage through a signed upload URL, so use them for large files.

### From your app on Tarout

Attach the bucket to the app, either from the bucket's page in the dashboard or with the CLI. Tarout creates an access key scoped to that bucket and app and adds the connection details to the app's environment variables:

```bash
tarout storage attach <bucket> <app-id>
```

The bucket and the app must be in the same project. `tarout deploy` does this for you when it creates a bucket for your project. If the app is already deployed, attaching restarts it so the variables take effect.

Attaching sets these variables (among others):

| Variable | Value |
|---|---|
| `STORAGE_ENDPOINT` | The S3 gateway endpoint |
| `STORAGE_BUCKET` | The bucket name to use in S3 requests |
| `STORAGE_REGION` | `auto` |
| `STORAGE_ACCESS_KEY_ID` | Access key for this app |
| `STORAGE_SECRET_ACCESS_KEY` | Secret for that key |

The same values are also set under the names many libraries read by default: `S3_ENDPOINT`, `S3_BUCKET`, `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, `AWS_ENDPOINT_URL`, `AWS_REGION`, and `AWS_S3_FORCE_PATH_STYLE=true`.

### AWS SDK (S3-Compatible)

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

const s3 = new S3Client({
  endpoint: process.env.STORAGE_ENDPOINT,
  region: process.env.STORAGE_REGION ?? "auto",
  forcePathStyle: true,
  credentials: {
    accessKeyId: process.env.STORAGE_ACCESS_KEY_ID,
    secretAccessKey: process.env.STORAGE_SECRET_ACCESS_KEY,
  },
});

await s3.send(
  new PutObjectCommand({
    Bucket: process.env.STORAGE_BUCKET,
    Key: "uploads/photo.jpg",
    Body: fileBuffer,
  })
);
```

`forcePathStyle: true` is required: the gateway expects the bucket in the URL path, not in the hostname. Always use `STORAGE_BUCKET` as the bucket name rather than the display name you typed in the dashboard.

### From outside Tarout

For a server or tool that does not run on Tarout, create a key under the bucket's **Access keys** section. These keys only work after you turn on the bucket's **External access** setting and add the IP addresses (or CIDR ranges) allowed to connect. From the CLI:

```bash
tarout storage external-access <bucket> --enable --cidrs 203.0.113.10/32
```

Requests from any other address are refused, even with a valid key. Keys created by attaching a bucket to an app on Tarout do not need this setting. Use the bucket's ID as the bucket name in S3 requests from outside Tarout.

## What the S3 gateway supports

- Upload (`PUT`), download (`GET`), metadata (`HEAD`), and delete (`DELETE`) of single objects.
- Listing a bucket (`GET` on the bucket), optionally filtered by prefix. A listing returns at most the first 1,000 keys.
- **No multipart upload.** Each object is sent in one `PUT` request. For large files, upload through the dashboard or `tarout storage put` instead of one very large gateway request.
- **No Range requests.** A `GET` always returns the whole object.

Tools that switch to multipart for big files (such as `aws s3 cp` or `rclone` above their size threshold) need that behavior turned off or the threshold raised.

## Access Control

- Files are private by default and are accessible through signed URLs or valid bucket-scoped credentials.
- A bucket with **Public Access** turned on lets anyone read its files through the storage gateway without credentials. Uploads and deletes still need a key.

## Use Cases

- **User uploads** - Profile pictures, documents, attachments.
- **Static assets** - Images, CSS, JavaScript.
- **Backups** - Database dumps, application snapshots.
- **Media** - Video, audio, and large file storage.

## Pricing

Paid storage is billed for the capacity you reserve, not for what you use: 0.12 SAR per GB per month. The capacity is shared by all buckets in the project. The free plan includes one 50 MB bucket. See the [Billing](https://tarout.sa/docs/billing) section for current rates.

---

## Every Tarout agent guide

- [Start Here (Agents)](https://tarout.sa/docs/for-ai/start.md)
- [Overview](https://tarout.sa/docs/for-ai.md)
- [Deploying an app](https://tarout.sa/docs/for-ai/deploy.md)
- [Databases](https://tarout.sa/docs/for-ai/database.md)
- [Object storage](https://tarout.sa/docs/for-ai/storage.md)
- [Custom domains](https://tarout.sa/docs/for-ai/domains.md)
- [Plans and upgrades](https://tarout.sa/docs/for-ai/billing.md)
- [Troubleshooting](https://tarout.sa/docs/for-ai/troubleshoot.md)
- [Agent Onboarding](https://tarout.sa/docs/for-ai/onboarding.md)
- [CLI Reference](https://tarout.sa/docs/for-ai/cli-reference.md)
- [CLI JSON Schema](https://tarout.sa/docs/for-ai/cli-json-schema.md)

Whole corpus in one file: https://tarout.sa/llms-full.txt · index: https://tarout.sa/llms.txt
Any docs page is raw markdown at the same URL + `.md`. Short link to the entry point: https://tarout.sa/deploy.md
