File Storage
Telbase provisions S3-compatible file storage automatically when you deploy with --storage. No buckets to create, no credentials to copy — just deploy and start uploading.
How It Works
When you deploy with --storage, Telbase creates an R2 storage bucket and injects four environment variables into your app:
| Variable | Purpose |
|---|---|
R2_ENDPOINT | S3-compatible endpoint URL |
R2_ACCESS_KEY_ID | Access key for authentication |
R2_SECRET_ACCESS_KEY | Secret key for authentication |
R2_BUCKET_NAME | Name of the storage bucket |
These are available at process.env.* in your deployed app. The bucket is S3-compatible, so standard AWS SDK patterns work out of the box.
multer, formidable, busboy, express-fileupload, @aws-sdk/client-s3, aws-sdk, boto3, or python-multipart. You can also provision explicitly with --storage.Provisioning Storage
# Deploy with file storage
telbase deploy --storage
# Or with Claude Code / MCP
telbase deploy --local --json --auto --storageStorage is provisioned once on first deploy. Subsequent deploys preserve the same bucket and credentials. Use --no-storage to skip storage provisioning.
Using Storage in Your App
Use the standard AWS S3 SDK. The storage is S3-compatible, so any library that works with S3 works with Telbase storage.
Install the AWS SDK:
npm install @aws-sdk/client-s3Create a storage client:
import { S3Client, PutObjectCommand, GetObjectCommand } from '@aws-sdk/client-s3';
const s3 = new S3Client({
region: 'auto',
endpoint: process.env.R2_ENDPOINT,
credentials: {
accessKeyId: process.env.R2_ACCESS_KEY_ID!,
secretAccessKey: process.env.R2_SECRET_ACCESS_KEY!,
},
});
// Upload a file
export async function uploadFile(key: string, body: Buffer, contentType: string) {
await s3.send(new PutObjectCommand({
Bucket: process.env.R2_BUCKET_NAME,
Key: key,
Body: body,
ContentType: contentType,
}));
return key;
}
// Download a file
export async function getFile(key: string) {
const response = await s3.send(new GetObjectCommand({
Bucket: process.env.R2_BUCKET_NAME,
Key: key,
}));
return response.Body;
}Example: File Upload Endpoint
A complete Express endpoint for handling file uploads with multer:
import express from 'express';
import multer from 'multer';
import { S3Client, PutObjectCommand } from '@aws-sdk/client-s3';
const app = express();
const upload = multer({ storage: multer.memoryStorage() });
const s3 = new S3Client({
region: 'auto',
endpoint: process.env.R2_ENDPOINT,
credentials: {
accessKeyId: process.env.R2_ACCESS_KEY_ID!,
secretAccessKey: process.env.R2_SECRET_ACCESS_KEY!,
},
});
app.post('/upload', upload.single('file'), async (req, res) => {
if (!req.file) return res.status(400).json({ error: 'No file' });
const key = `uploads/${Date.now()}-${req.file.originalname}`;
await s3.send(new PutObjectCommand({
Bucket: process.env.R2_BUCKET_NAME,
Key: key,
Body: req.file.buffer,
ContentType: req.file.mimetype,
}));
res.json({ key, size: req.file.size });
});Local Development
Pull storage credentials to your local environment for development:
# Download all env vars (including R2_*) to .env.local
telbase env pull
# Or view them individually
telbase env get R2_ENDPOINT
telbase env get R2_BUCKET_NAMEYour local code connects to the same R2 bucket as production. This means files uploaded locally are visible in production and vice versa.
my-app-dev) with its own storage bucket. Use telbase env pull in that project to get separate credentials.Managing Credentials
Storage credentials can be viewed and rotated from the dashboard or CLI.
# View credentials (redacted by default)
telbase env list
# Rotate credentials (generates new keys, invalidates old ones)
# Use the dashboard Storage tab > Rotate CredentialsCORS Configuration
If your frontend uploads directly to storage (browser-side uploads), configure CORS origins from the dashboard Storage tab. Add your app's domain to allow cross-origin requests.
Storage Tiers
| Tier | Buckets | Storage | Cost |
|---|---|---|---|
| Free | — | — | $0 |
| Starter | 1 | 1 GB | $5/mo |
| Builder | 3 | 5 GB | $19/mo |
| Pro | 10 | 25 GB | $79/mo |
Storage uses Cloudflare R2 with $0 egress fees. Upgrade at Settings → Billing.
Next Steps
- Quick Start — deploy your first app
- Databases — auto-provisioned SQLite and PostgreSQL
- Using with Claude Code — AI-powered development and deployment
- CLI Reference — full command reference