# Primo CMS: a self-hosted visual CMS where the database and the files stay in sync

> Primo is an MIT-licensed, self-hosted CMS for developers who hand sites to clients. It ships as one Go binary with PocketBase for storage and Svelte 5 for the editor, and its pull/edit/push CLI is still labelled beta.

**primocms/primo** — Agent-native visual CMS. Build sites with Claude/Codex/whatever, manage them visually. 

- Repository: https://github.com/primocms/primo
- Website: https://primo.build
- Stars: 2,372 · Forks: 571
- Language: Svelte
- License: MIT
- Published: 2026-09-28 · Updated: 2026-09-28 · Language: en
- Canonical page: https://hysenlabs.com/projects/primocms-primo

## The handoff problem Primo is built around

A freelancer finishes a site, hands over the login, and then spends the next two years fielding "I broke the homepage" messages. That is the problem Primo names in its own README, and the audience it names with it: freelancers, small agencies, and small teams who code their clients' sites and need the clients to manage content afterward.

The design answer is a split of authority. The client gets a visual editor with guardrails the developer defines. The developer keeps a folder of structured files that a code editor, Git, or an AI agent can read and write. The README frames the whole product as two synchronized representations of every site: a relational database that powers the visual editor and multi-site serving, and a folder of structured files that powers code editors, version control, and agents. Edit either side, and the README says both stay in sync.

That is a narrower promise than a general-purpose CMS makes. Primo is not trying to be the place where you also build the site. It assumes you already write Svelte components, and it takes over what happens after the build.

## One Go binary, PocketBase underneath, Svelte 5 on top

The architecture is unusually flat, and the repository confirms it. The Dockerfile has three stages: a node-builder that runs npm install and builds the frontend with `npx vite --config common.config.js build` and `npx vite --config app.config.js build`, a go-builder that compiles the binary with the version stamped in via ldflags, and an alpine runtime that copies in the executable and starts it with `./primo serve --http=0.0.0.0:8080`.

Go dependencies in go.mod line up with that description. `github.com/pocketbase/pocketbase` is a direct requirement, as are `github.com/dop251/goja` for embedded JavaScript and `github.com/gorilla/websocket` for live updates. PocketBase brings SQLite storage, auth, and an admin layer with it. There is no separate API service to deploy and no external database to provision.

The README states the same server hosts the CMS and serves your published sites, with no external services and no separate frontend to wire up. Static output is a first-class feature: the README describes it as clean HTML that you can deploy to any static host, or let Primo serve it. The trade-off is that you are adopting PocketBase's operational model whether or not you wanted it, including its SQLite file as the single source of truth for the database side.

## Installing Primo with Docker and creating a first site

The README gives two paths. The easiest, in its words, is the Railway one-click deploy button. The other is Docker, and it is short enough to reproduce exactly. The image is published to the GitHub Container Registry, and the data directory is a named volume, so the container can be replaced without losing the SQLite file.

```bash
docker run -d -p 8080:8080 -v primo-data:/app/pb_data ghcr.io/primocms/primo:latest
```

After that, the README says to open your server's URL to create your first site. On a local machine that URL is the host on port 8080. If you are deploying anywhere long-lived, note the README's own warning about image tags: point the service at `ghcr.io/primocms/primo:latest` so auto-update tracks releases, and if you deployed earlier with `:main`, switch it, because `:main` is a rolling branch build rather than a release and will not advance on version releases. That is a real trap for anyone who copied a deployment config before the note existed.

The Dockerfile sets four environment variables that matter for a self-hosted install: `PRIMO_SUPERUSER_EMAIL`, `PRIMO_SUPERUSER_PASSWORD`, `PRIMO_USER_EMAIL`, and `PRIMO_USER_PASSWORD`. They are declared empty in the image, so a first run without them will not have a provisioned superuser. The README does not document what each one does, which is the kind of gap you discover at deploy time rather than read time.

## The pull, edit, push loop and why it is the risky part

The workflow the README advertises is three commands. `primo pull yourserver.com` pulls the site down as local files. You then open `claude` or Cursor or any agent and edit. `primo push` sends changes back, and the README says the CMS updates live while clients keep editing content in the browser.

```bash
primo pull yourserver.com       # pull your site down as local files
claude                          # edit with Claude Code, Cursor, or any agent
primo push                      # push changes back — CMS updates live
```

This is the feature that distinguishes Primo from every hosted headless CMS in its comparison list, and it is also the one the project itself flags. The README's project status section puts the visual CMS, block library, static output, multi-site, and self-hosted deployment under Stable, and puts the CLI plus local-file workflow under Beta, alongside AI-agent compatibility.

Two consequences follow. First, treat the file side as the less proven half of the product, even though it is the half the marketing sentence leads with. Second, concurrent writes are the obvious failure mode of a two-representation system: the README says clients keep editing in the browser the whole time, but it does not document conflict resolution, merge behaviour, or rollback for a push that lands on content someone else changed. If you adopt Primo for a client site, that is the question to answer before you let an agent run `primo push` unattended.

## Where Primo is the wrong choice

Primo's own comparison section sets the boundaries. Against WordPress it argues the same mental model with modern tooling: Svelte instead of PHP, static output instead of server rendering, one binary instead of themes, plugins, and hosting. That is a fair description of the difference, and it is also the reason a team with a PHP shop, an existing plugin dependency, or editors trained on the WordPress admin should not switch. You would be trading a large ecosystem for a single binary.

Against Sanity and Storyblok, the README calls Primo monolithic and lists the absences: no separate frontend to wire up, no API tokens, no deployment glue. Those absences are the pitch, but they cut the other way too. If your site is a React or Next.js application that needs to fetch content at runtime, or if you need a content API that other services call, a headless CMS is the correct shape and Primo is not. Primo's output is static HTML and its content lives in a database you host.

The beta label on the CLI is the third boundary. Anyone whose release process depends on scripted content deployment should assume the file workflow can change between releases, and should read the release notes for v3.2.6 through v3.2.8 before building on it.

## Maintenance, licensing, and what a self-hosted install costs you

The repository is not archived, and the last push was on 2026-09-28. Releases are frequent: v3.2.6 on 2026-08-20, v3.2.7 on 2026-09-07, and v3.2.8 on 2026-09-23. The README also claims active development since 2019, which the release cadence is consistent with.

Upgrade cost is mostly the image tag. Because the runtime is a single container and the state is the `pb_data` volume, an upgrade is a pull of `ghcr.io/primocms/primo:latest` and a restart, and the README's note about `:main` versus `:latest` is the one configuration detail that will silently strand you on an old build. The migrations directory in the repository root indicates the database schema is versioned, but the README does not document rollback, so a downgrade after a schema migration is an open question rather than a supported path.

Licensing is MIT, which is permissive and places few conditions on commercial use or modification. That covers the Primo code itself. It does not settle the PocketBase dependency, your client contracts, or what happens to content ownership when a client leaves, so treat the MIT badge as one input rather than the whole answer. Nothing here is legal advice.

## Conclusion

Adopt Primo if you code client sites in Svelte and want the client to edit content in a browser while you keep working from files. Do not adopt it if you need a stable, fully documented CLI workflow today, or if your stack is PHP or a hosted headless CMS. Before committing, verify two things yourself: that the beta pull/push round trip survives your own Git workflow, and that your backup and restore procedure covers the pb_data volume, because the README documents neither.

## FAQ

### What is Primo CMS?

Primo is a self-hosted CMS for developers who build sites for clients who need to manage them afterward. Each site exists as both a relational database and a folder of structured files, so a visual editor and a code editor can both work on it.

### How do I install Primo?

The README offers a Railway one-click deploy button, or a single Docker command that publishes port 8080 and mounts a named volume at /app/pb_data. After starting the container, you open the server URL to create your first site.

### Which image tag should I use for a Primo deployment?

The README says to point the service at ghcr.io/primocms/primo:latest so auto-update tracks releases. It warns that :main is a rolling branch build rather than a release, so it will not advance when a version is released.

### Is Primo's CLI stable?

No. The README's project status lists the CLI and local-file workflow, including primo pull and primo push, as beta, while the visual CMS, block library, static output, multi-site, and self-hosted deployment are listed as stable.

### What licence does Primo use?

Primo is MIT licensed, according to the repository's LICENSE file and the badge at the top of the README.

## Sources

- [License: MIT](https://github.com/primocms/primo/blob/main/LICENSE)
- [primocms/primo on GitHub](https://github.com/primocms/primo)
- [Project website](https://primo.build)
- [README](https://github.com/primocms/primo/blob/main/README.md)
- [Releases](https://github.com/primocms/primo/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/primocms-primo
