86d includes a storage abstraction layer that handles file uploads for product images, PDFs, and other media. You choose a storage backend by setting theDocumentation Index
Fetch the complete documentation index at: https://86d.app/docs/llms.txt
Use this file to discover all available pages before exploring further.
STORAGE_PROVIDER environment variable. The three supported providers are local (the default when running Docker), vercel (for Vercel deployments with Vercel Blob), and s3 (for any S3-compatible object storage). Uploaded files are stored under the path stores/{storeId}/{uuid} and are isolated per store automatically.
Choosing a provider
| Provider | Best for |
|---|---|
local | Docker Compose, self-hosted servers, local development |
vercel | Deployments on Vercel using Vercel Blob |
s3 | AWS S3, Cloudflare R2, MinIO, Railway object storage |
Local storage
Local storage writes files to a directory on the same filesystem as the running app. It is the default when you rundocker compose up and requires no external services.
.env
GET /uploads/[...path] with immutable cache headers. SVG files are served with a restrictive Content Security Policy (default-src 'none'; style-src 'unsafe-inline') to prevent XSS. PDFs are served as attachments.
Vercel Blob
When deploying to Vercel, use Vercel Blob for durable object storage. Add a Blob store to your Vercel project and Vercel injectsBLOB_READ_WRITE_TOKEN automatically.
.env
S3-compatible storage
Use thes3 provider for AWS S3, Cloudflare R2, MinIO, or Railway object storage. Every S3-compatible API is supported.
.env
MinIO (Docker Compose)
MinIO (Docker Compose)
When running the default Set
docker-compose.yml, MinIO is included and pre-configured. The default credentials are:.env
STORAGE_PUBLIC_URL_MODE=proxy so upload URLs are served through the store app at /uploads/... rather than pointing directly to the MinIO container, which is not reachable from the browser.Cloudflare R2
Cloudflare R2
R2 exposes an S3-compatible API. Find your endpoint in the R2 dashboard under your bucket settings.
.env
Railway object storage
Railway object storage
Railway uses virtual-hosted-style URLs. Set
S3_VIRTUAL_HOSTED_STYLE=true in addition to the standard S3 variables..env
Public URL mode
STORAGE_PUBLIC_URL_MODE controls the format of URLs returned by the upload API. It applies to the local and s3 providers.
| Value | Behavior |
|---|---|
direct | Upload responses return the raw bucket or filesystem URL (for example http://minio:9000/86d/stores/...). This is the default. |
proxy | Upload responses return a same-origin URL (for example /uploads/stores/...). The store app fetches the file from the backend and re-serves it to the browser. |
proxy mode when:
- You are running MinIO inside Docker Compose and the bucket URL is not reachable from the browser.
- You want all media to be served from your own domain without exposing the underlying storage provider.
- You are using a private S3 bucket and need to control access at the application layer.
.env
Upload limits and accepted types
All uploads go throughPOST /api/upload (admin only). The endpoint validates file contents using magic-byte detection. Setting a fake Content-Type header does not bypass validation.
| File type | Max size | Accepted formats |
|---|---|---|
| Images | 4.5 MB | JPEG, PNG, WebP, GIF, SVG |
| Documents | 10 MB |
javascript: URIs before being accepted. Malicious SVGs are rejected with a 400 error.
File paths
Uploaded files are stored atstores/{storeId}/{uuid} within the configured storage root. The storeId segment ensures that files from different stores cannot collide or be accessed cross-store.

