Tooling
Sanity vs Payload vs Directus: a 14-editor field test
A Haarlem trade publisher runs 14 editors and one weekly print deadline. We graded three headless CMS stacks on the metrics that actually decide the bill.

It is 22:47 on a Wednesday. The editor-in-chief at a Haarlem trade publisher has just noticed that a freelancer's bulk import has overwritten the category fields on 217 archive articles. The print edition ships to the press at 09:00. No one on the 29-person team has "ops" in their title.
That moment picks your CMS for you. Not the per-seat sticker price. Not the GraphQL schema. The 23:00 restore.
We spent the last quarter rebuilding the editor desk for exactly this publisher. The shortlist came down to three: Sanity, Payload, and a hand-rolled Directus + Postgres stack. On a feature matrix they look interchangeable. They are not.
The per-seat math
The headline number is the one every founder runs first.
Sanity's Growth plan charges per editor seat. For 14 redacteuren you land somewhere around €200 per month on the listed tariff, depending on how many seats your team can route through cheaper viewer roles. Add the document-history retention beyond 30 days and an enterprise SSO add-on and you are closer to €350. The live number sits on the public Sanity pricing page.
Payload is MIT-licensed and free if you self-host. Payload Cloud's Standard tier sits around $35 per month for the hosted version with the same editor count. We have seen real teams run Payload on a single €18-per-month Hetzner CX22 with a managed Postgres on the side. Total fixed cost for 14 editors: under €50 per month.
Directus has a similar cost shape. It is free under its Business Source License if your organisation's annual revenue is below $5M, otherwise a commercial agreement applies. A trade publisher pushing €3M is in the safe zone today, less so the year they acquire a competitor. Worth a calendar reminder.
If you stop reading at this line, Payload and Directus win 6-to-1 on sticker price. We almost did. Then we asked the second question.
Preview latency on a 4G train
A senior editor at this publisher commutes from Utrecht to Zwolle three days a week. She writes the editorial in transit. She wants to hit "preview" and see the article render against the actual front-page layout on her phone before the train reaches Amersfoort.
Sanity's Studio is a static React app. The Content Lake sits on their global CDN with a regional gateway in the EU. Draft previews resolve fast even during 4G handovers, because the API does not have to round-trip to your origin server. Across a week of measurements we saw a median 380ms from publish to preview update on that stretch.
Payload runs wherever you host it. Park it on a Hetzner box in Falkenstein and you get a fast response from Berlin and a less-fast one when the train passes through a 3G island east of Amersfoort. We measured 700ms median, with a long tail past four seconds when the connection re-handshaked. A Vercel edge function in front of it closes some of the gap; not all of it.
Directus has a similar profile to Payload but ships with a built-in WebSocket live preview, which is a real feature when the rest of the editor desk wants to watch a draft come together. The tradeoff is that the WebSocket connection drops on every cell-tower handover. Editors learn to refresh.
This is the kind of thing you cannot benchmark in a Slack channel. We sat on the 17:08 to Zwolle with the editor. We watched her hit preview, lose connection, hit it again, swear. The difference between 400ms and "lost connection, try again" is not a number. It is a vibe.
The 23:00 restore
Now the part that picks the winner.
Sanity. Document History is built into Studio with 30 days on Growth, longer on Business. For a single botched document an editor with the right role can revert in three clicks. For 217 documents at once you need the dataset export and import flow. If you do not have a recent sanity dataset export snapshot, you can request a point-in-time restore from Sanity support. They respond inside the hour during EU business hours. At 23:00 on a Wednesday, that hour starts when their Amsterdam team logs in around 08:30. By then the print deadline is gone.
Payload. You own the Postgres. Restore is pg_restore against your last backup. This only works if you actually configured the backups. We have walked into too many Payload-on-Hetzner stacks where the backup cron was commented out three months ago "while we tested something." Payload also exposes per-document versioning if you set versions: true on the collection. Most teams forget. Set it on day one.
Hand-rolled Directus. Versions and a full activities log are on by default. Every change is recorded with the user, the timestamp, the previous value, and the new value. The Directus UI has a built-in revert per item, scriptable across 217 items via the SDK. You still own the Postgres and can pg_restore as a backstop.
This is where the stack diverges. Sanity assumes your support contract is the disaster-recovery plan. Payload assumes you are. Directus splits the difference: it ships the tools, and you still own the database.
If your team picks Payload or a self-hosted Directus and there is no scheduled pg_dump running, your disaster-recovery plan is fiction. Set the cron and test the restore on day one, not after the first incident.
Where each one actually fits
After three months running parallel pilots on a sample editorial workload, the answer was not "one of these is best." It was "each one is best for a specific shape of team."
Sanity fits when you have budget for the per-seat line and do not want to think about infra. The schemas are typed, the studio is opinionated, and editors who like rails will love it. Preview latency is best-in-class without effort.
Payload fits when you have at least one engineer who treats the CMS like part of the application. You want to ship a Next.js front-end and a CMS admin out of one Node process. You want to control every byte and you have a backup strategy you actually test.
Directus on Postgres fits when you want the operational ergonomics of a real database without giving up the editor UX of an opinionated admin. Your editors will use bulk operations, imports, and exports daily. You need a real activity log without writing your own. You sit under the BSL revenue threshold or you are comfortable with the commercial license.
For this Haarlem publisher we shipped Directus on Postgres. The deciding moment was a hallway question from the print editor: "if something goes wrong, can I undo it without calling someone?" Directus answered yes by default. Read the rest of the trade-offs on the Directus docs if you are between this and Payload.
The five-minute audit
If you are sitting on Sanity, Payload, WordPress, or anything else and you do not know what your 23:00 restore looks like, here is a five-minute test. Open your CMS. Make a small destructive change to a single document. Change the title. Now try to restore it without asking an engineer. Time yourself.
If you cannot do it in under 60 seconds, your CMS is one wrong import away from a long night. The next step is to wire a snapshot in front of every bulk operation. On a Directus + Postgres stack the script looks like this.
# 1. Snapshot before any bulk operation
pg_dump -Fc -h db.example.nl -U editor cms_prod \
> /backups/cms_pre_import_$(date +%Y%m%d_%H%M).dump
# 2. If the import goes wrong, restore the touched tables only
pg_restore -h db.example.nl -U editor -d cms_prod \
-t articles -t articles_translations --data-only \
--clean /backups/cms_pre_import_20260615_2145.dump
# 3. Verify with a row count and a manual spot check
psql -h db.example.nl -U editor cms_prod \
-c "SELECT count(*), max(updated_at) FROM articles;"The whole flow fits on a sticky note. You want it on a sticky note.
The thing the trade publisher actually shipped with
When we wired the editor desk together at this publisher we put process automation in front of every bulk import: each upload runs a pg_dump first and posts the snapshot path into a Slack channel. The night the freelancer's XLSX overwrote 217 categories, the editor-in-chief copied the path from Slack, ran one restore command, and went to bed.
That is the win. Not the CMS choice itself, but the fact that the CMS choice left room for a five-line backup script to do the rest. Open your own CMS tonight, change a title, time the undo.
Key takeaway
The CMS that wins is not the one with the prettiest schema. It is the one that lets a non-engineer restore 217 documents at 23:00.
FAQ
Can a non-engineer restore a botched bulk import in Sanity?
Yes for a single document via Studio's history view. A 200-document restore needs the dataset export and import flow or a support ticket. Plan it before you need it.
Is Directus free for a 14-editor team?
Yes under the BSL license if your organisation's annual revenue is below $5M. Above that threshold a commercial agreement applies. Check before you scale or acquire.
Which headless CMS has the lowest draft-preview latency on mobile?
In our Utrecht-Zwolle 4G tests Sanity's hosted Content Lake had the lowest median. Self-hosted Payload or Directus latency depends on where you host the origin.
Should I run Payload Cloud or self-host?
Self-host if you already run a Postgres and have someone to own backups. Payload Cloud is worth the small monthly fee if you do not want to maintain another server.