Features / Extend

Many sites, one instance

Spaces keep content, assets, history and languages separate per site — on a single process. Create, duplicate, rename at runtime; schemas shared, API keys scoped per space.

An agency runs twelve client sites. Twelve CMS instances mean twelve deployments, twelve upgrades, twelve backups. RustyCMS runs twelve spaces on one process instead: each with its own content, its own assets, its own history, its own languages — sharing the binary, the schemas and the maintenance.

Separate what has to be separate

A space is its own content room. Entries, images, version history, reference index — none of it sees the neighbour. On disk that is one directory per space:

content/
├── site-a/    assets/  .history/  page/  post/
└── site-b/    assets/  .history/  page/  post/

Each space decides which languages it carries: { locales: ["de", "en"] } in its settings, otherwise the global default from RUSTYCMS_LOCALES applies. A single-language club site and a trilingual shop run on the same instance without one inheriting the other’s languages.

Share what should be shared

Schemas do not have to be copied per space. A type pack is maintained once and subscribed to by every space; where a site differs, it adds an override that names only the difference. Everything else keeps coming from the pack — a change there reaches every subscribing site immediately.

One parameter on the request

Every content and asset endpoint accepts ?_environment=<name>. Without it, the first space in RUSTYCMS_ENVIRONMENTS answers — the default. A site’s frontend sets the parameter once in its API client and needs to know nothing else.

GET /api/environments
# → { "environments": ["site-a", "site-b"], "default": "site-a" }

GET /api/content/post?_environment=site-b

Create without a restart

Spaces are not a deployment concern. Create, duplicate, rename, delete — all at runtime, via the API or on the “Spaces” page in the admin UI. Duplicating copies content, assets and history in full, so a staging copy of production exists in seconds.

POST /api/environments/site-a/duplicate
{ "name": "site-a-staging" }

Names use a-z, 0-9, - and _. The last remaining space cannot be deleted, and every write requires write permission.

Access per space

An API key can be restricted to spaces: in RUSTYCMS_API_KEYS_JSON, each key carries a list of allowed environments, optionally also allowed collections. The key in site B’s frontend reads site B and nothing else. The same applies to admin UI users — allowed_environments decides which spaces someone sees at all. Admin roles bypass both lists.

What doesn’t happen

Deletion is final — there is no recycle bin for a whole space, but there is the automatic per-environment backup. In Postgres mode, where content lives in table rows rather than directories, create and list work; duplicate, rename and delete answer with 422 there, because they would move directories.

Want to see this on your own project?

Tell me briefly what you're planning. I'll show the admin UI on a real installation and say honestly whether it fits.

Request early access