Skip to content
Modularcms

■  Headless CMS · .NET 10 · PostgreSQL

Content, set in modules.

ModularCMS composes pages from reusable blocks, proofs them through review and releases, and delivers them to any frontend through three layers of cache. Media, translations, personalization, consent, forms, and frontend users are built in, and it runs on any cloud or on a single box.

Kubernetes, Docker Compose, or Terraform on AWS · Migrates from Umbraco

alpenglow / news / 2026 / winter-seasonDraft
  1. Draft
  2. Review
  3. Live

Autosaved · 3 blocks · 4 checks passed

Hero block

Alpenglow opens its winter season

Twelve lifts, two new runs, and a later last chair.

Lift status

Ski school

Webcams

Quote block

“Best opening snow in a decade.”

GET/delivery/api/v1/content/item/news/2026/winter-season
status
200
cache
no-store
time
41 ms
{ "contentType": "article",
  "properties": { "sections": { "blockList" } },
  "meta": { "preview": true } }

Illustrative · timings from the verification run

Cached response, p95
8.5 ms
at 2,741 requests a second
Publish to visible, p95
0.56 s
every cache layer updated
Pages per site
250k
type changes stay instant
Outbound calls
0
no telemetry, no licence checks

Timings measured on a single-host Docker Compose verification run (September 2026); the platform’s targets are 150 ms for cached responses and 5 s from publish to visible.

§ 01  The composition model

Blocks, not pages. Reuse is the default.

Model content once as element types, then let editors set pages from them. The same block serves every site, language, and channel you deliver to.

  1. 01

    Element types, composed

    Block List and Block Grid fields assemble pages from element types with their own content and settings. Pickers, repeatable groups, links, embeds, tables, locations, and date ranges come as field types.

  2. 02

    Change the model at any size

    Values live as JSONB inside a relational shell, so adding or reshaping a field on a 250,000-page site saves immediately: no lock, no migration, no reindex.

  3. 03

    One block, many audiences

    A block can carry variants per audience segment and declares the consent category of anything third-party it loads, right in the Delivery API response.

§ 02  What's inside

Everything a content team needs, in one proof.

Not a starter kit and a list of integrations: the features large sites need are part of the platform, tested together.

Governance that ships

Multi-stage approvals, timezone-true scheduling, content locks, and releases that bundle many pages into one atomic publish you can preview, approve, and roll back.

Cloudinary-style media

Upload an original once. Sizes, focal-point crops, formats, and adaptive video are produced from the URL and served from the CDN, never from the app.

Translations as children

Each translation sits under its master item with its own status, review, and XLIFF round trip. Right-to-left and CJK work end to end.

Personalization that keeps the cache

Segments, block and field variants, and recommendation slots, chosen by a separate uncached decision so every content response stays cacheable.

Consent, built in

Jurisdiction-aware banners, script gating, Global Privacy Control, and cookie scanning, delivered by the same one-tag loader.

Many tenants, one deployment

Organization, instance, site. Every row, cache key, and storage path is scoped by instance and enforced by PostgreSQL row-level security.

Frontend users and forms

Member pools with single sign-on across domains, self-service accounts, gated content, and forms whose submissions are encrypted per instance.

Search, SEO, and monitoring

An SEO tab that fills itself, checks that can block a publish, sitemaps and redirects, tunable search, and a crawler watching for broken links and Core Web Vitals.

Read the full feature list: 18 areas, 128 capabilities

§ 03  Delivery

One publish. Three caches. Nothing stale.

Each cache layer has one job and one way to invalidate it, so a cache problem is always traceable to a single place and a publish never waits on a purge.

Layer 1

Edge cache

Where
Any CDN: CloudFront, Front Door, Cloudflare, Fastly
Holds
Full responses keyed on the site's publish version
Invalidated
A new version makes old entries unreachable. No purge storms.

Layer 2

Output cache

Where
In process, on every API node
Holds
Composed documents with a tag for every item they depend on
Invalidated
Evicted by tag the moment a picked author, image, or type changes

Layer 3

Distributed cache

Where
Redis or Valkey
Holds
Publish versions, routing tables, locks, rate limits, queues
Invalidated
Written by the publishing pipeline; degrades safely if Redis goes away
  1. 01An editor, a schedule, or a release publishes.

  2. 02The pipeline works out every affected item: ancestors, referrers, translations.

  3. 03The site's publish version moves on, and the tag set goes out over Redis.

  4. 04Every API node evicts matching entries; search, sitemap, and redirects update.

  5. 05Signed webhooks tell consumer sites to revalidate, carrying the new version.

  6. 06The next request is composed once from PostgreSQL and fills both caches again.

What personalization does not touch

Variants, recommendations, and member content never vary a cached response.

Decisions come from a separate uncached endpoint and are applied by the loader in the browser. Preview, member-only content, and anything personal is marked no-store and never reaches the edge.

§ 04  For developers

Your frontend. Your framework. Typed all the way down.

Public sites are your own apps. ModularCMS gives them a versioned Delivery API, a generated SDK, a Next.js adapter, one script tag for widgets, and a CLI for your pipeline.

  • One OpenAPI document generates the SDK, the backoffice client, and the reference
  • An API explorer in the backoffice with Try it and curl, fetch, and SDK snippets
  • Structure as code: plan, diff, and apply content types from Git
  • Two-way Git sync for structure, with GitHub pull requests for releases
  • Signed plugin packages and backoffice extension points
The developer tour

app/[...slug]/page.tsx

import { Blocks, getDeliveryClient, seoToMetadata, cacheTags } from "@modularcms/next";
import { expand } from "@modularcms/delivery";
import { Hero, CardGrid, Quote } from "@/components/blocks";

export async function generateMetadata({ params }) {
  const path = "/" + (await params).slug.join("/");
  const page = await getDeliveryClient().content.item(path);
  return page ? seoToMetadata(page.seo, page) : {};
}

export default async function Page({ params }) {
  const path = "/" + (await params).slug.join("/");
  const cms = getDeliveryClient({ tags: [cacheTags.path(path)] });
  const page = await cms.content.item(path, { expand: expand({ blocks: true }) });

  return (
    <Blocks
      items={page?.properties.sections?.items}
      components={{ hero: Hero, cardGrid: CardGrid, quoteBlock: Quote }}
    />
  );
}

The adapter wires draft mode, cache tags, SEO metadata, images, rich text, menus, and personalization into the App Router. The same page code renders drafts and published content.

§ 05  Hosting

Runs on any cloud. Or on none.

Four portable services are all the core needs. Everything else is optional and has an in-platform fallback, so you choose the cloud, and can leave it.

Required

  • PostgreSQL 16+
  • Redis or Valkey
  • Keycloak
  • A storage backend

A local volume on a single host, or S3-compatible, Azure Blob, or Google Cloud Storage for many nodes.

Optional, with fallbacks

  • OpenSearch / Elasticsearch
  • Any CDN
  • Email service
  • Analytics providers
  • AI providers

Search falls back to PostgreSQL full-text search; analytics to the first-party store.

Deploy with

  • Kubernetes

    Helm chart with migrations as a hook, rolling restarts, probes

  • Docker Compose

    The whole platform on one host, for small teams and local work

  • Terraform

    Modules for managed dependencies, AWS first; nothing requires AWS

No telemetry. No licence checks. No calls home.

The only outbound connections are the update check and the store connection, and both are off until you turn them on.

§ 06  Coming from Umbraco

Bring the site. Keep the ideas you like.

ModularCMS borrows the good parts of Umbraco 17 — document types, Block List and Block Grid, dictionary items, public access, culture and hostnames — and imports real Umbraco sites, Umbraco Cloud included.

How migration works

Migration · alpenglow-umbraco

rehearsed
Document types → content types
38
Element types and blocks
61
Content nodes and translations
12,406
Media from the Azure Blob container
8,915
Dictionary items
402
Redirects kept from Umbraco
1,137

Illustrative · rehearse into staging, compare, then run for real

§ 07  Roadmap

Version 1.0 is the foundation. Here's what gets set next.

Phases 1 and 2 ship together as 1.0. AI, the widget builder, and the marketplace follow, built on hooks that are already in place.

  1. Phase 1in 1.0

    Foundation

    The core CMS, media, consent, the Delivery API, tenancy, hosting, and the Umbraco import.

  2. Phase 2in 1.0

    Analytics, users, personalization

    Analytics, full frontend users, forms, personalization, releases, developer tooling, and integrations.

  3. Phase 3next

    AI

    AI with your own keys or metered platform keys, budgets, and every call accounted for.

  4. Phase 4next

    Widgets and catalog

    A widget builder, the component catalog, visual editing on the live site, and co-editing.

  5. Phase 5next

    Marketplace

    The ModularCMS Store for publishing, licensing, and selling packages.

See the full roadmap

■  Ready for press

See your content set in modules.

We’ll walk through the backoffice with your own site in mind, or import a copy of your Umbraco site and show you the result.