Files on a disk, rows in a table, and nothing clever in between

Nobody chooses a CMS for its media library, and everybody checks it has one. This is the short page: what it does, and where the bytes go.

For everybody, eventually

Bytes never live in the database

An asset is a row holding a name, a kind, a size, alt text, tags, a folder — and a storage key naming where the bytes actually are, plus the name of the provider holding them. The file itself is on a disk or in a bucket.

Which provider handles an upload is decided at upload time and then recorded on the row. Downloads and deletes route by what is stored on the row rather than by re-running the decision, so changing the configuration later does not orphan everything uploaded before it.

Because a provider is just an implementation of three operations — put, get, remove — plus an optional direct URL, a new backend is a package rather than a change to the media plugin. Five ship: local disk, any S3-compatible bucket, Azure Blob Storage, Google Cloud Storage and Vercel Blob, with an in-memory one for tests.

What it does

Folders, and a library you can filter
Assets live in folders and carry tags, and the library filters and sorts like every other list in the product.
Two derivatives per image
A thumbnail at 320px and a preview at 1280px, generated on upload and never upscaled. The editor asks for a derivative, so a page of twelve images does not cost twelve full-size downloads.
Alt text lives with the asset
Written once, carried everywhere the asset is used, and counted on the dashboard as a standing total rather than a percentage.
Media fields on any content type
One asset or several, restricted by kind or MIME type if you want. The values ride the entry’s own bag, so they are captured by version snapshots and synced across languages like any other field.

What happens when the file is not an image

Derivative generation is best-effort by design. An SVG, a corrupt file or a format the processor does not understand produces no derivatives and does not fail the upload — the asset lands, and downloads fall back to the original.

Restricting what a field accepts happens in two layers. The content model only shape-checks that a media value is an identifier; verifying that the asset exists, belongs to this workspace and matches the field’s restriction needs the media tables, so the media plugin binds a port the content plugin declares.

If the media plugin is absent entirely, media fields still validate their shape and still store. They simply skip the existence check — which is the general pattern here: a missing plugin makes a smaller product, not a broken one.

The specifics

Short page, so here is the whole of it in one table.

Thing What it is
Storage backends Local disk, any S3-compatible bucket, Azure Blob Storage, Google Cloud Storage and Vercel Blob. One per deployment; a sixth is a package implementing three operations.
Asset kinds Image, video, audio, document and archive — the coarse categories a media field can restrict itself to.
Finer restriction A field can also name exact MIME types or a type/* glob. An unknown kind is rejected when the content type is defined.
Derivatives thumb at 320px and preview at 1280px, generated on upload, never upscaled, and kept in a reserved key namespace so one can never overwrite an original.
On the asset row Name, kind, MIME type, size, alt text, tags, folder, created date, and the storage key plus provider.
Scope One library per workspace, with folders inside it.

The questions people actually check

Where do the files go?

Wherever you configured: a directory on the server, or a bucket on S3, Azure, Google Cloud or Vercel. That decision is in your configuration file, not in a settings screen somebody can change by accident.

Is there a file size limit?

Whatever your reverse proxy and your storage allow. The product does not impose one of its own.

Can I reuse an asset across brands?

No. Media is scoped to a workspace like everything else, so the same logo in two workspaces is two uploads.

What happens if I delete an asset an entry uses?

The entry keeps the identifier and the reference resolves as missing rather than breaking the page. There is no cross-plugin foreign key, so the database will not stop you.

Every feature is included, free

The core is MIT licensed and every feature is in it — none of them is paid-only. Community runs free in production; a plan buys room and governance, not a different product.