Research Report · 2026-05-22 · Hermes

CMS & Content System Stack

A complete content-system stack for delivering full web apps to boas.dev clients — authoring, storage, delivery, framework, and hosting layers. Vendor evaluation across four candidates against twelve axes, with material findings from current vendor state.

Scope: CMS + supporting content infrastructure Candidates evaluated: Directus · Payload · Strapi · Sanity Method: 4×12 scorecard + firecrawl confirmation of pricing/licence/trajectory

Verdict

Adopt Directus

Database-first headless CMS, self-hosted on Docker, BSL 1.1 licensed but effectively free at agency scale ($5M revenue threshold). Wins on schema flexibility, lock-in escape, API surface, file handling, and overall stack alignment. Pairs cleanly with the existing Cloudflare R2 storage layer and supports IS-first localization with the draft+preview+manual-publish workflow required for the brand voice.

The Stack — 9 Layers

Full picture of what gets adopted, not just the CMS choice. Each layer answers one question; together they cover the lifecycle from author → store → deliver → render.

01 · CMS
Directus 11+
Database-introspecting headless CMS. Auto-generated REST + GraphQL + websockets. Mature admin UI, native i18n, revisions, role-based permissions.
BSL 1.1 · self-host
02 · Database
PostgreSQL
Directus introspects the schema directly — pg_dump is the export story. No proprietary content format. Schema lives in raw SQL.
open · portable
03 · Object Storage
Cloudflare R2
Connected to Directus via the aws-s3 file driver (S3-API compatible). $0.015/GB storage, $0 egress. CMS file field owns referential integrity; R2 owns bytes.
S3-compat · zero-egress
04 · CDN / Edge
Bunny CDN
Sits in front of R2 for image delivery on hot paths. Bandwidth Alliance with R2 = free egress between them. Adds image-optimization headers + global edge.
added when needed
05 · Image Pipeline
Cloudflare Images
Resize / transform / format-shift on the fly. $5 per 100k stored + $1 per 100k delivered. Predictable per-image pricing. Added when first image-heavy product ships.
when first image product
06 · Video Pipeline
Bunny Stream
Per-minute pricing (~$5/1k stored + $1/1k delivered). ~50% cheaper than CF Stream, ~80% cheaper than Mux. Added when first video product ships.
when first video product
07 · Frontend
Astro (content) · Next.js (app)
Content-heavy clients ship via Astro (less JS, faster). App-heavy clients ship via Next.js (interactive dashboards, RSC). Both consume Directus via official SDK.
framework-agnostic CMS
08 · Hosting
VPS · Docker · per-client container
One Directus + Postgres container pair per client. Co-tenanted with existing services. Per-client isolation = clean blast radius + per-client backup + per-client upgrade window.
isolated tenancy
09 · Localization
Per-field locale variants + draft/preview/publish
Directus translations interface handles IS / EN per field. Publish gated to a designated operator role — drafts saved freely, publish requires sign-off. Matches brand-voice requirement.
IS-first capable

Vendor Matrix — 4 × 12

Each axis scored 1–5. The CMS choice is one decision; the surrounding stack is a separate decision. This matrix evaluates the four shortlist candidates strictly on CMS capability + adoption posture.

# Axis Directus Payload Strapi Sanity
1Authoring UX 4445
2Schema flexibility 5545
3API surface 5454
4File handling (R2 via S3) 5543
5Self-host posture 4441 🚩
6Vendor risk 42 🚩34
7Total cost (self-host scale) 5543
8Lock-in cost 5432
9Front-end integration 4545
10Multi-tenant pattern 3324
11Time-to-first-app 4545
12Stack alignment (Docker · Postgres · S3) 5432
Σ  /  60 53 50 43 43

Material Findings

Four firecrawl-confirmed findings from current vendor state. Two are licensing / trajectory pivots that reshape the conventional rankings.

Licence · Directus

BSL 1.1 with $5M revenue threshold

Self-hosting is free below the $5M annual revenue threshold. BSL converts to GPL 3 years after each release date. For any agency operating below the threshold, Directus is functionally MIT-equivalent. Commercial fallback path exists from Monospace Inc if scale crosses the line.

Trajectory · Payload

Acquired by Figma — Cloud paused

Payload joined Figma. Cloud has paused deployment of new projects; existing Cloud customers must eventually migrate. Open-source MIT core is "committed to maintain" per the announcement, but the roadmap is now subject to Figma's product strategy rather than CMS-product DNA. Self-host of the OS core remains viable; reserve as plan B only.

Licence · Strapi

CE = MIT · EE paywalls higher-tier features

Community Edition keeps MIT, RBAC, custom roles + permissions, REST + GraphQL, unlimited usage, and full content management. Enterprise Edition (quote-based) walls SSO, audit logs, review workflows, Strapi AI, and content history. Strapi Cloud Growth $45/mo. Less aggressive walling than competitor folklore suggests — but multi-tenant pattern remains historically weak.

Pricing · Sanity

Free 20 seats / 10k docs · Growth $15/seat (org-scoped)

Free tier is genuinely generous: 20 seats, 10k documents, 1M API requests/month. Growth is $15 per seat per month org-scoped, not per-project — so a 5-seat org pays $75/mo regardless of project count. Cost ceiling reachable for small agencies but managed-only model conflicts with self-host preference. Useful as authoring-UX reference, not as primary tier.

Why Directus Wins

Six cells scored 5/5. Pinned strengths against the brief:

Rejected Alternatives

Eight other candidates considered and dropped, with one-line reasons.

Payload (50/60)
TrajectoryFigma acquisition · Cloud paused · OS roadmap subject to Figma strategy. Reserve fallback only.
Strapi (43/60)
Multi-tenantMulti-tenant historically weak · Strapi-specific schema format raises lock-in · EE-wall direction of travel.
Sanity (43/60)
Managed-onlyCannot self-host the content API · cost ceiling reachable but architecture mismatch · useful for authoring-UX reference.
PocketBase
i18nLocalization is DIY · no native per-field locale variants · fails the brand-voice draft/preview/publish requirement.
TinaCMS
i18n + git-as-DBSame i18n gap · git-as-DB pattern incompatible with end-client editing workflow.
Hygraph
Managed-onlyManaged-only + GraphQL-only architecture creates double lock-in.
Contentful
PricingEnterprise pricing tier is an order of magnitude above the agency budget envelope.
Builder.io / Plasmic
DNAVisual-marketing-builder DNA · mismatch with dashboard + structured-content client work.

Architecture in One Paragraph

Each client web app gets a dedicated Docker pair: Directus + Postgres, isolated on a shared VPS alongside sibling services. Content authors work in the Directus admin — drafts, revisions, IS / EN per-field translations, role-gated publish. File uploads flow through the Directus file field into Cloudflare R2 via the aws-s3 driver, with Bunny CDN as the hot-path edge. The front-end — Astro for content-heavy clients, Next.js for app-heavy clients — consumes Directus via REST or GraphQL through the official SDK. Image transforms route through Cloudflare Images when added; video routes through Bunny Stream when added. Backup is pg_dump on a daily cadence plus R2 bucket versioning. Upgrades happen per client on independent windows because the per-client isolation pattern decouples them.