Twelve production lessons from building on Cockpit CMS v2
Self-hosted, open-source, headless. The things that bit us in production and the patterns that earn their keep.
We've shipped four production sites on Cockpit CMS v2 in the past 18 months. Including this one. It's not the most-Googled headless CMS — that crown belongs to Sanity, Strapi, Contentful, in some order — but Cockpit fits a niche the famous names ignore: small to medium content workloads on cheap infrastructure, edited by people who don't want a 90-minute training session.
Here are twelve things we learned the hard way. If you're evaluating Cockpit, this will save you a week.
1. Cockpit v2 is not Cockpit v1
If a tutorial is from before 2024, throw it out. API shape changed. Directory layout changed. Admin UI changed. Data model changed. v2 is, for practical purposes, a different product. Filter your search results to "v2" or "2024+".
2. Self-hosting means you own the WAL
Cockpit v2's default storage is SQLite. SQLite uses Write-Ahead Logging by default. The .sqlite-wal and .sqlite-shm files matter. If you rsync only the .sqlite file to a new server, you lose recent writes. Either checkpoint first (PRAGMA wal_checkpoint(FULL)) or copy all three files together.
3. The schema cache is a real thing
After editing a model JSON, Cockpit caches the parsed schema in storage/data/app.memory.sqlite. Until you delete that file, the admin UI will show stale fields. Build it into your deploy script.
4. i18n is per-field, not per-document
You mark fields as i18n: true in the schema. Cockpit stores them as field (default locale) and field_pt, field_es, etc. The API serves whichever locale you request via ?locale=pt. Plan your schema with this in mind; retrofitting later is a manual SQL job.
5. The API key in the URL is a footgun
Don't pass the API key as a query parameter. It ends up in logs, in browser history, in referrer headers. Use the api-key HTTP header instead. Build your fetch helper to enforce it.
6. Image assets need a proxy
Cockpit's image API requires the API key. If you reference Cockpit URLs directly from your frontend, you expose the key. We wrote a 30-line PHP proxy that takes ?id=&w=, fetches with the key server-side, and returns WebP with cache-control headers. This is true for any headless CMS with a private asset API.
7. Self-HTTPS loopback breaks on some hosts
When PHP on example.com tries to call https://example.com/cockpit/api/..., some shared hosts refuse the loopback. file_get_contents() returns false with no error. The fix: use cURL (it works where file_get_contents doesn't) with a sane fallback. Don't assume your local dev environment matches your production one.
8. Cockpit doesn't have a built-in webhook for "content changed"
There's an event system inside Cockpit's PHP, but no out-of-the-box outbound webhook on save. If you need to invalidate a CDN on publish, you'll write the bridge yourself: a small Cockpit addon that listens to collection.save.after and POSTs to your CDN's purge endpoint.
9. The model JSON is your real source of truth
Cockpit's UI lets you build models visually, but version-control the model JSON files. Keep them in your repo alongside the site code. If your Cockpit instance ever dies, you can rebuild the entire schema from those files. The data itself lives in SQLite — back that up separately.
10. Roles + API keys are decoupled
An API key has a role attached at creation. Changing the role of an API key in the admin UI requires deleting the key and making a new one. Don't bake API keys into a deployment until you've decided what permissions it needs.
11. Markdown is not the default — WYSIWYG is
If you want devs writing in Markdown and editors writing in WYSIWYG, you'll pick the WYSIWYG widget. The output is HTML. Embed Prism.js or similar on the rendering side if you want code blocks to render with syntax highlighting; Cockpit does not do this for you.
12. The community is small but responsive
Cockpit's GitHub issues get attention from the maintainer (Artur Heinze) directly. Documentation is patchy — read the source. This is the price of admission for a CMS that's not VC-backed: less Stack Overflow, more reading modules/Content/Controller/Api.php when something behaves unexpectedly. We've found this more reliable than dealing with paid support at VC-funded vendors.
The summary
If you need a CMS that's: free, self-hostable on cheap infrastructure, fast to edit, with first-class i18n and a real REST API — Cockpit is the strongest option in 2025. If you need real-time collaborative editing, an army of integrations out of the box, or hand-holding documentation — pick something else. The trade-off is honest. We pay it gladly.