{"docs":[{"id":2,"title":"Setting up Payload CMS: more hassle than Sanity, but a nice place to land","slug":"setting-up-payload-cms","publishedAt":"2026-09-18T10:00:00.000Z","excerpt":"Notes from building a Payload CMS twin of the Sanity test blog. It took noticeably more work to get running, mostly on the hosting side, but the result is a self-contained setup that is easy to reason about.","mainImage":null,"body":{"root":{"type":"root","children":[{"type":"paragraph","version":1,"format":"","indent":0,"direction":"ltr","textFormat":0,"children":[{"type":"text","text":"This blog is a twin. The same theme, the same content model and the same kind of static site as the Sanity test blog, but with ","format":0,"detail":0,"mode":"normal","style":"","version":1},{"type":"text","text":"Payload CMS","format":1,"detail":0,"mode":"normal","style":"","version":1},{"type":"text","text":" behind it. The point was to compare the two honestly, from the perspective of someone who has spent years with PHP content systems. Here is what the Payload side taught us.","format":0,"detail":0,"mode":"normal","style":"","version":1}]},{"type":"heading","version":1,"tag":"h2","format":"","indent":0,"direction":"ltr","children":[{"type":"text","text":"It is one app, and that changes everything","format":0,"detail":0,"mode":"normal","style":"","version":1}]},{"type":"paragraph","version":1,"format":"","indent":0,"direction":"ltr","textFormat":0,"children":[{"type":"text","text":"Sanity is a hosted content store with a separate editing app. You never run a server. Payload is the opposite: the admin panel, the API, the database access and the file uploads all live in one Node process that you host yourself. If you come from a PHP CMS this feels familiar, almost comforting. Your content is a SQLite file and a folder of images on a machine you control, and a backup is a copy.","format":0,"detail":0,"mode":"normal","style":"","version":1}]},{"type":"paragraph","version":1,"format":"","indent":0,"direction":"ltr","textFormat":0,"children":[{"type":"text","text":"The flip side is that every operational chore is yours too: the process manager, the memory, the migrations, the SSL, the restarts. With Sanity none of that existed.","format":0,"detail":0,"mode":"normal","style":"","version":1}]},{"type":"heading","version":1,"tag":"h2","format":"","indent":0,"direction":"ltr","children":[{"type":"text","text":"Where the hassle was","format":0,"detail":0,"mode":"normal","style":"","version":1}]},{"type":"paragraph","version":1,"format":"","indent":0,"direction":"ltr","textFormat":0,"children":[{"type":"text","text":"Defining the content model and wiring up the site was quick and pleasant. Almost all of the friction was in getting the thing to run on a small production server.","format":0,"detail":0,"mode":"normal","style":"","version":1}]},{"type":"list","version":1,"listType":"bullet","tag":"ul","start":1,"format":"","indent":0,"direction":"ltr","children":[{"type":"listitem","value":1,"format":"","indent":0,"version":1,"direction":"ltr","children":[{"type":"text","text":"Memory. ","format":1,"detail":0,"mode":"normal","style":"","version":1},{"type":"text","text":"The default production build of the admin panel peaked at around 2.5 GB of RAM and was silently killed on a 2 GB server. Switching bundlers and capping the Node heap brought it down to about 1.4 GB. The running CMS itself is modest, roughly 350 MB, but the build is a real cost on shared hosting.","format":0,"detail":0,"mode":"normal","style":"","version":1}]},{"type":"listitem","value":2,"format":"","indent":0,"version":1,"direction":"ltr","children":[{"type":"text","text":"Folder layout. ","format":1,"detail":0,"mode":"normal","style":"","version":1},{"type":"text","text":"Hosting tools assume the app lives at the root of the repository. Ours lived in a subfolder next to the site, so the generated start command pointed at the wrong place and the process crash-looped until we passed the folder explicitly.","format":0,"detail":0,"mode":"normal","style":"","version":1}]},{"type":"listitem","value":3,"format":"","indent":0,"version":1,"direction":"ltr","children":[{"type":"text","text":"Zero-downtime deploys and state. ","format":1,"detail":0,"mode":"normal","style":"","version":1},{"type":"text","text":"Each deploy is a fresh clone, so the database file and the uploads have to live outside the release folder and be linked in. The first deploy failed simply because that shared folder did not exist yet.","format":0,"detail":0,"mode":"normal","style":"","version":1}]},{"type":"listitem","value":4,"format":"","indent":0,"version":1,"direction":"ltr","children":[{"type":"text","text":"Migrations. ","format":1,"detail":0,"mode":"normal","style":"","version":1},{"type":"text","text":"In development the schema syncs itself. In production it does not, so every change to a collection means generating a migration file, committing it, and running it on deploy. Sensible, but one more thing to remember.","format":0,"detail":0,"mode":"normal","style":"","version":1}]},{"type":"listitem","value":5,"format":"","indent":0,"version":1,"direction":"ltr","children":[{"type":"text","text":"Process manager details. ","format":1,"detail":0,"mode":"normal","style":"","version":1},{"type":"text","text":"The hosting default started several clustered instances. For a CMS with a single SQLite file, one process in plain fork mode is the right answer, and it took a manual fix to get there.","format":0,"detail":0,"mode":"normal","style":"","version":1}]}]},{"type":"heading","version":1,"tag":"h2","format":"","indent":0,"direction":"ltr","children":[{"type":"text","text":"What was nice","format":0,"detail":0,"mode":"normal","style":"","version":1}]},{"type":"list","version":1,"listType":"bullet","tag":"ul","start":1,"format":"","indent":0,"direction":"ltr","children":[{"type":"listitem","value":1,"format":"","indent":0,"version":1,"direction":"ltr","children":[{"type":"text","text":"The content model is plain code. Collections, fields, access rules and hooks sit in a few TypeScript files, and the types for the whole API are generated from them.","format":0,"detail":0,"mode":"normal","style":"","version":1}]},{"type":"listitem","value":2,"format":"","indent":0,"version":1,"direction":"ltr","children":[{"type":"text","text":"Drafts and versions come built in, with a clean rule that anonymous requests only ever see published documents.","format":0,"detail":0,"mode":"normal","style":"","version":1}]},{"type":"listitem","value":3,"format":"","indent":0,"version":1,"direction":"ltr","children":[{"type":"text","text":"Images are resized on upload and served with their dimensions, so the static site can write proper ","format":0,"detail":0,"mode":"normal","style":"","version":1},{"type":"text","text":"width","format":16,"detail":0,"mode":"normal","style":"","version":1},{"type":"text","text":" and ","format":0,"detail":0,"mode":"normal","style":"","version":1},{"type":"text","text":"height","format":16,"detail":0,"mode":"normal","style":"","version":1},{"type":"text","text":" attributes without a hosted image service.","format":0,"detail":0,"mode":"normal","style":"","version":1}]},{"type":"listitem","value":4,"format":"","indent":0,"version":1,"direction":"ltr","children":[{"type":"text","text":"The public site needed no framework at all. A short script fetches published posts from the REST API and writes HTML files. Publishing a post calls the host's deploy hook and the site rebuilds itself.","format":0,"detail":0,"mode":"normal","style":"","version":1}]},{"type":"listitem","value":5,"format":"","indent":0,"version":1,"direction":"ltr","children":[{"type":"text","text":"An official MCP plugin exposes the collections to AI tools with the normal access rules. This post was drafted that way and published by a human.","format":0,"detail":0,"mode":"normal","style":"","version":1}]}]},{"type":"heading","version":1,"tag":"h2","format":"","indent":0,"direction":"ltr","children":[{"type":"text","text":"Verdict so far","format":0,"detail":0,"mode":"normal","style":"","version":1}]},{"type":"paragraph","version":1,"format":"","indent":0,"direction":"ltr","textFormat":0,"children":[{"type":"text","text":"Sanity got us to a working blog faster, because the hard part was somebody else's server. Payload took an afternoon of hosting detours, most of them about memory and folder conventions rather than the CMS itself. Once up, though, it is the setup that is easier to explain: one app, one database file, one folder of images, and a plain static site in front. If you already run your own servers and like knowing where everything lives, that trade is a good one.","format":0,"detail":0,"mode":"normal","style":"","version":1}]},{"type":"quote","version":1,"format":"","indent":0,"direction":"ltr","children":[{"type":"text","text":"Boring choices, few dependencies, and a server you can read the logs on.","format":0,"detail":0,"mode":"normal","style":"","version":1}]}],"direction":"ltr","format":"","indent":0,"version":1}},"seo":{"metaTitle":"Setting up Payload CMS: learnings","metaDescription":"What it took to get a Payload CMS blog running next to its Sanity twin: the wins, the hosting surprises, and what a PHP developer should expect.","ogImage":null},"updatedAt":"2026-09-18T07:37:09.509Z","createdAt":"2026-09-18T07:36:13.333Z","_status":"published"},{"id":1,"title":"Dis is test","slug":"dis-is-test","publishedAt":"2026-09-18T07:28:13.174Z","excerpt":"dis is excerpt","mainImage":null,"body":{"root":{"children":[{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"This post lives in ","type":"text","version":1},{"detail":0,"format":1,"mode":"normal","style":"","text":"Payload CMS","type":"text","version":1},{"detail":0,"format":0,"mode":"normal","style":"","text":", stored as ","type":"text","version":1},{"detail":0,"format":2,"mode":"normal","style":"","text":"Lexical","type":"text","version":1},{"detail":0,"format":0,"mode":"normal","style":"","text":" JSON in a SQLite file, and rendered to HTML by a small Node script. See the ","type":"text","version":1},{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"Sanity twin","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"link","version":3,"fields":{"url":"https://sanity.eddiedale.com","newTab":true,"linkType":"custom"}},{"detail":0,"format":0,"mode":"normal","style":"","text":" for comparison.","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""},{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"What is different","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"heading","version":1,"tag":"h2"},{"children":[{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"One app: admin, API and database access together, like a PHP CMS.","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"listitem","version":1,"value":1},{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"Uploads are files on disk, resized by ","type":"text","version":1},{"detail":0,"format":16,"mode":"normal","style":"","text":"sharp","type":"text","version":1},{"detail":0,"format":0,"mode":"normal","style":"","text":".","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"listitem","version":1,"value":2}],"direction":"ltr","format":"","indent":0,"type":"list","version":1,"listType":"bullet","start":1,"tag":"ul"},{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"Boring choices, few dependencies.","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"quote","version":1},{"type":"block","version":2,"format":"","fields":{"id":"6aace870e79fbb2b85c55b2e","code":"node site/build.mjs\n# Built 1 post into site/dist","language":"bash","blockType":"code"}},{"type":"upload","version":3,"format":"","id":"6aace870e79fbb2b85c55b2f","fields":null,"relationTo":"media","value":null},{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"That image above is an upload node inside the rich text.","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":null,"format":"","indent":0,"type":"root","version":1}},"seo":{"metaTitle":null,"metaDescription":null,"ogImage":null},"updatedAt":"2026-09-18T07:29:59.795Z","createdAt":"2026-09-18T07:28:18.776Z","_status":"published"}],"hasNextPage":false,"hasPrevPage":false,"limit":10,"nextPage":null,"page":1,"pagingCounter":1,"prevPage":null,"totalDocs":2,"totalPages":1}