Media storage
Keep uploaded pictures and videos on the server's disk or in any S3-compatible bucket (AWS S3, Cloudflare R2, MaxIOFS), and move existing files between them.
Everything people upload (pictures, videos, files) and the thumbnails made from them live in one
place, picked with STORAGE_DRIVER:
STORAGE_DRIVER | Where files live | For |
|---|---|---|
disk (default) | UPLOAD_DIR, by default uploads/ in the data directory | Most installs: the binary or the Docker container |
s3 | An S3-compatible bucket | Storage that grows without a bigger disk, or media kept off the machine |
Nothing else changes: the dashboard, the API, MCP and the SDKs work the same with either.
Use a bucket
Set these in the server's environment (-e or --env-file with Docker, exported before
staqly serve with the binary; see Configuration)
and restart:
STORAGE_DRIVER=s3
S3_ENDPOINT=https://s3.eu-central-1.amazonaws.com
S3_REGION=eu-central-1
S3_BUCKET=my-staqly-media
S3_ACCESS_KEY_ID=AKIA...
S3_SECRET_ACCESS_KEY=...The server checks the bucket on start and refuses to run when a setting is missing or the bucket
can't be reached, with the reason in the log. A missing bucket is created only with
S3_CREATE_BUCKET=true.
| Provider | S3_ENDPOINT | Notes |
|---|---|---|
| AWS S3 | https://s3.<region>.amazonaws.com | Set S3_REGION to the bucket's region. |
| Cloudflare R2 | https://<account id>.r2.cloudflarestorage.com | S3_REGION=auto. No egress fees, which helps: platforms download every published file. |
| MaxIOFS, MinIO, Garage, SeaweedFS | http://<host>:<port> | Set S3_FORCE_PATH_STYLE=true. |
S3_PREFIX keeps staqly's files in a folder of a shared bucket (staqly/).
The access key needs to read, write, delete and list objects in the bucket (on AWS:
s3:GetObject, s3:PutObject, s3:DeleteObject, s3:ListBucket), plus s3:CreateBucket if you
use S3_CREATE_BUCKET. The bucket can stay private.
How files reach people and platforms
- The dashboard and the API stream files from the bucket themselves, with the same access checks as on disk: a file is found through its workspace first, never by its name in the bucket. Video seeking works (ranged reads), and the bucket needs no CORS settings.
- Platforms that download media themselves get a signed link to the bucket that works for two
hours. With
S3_PUBLIC_URL(a CDN in front of a public bucket) they get a plain link there instead. - Thumbnails are made on first use and kept next to the files.
Every workspace has its own folder in the store, on disk and in a bucket alike:
workspaces/<id>/media/ (the uploads), workspaces/<id>/thumbs/ (thumbnails, a cache) and
workspaces/<id>/projects/ (media editor projects). Files from versions before these folders are
moved into them on the first start after the upgrade.
Move existing files into a bucket
Switching from disk to a bucket doesn't move the files by itself. Copy them with:
docker exec staqly server storage migrate --dry-run # what would be copied
docker exec staqly server storage migrate # copy UPLOAD_DIR into the bucketRun it with the new STORAGE_DRIVER=s3 settings in place. Files already in the bucket are skipped,
so a run that stopped half way continues where it left off, and running it twice copies nothing.
--from <folder> copies another folder, for example the uploads of a backup. The files on disk
stay where they are until you delete them.
Backups
With disk, the uploads folder is part of the data directory you back up (see
Backup & restore). With s3, the database backup no longer
contains the files: protect the bucket with your provider's tools (versioning, replication or a
lifecycle copy to a second bucket). The workspace export with files (POST /v1/workspace/export)
reads them from the bucket either way.
Development
The development stack (make dev) runs MaxIOFS and keeps
media there, so the bucket path is the one you test every day. See
Docker Compose.