Directus vs Payload vs SonicJS: Open Source CMS Battle
Compare Directus, Payload, and SonicJS open source headless CMS platforms. Performance benchmarks, database flexibility, and developer experience analysis.

Directus vs Payload vs SonicJS: Open Source CMS Battle
TL;DR — Directus wraps existing databases but struggles with performance at scale (1.5s+ response times with relations). Payload offers excellent TypeScript DX but requires 13GB RAM for dev servers. SonicJS is edge-first with zero cold starts and sub-50ms global latency.
Key Stats:
- Directus: 45,000ms+ load times with 400k files (GitHub Issue #7783)
- Payload: 15x slower than Mongoose for populated queries
- SonicJS: Under 50ms globally, zero cold starts
- All three: MIT/GPL licensed, truly open source
Looking for an open source headless CMS? Directus, Payload, and SonicJS represent three different philosophies. This comparison covers the real-world trade-offs based on developer feedback and production experiences.
Quick Comparison
| Feature | Directus | Payload CMS | SonicJS |
|---|---|---|---|
| Architecture | Node.js (Express) | Next.js-native | Edge-first (Cloudflare Workers) |
| Database | PostgreSQL, MySQL, SQLite, MSSQL, Oracle | MongoDB, PostgreSQL | D1 (SQLite at edge) |
| Existing DB Support | Yes (wraps existing tables) | No | No |
| TypeScript | Partial | Native, excellent | Native, full type safety |
| Cold starts | 300-1000ms | 100-300ms | 0-5ms |
| Global latency | 200-500ms | 150-400ms | Under 50ms |
| Admin UI | Vue.js | React | HTMX |
| License | GPL v3 / BSL | MIT | MIT |
| GraphQL | Built-in | Yes | Roadmap |
The Real Problems
Directus: Performance at Scale
Directus's strength—wrapping existing databases—becomes its weakness with complex queries.
Documented Performance Issues from GitHub:
| Issue | Scenario | Reported Time | Notes |
|---|---|---|---|
| #11766 | 500 items with relations | 1,500ms | runAST bottleneck |
| #7783 | 400k files | 45,000ms+ | Heap out of memory |
| #1575 | Simple queries | 4,000ms | Cold query performance |
| #2020 | 1,000 items | 5,500ms | Collection listing |
| #10034 | 200 row inserts | Connection crashes | Pool exhaustion |
"With a lot of items linked to other items, Directus becomes slow. For only 500 items, Directus took 1.5 seconds to load all of them." — GitHub Issue #11766
The file system performance is particularly concerning:
"Loading with 400k files takes around 45 seconds. My latest test was now at 500 seconds." — GitHub Issue #7783
Common Directus pain points:
- Connection pool crashes under load (Issue #10034)
- Missing database indexes cause slowdowns
- Memory consumption with large datasets
- Complex relations degrade performance significantly
Payload: Resource Hunger
Payload's TypeScript-first approach is excellent, but the development experience has issues.
Documented Performance Issues from GitHub:
| Issue | Scenario | Reported Time | Notes |
|---|---|---|---|
| #11325 | 1,000 docs with populate | 4,000ms | 15x slower than Mongoose |
| #7505 | Query with joins | 5,734ms | vs 23ms raw Mongoose (249x slower) |
| Discussion #14230 | Dev server | 10-60s per call | 13GB RAM consumption |
| Discussion #2413 | Production page loads | 20,000ms | With ~10 concurrent users |
"Dev server needs 13GB of RAM just to start." — GitHub Discussion #14230
"find() returning populated documents is 15x slower than the same query in Mongoose." — GitHub Issue #11325
Common Payload pain points:
- Heavy memory usage during development
- Performance degrades with population/relations
- S3 adapter memory leaks reported
- Build times can be excessive
SonicJS: Honest Limitations
SonicJS is newer and building rapidly:
- Smaller plugin ecosystem (growing with community)
- D1 database only (edge-native by design)
- Learning curve for edge concepts
- GraphQL on roadmap, not shipped yet
The difference? Edge-first architecture means you're not fighting fundamental design decisions.
Architecture Deep Dive
Directus: Database-First
Directus can wrap any existing SQL database, making it unique for brownfield projects:
// Directus wraps your existing database
{
"database": {
"client": "pg",
"connection": {
"host": "localhost",
"database": "existing_db"
}
}
}
When this works well:
- Legacy database integration
- Multiple applications sharing data
- Gradual CMS adoption
When this fails:
- Complex relations at scale
- High-traffic applications
- Global performance requirements
Payload: Next.js Native
Payload integrates directly into Next.js applications:
// payload.config.ts
import { buildConfig } from 'payload/config'
export default buildConfig({
collections: [Posts, Users, Media],
db: mongooseAdapter({ url: process.env.DATABASE_URI }),
})
Strengths:
- Same deployment as your frontend
- Excellent TypeScript integration
- Code-first content modeling
Weaknesses:
- Inherits Next.js cold start times
- Memory-intensive development
- Performance with populated queries
SonicJS: Edge-First
SonicJS runs on Cloudflare Workers globally:
// sonic.config.ts
import { createSonicJS } from '@sonicjs-cms/core'
const cms = createSonicJS({
database: env.DB, // D1 at the edge
cache: env.CACHE, // KV for instant reads
storage: env.STORAGE, // R2 for files
})
export default cms.app
Implications:
- Zero cold starts (V8 isolates)
- Under 50ms response globally
- Automatic scaling
- Predictable Cloudflare pricing
Performance Comparison
| Scenario | Directus | Payload | SonicJS |
|---|---|---|---|
| Simple GET (same region) | 50-200ms | 50-150ms | 15-30ms |
| Simple GET (cross-region) | 200-500ms | 150-400ms | 30-60ms |
| Complex query (relations) | 500-1500ms+ | 200-500ms* | 40-80ms |
| Cold start | 300-1000ms | 100-300ms | 0-5ms |
| With 400k files | 45,000ms+ | Unknown | Under 100ms |
*Payload populated queries can be 15x slower than raw queries
Why Edge Wins
When your CMS runs in 300+ locations worldwide:
User in Sydney → Sydney Edge (15ms)
User in Tokyo → Tokyo Edge (15ms)
User in London → London Edge (15ms)
vs Traditional (server in Virginia):
User in Sydney → Virginia Server (250ms+)
Database Flexibility
Directus: Maximum Flexibility
Supports PostgreSQL, MySQL, MariaDB, SQLite, MS SQL Server, Oracle, and CockroachDB. Can introspect existing schemas.
Payload: MongoDB or PostgreSQL
Excellent with MongoDB, PostgreSQL adapter available but newer. No existing database wrapping.
SonicJS: D1 Only (By Design)
D1 (SQLite at the edge) provides:
- Global replication
- Zero configuration
- Edge-native performance
- Standard SQL queries
Trade-off: No PostgreSQL/MongoDB. Benefit: Consistent global performance.
Developer Experience
TypeScript Support
Directus: SDK is TypeScript, but content types require generation:
// Types must be generated from schema
import type { Collections } from './directus-types'
Payload: Native TypeScript with excellent inference:
import { CollectionConfig } from 'payload/types'
const Posts: CollectionConfig = {
slug: 'posts',
fields: [
{ name: 'title', type: 'text', required: true },
],
}
SonicJS: Full type safety from schema to API:
const posts = defineCollection({
name: 'posts',
fields: {
title: { type: 'string', required: true },
content: { type: 'richtext' },
},
})
// Types automatically inferred
Content Modeling
All three support code-first content modeling. Key differences:
- Directus: Can also use visual schema builder
- Payload: Richest field types and hooks
- SonicJS: Simplest API, edge-optimized
Pricing Comparison
Directus
- Self-hosted: Free (GPL v3)
- Cloud: Starting at $99/month
- Hidden costs: Database hosting, server management
Payload
- Self-hosted: Free (MIT)
- Cloud: Starting at $35/month
- Hidden costs: High memory requirements increase hosting costs
SonicJS (Cloudflare)
- Free tier: 100,000 requests/day, 10GB D1
- Paid: $5/month flat + minimal usage
- Typical site: $5-20/month total
When to Choose Each
Choose Directus If:
- You have an existing database to wrap
- Multiple applications share the same data
- You need maximum database flexibility
- Performance at scale isn't critical
Choose Payload If:
- You're building with Next.js
- TypeScript DX is paramount
- You have resources for higher memory usage
- MongoDB is your preferred database
Choose SonicJS If:
- Global performance is critical
- You want predictable, low costs
- Zero cold starts matter
- Edge-first architecture appeals to you
Migration Considerations
Migrating to SonicJS
From Directus:
- Export data via Directus API
- Map collections to SonicJS schemas
- Import via SonicJS API
- Main challenge: SQL dialect differences
From Payload:
- Export collections and content
- Convert Payload config to SonicJS
- Migrate MongoDB documents to D1
- Main challenge: MongoDB → SQLite data transformation
FAQ
Can SonicJS wrap my existing database like Directus?
No. SonicJS is designed for D1 (edge SQLite) to provide consistent global performance. If you need to wrap an existing database, Directus is the right choice.
Is Payload's memory usage really that bad?
During development, yes. Production deployments are more reasonable, but you'll need adequate RAM for the dev server. This is documented in community discussions.
How does Directus handle high traffic?
It requires careful configuration—connection pooling, caching layers, and potentially read replicas. Out of the box, it can struggle under load per community reports.
What about GraphQL support?
Directus and Payload both support GraphQL. SonicJS focuses on REST for edge performance, with GraphQL on the roadmap.
Conclusion
Three different philosophies:
- Directus excels at database flexibility but struggles with performance at scale
- Payload offers best-in-class TypeScript DX but requires significant resources
- SonicJS delivers edge-first performance with architectural simplicity
For new projects prioritizing global performance and cost efficiency, SonicJS provides a compelling modern alternative.
Key Takeaways
- Directus can wrap existing databases but performance degrades with scale
- Payload offers excellent TypeScript but needs 13GB+ RAM for development
- SonicJS delivers under 50ms global latency with zero cold starts
- All three are genuinely open source
- Edge-first architecture eliminates geographic latency
Ready to try edge-first CMS? Get started with SonicJS in under 5 minutes.
Sources & References
Directus Issues
- Performance with Relations (Issue #11766) — 1.5s with 500 items
- File Loading Slowdowns (Issue #7783) — 45s+ with 400k files
- Cold Query Performance (Issue #1575) — 4s simple queries
- Collection Listing Performance (Issue #2020) — 5.5s for 1k items
- Connection Pool Crashes (Issue #10034) — Pool exhaustion
Payload Issues
- Query Performance (Issue #11325) — 15x slower than Mongoose
- Query Overhead (Issue #7505) — 249x slower with joins
- Dev Server Memory (Discussion #14230) — 13GB RAM
- Production Performance (Discussion #2413) — 20s page loads
Related Articles

Directus vs Sanity vs SonicJS: Database Wrapper vs SaaS vs Edge
Compare Directus, Sanity, and SonicJS headless CMS. Database flexibility, real-time collaboration, and edge-first performance analyzed.

Strapi vs Directus vs SonicJS: Headless CMS Comparison
Compare Strapi, Directus, and SonicJS headless CMS. Real performance data, developer feedback, and why edge-first architecture outperforms traditional servers.

Strapi vs Payload vs SonicJS: Headless CMS Comparison
Compare Strapi, Payload, and SonicJS headless CMS. Real benchmarks, developer feedback, migration issues, and why edge-first delivers sub-50ms global latency.