Storage
Configure Cloudinary or S3-compatible storage for media and PDFs.
Media and generated documents are stored through @prolifto/infrastructure. The
provider is selected by STORAGE_PROVIDER.
Providers
| Provider | Use it for |
|---|---|
cloudinary | Default. Managed image and video storage |
s3 | AWS S3, Cloudflare R2, SeaweedFS, RustFS, or another S3-compatible store |
Cloudinary
STORAGE_PROVIDER="cloudinary"
NEXT_PUBLIC_CLOUDINARY_CLOUD_NAME="..."
NEXT_PUBLIC_CLOUDINARY_PRESET_NAME="..."
CLOUDINARY_API_KEY="..."
CLOUDINARY_API_SECRET="..."Cloudinary is the default when STORAGE_PROVIDER is unset. The adapter uploads
to a folder, returns the provider URL, and supports deleting and downloading
assets.
S3-compatible storage
STORAGE_PROVIDER="s3"
S3_ENDPOINT="http://localhost:8333"
S3_REGION="us-east-1"
S3_ACCESS_KEY_ID="admin"
S3_SECRET_ACCESS_KEY="admin"
S3_BUCKET="prolifto"
S3_FORCE_PATH_STYLE="true"
S3_PUBLIC_URL_BASE="http://localhost:8333/prolifto"
S3_AUTO_CREATE_BUCKET="false"
S3_AUTO_PUBLIC_READ="false"Notes:
S3_FORCE_PATH_STYLEdefaults totrue, which works for SeaweedFS and most local providers.S3_PUBLIC_URL_BASEcontrols the URL returned for uploaded objects. For virtual-hosted buckets, set it to the real public bucket or CDN base.S3_AUTO_CREATE_BUCKETandS3_AUTO_PUBLIC_READare development conveniences and default tofalse. Keep them off for managed providers.- The adapter returns public object URLs. Front a private bucket with a CDN or add presigned-URL support before using one in production.
- Mirror
S3_ENDPOINTandS3_PUBLIC_URL_BASEin the storefront sonext/imageand the content security policy allow uploaded media. - Uploads are capped at 100 MB, with content-type detection and host validation for URL imports.
Local development
Start SeaweedFS:
pnpm storage:up
pnpm storage:logs
pnpm storage:down
pnpm storage:resetRustFS is an alternative:
pnpm rustfs:upRustFS exposes a console at http://localhost:9001 (rustfsadmin /
rustfsadmin) and its S3 API at http://localhost:9000. Only run one storage
service at a time.
Switching providers
Switching STORAGE_PROVIDER does not migrate existing media rows. Keep the
previous provider reachable until old assets are deleted, or migrate the objects
and update media.provider / media.url.
Generated PDFs
Invoice and receipt PDFs are generated by Puppeteer and stored through the same storage abstraction. If PDF generation fails, check:
- The storage provider credentials.
- The Puppeteer executable (
PUPPETEER_EXECUTABLE_PATH,PUPPETEER_NO_SANDBOX). - The storage health endpoint.
Health check
The admin health endpoint checks the database and storage:
curl http://localhost:3000/api/my-store/healthA storage failure returns degraded; a database failure returns down.