primo dev runs the full Primo CMS on your machine and keeps it in sync with your local files. You write blocks and content in your editor; the CMS picks up the changes. With --author cms, edits you make in the CMS are written back to disk instead (see Author Modes).
Starting the Dev Server
From a workspace root (the directory containingserver.yaml):
primo dev runs until you stop it (Ctrl+C). It prints the URLs for each site:
http://127.0.0.1:3000/admin/dashboard— the dashboardhttp://my-site.localhost:3000/admin/site— the CMS editor for a sitehttp://my-site.localhost:3000/— the site’s preview
primo dev tells you to cd into it rather than creating a new one.
Hosted server changes
If your workspace has a hosted server configured,primo dev checks its locally tracked sites and shared library against your last pull or push before loading the local CMS. If server data changed or was deleted, you see a warning naming the affected site or library.
The check uses your saved login and waits up to five seconds across all requests. Missing sync history, an unreachable server, an expired login, or an older server produces a notice that freshness could not be verified. Local development still starts. Workspaces without a configured server skip the check.
This check does not pull data or change your saved sync history. To update an outdated workspace, save your local work, stop primo dev, and pull from the hosted server before editing. A pull replaces local files, so preserve your changes first. primo push checks the server again when you upload.
Ports
primo dev uses two ports: the CMS port (3000 by default, or port: in server.yaml) and the next one up for live reload. If either is taken, it picks the next free pair and tells you:
my-site.localhost:3000 are updated to the port in use, so you can run several workspaces side by side.
If you chose the port yourself (with -p or port: in server.yaml), primo dev asks before switching to another one. Without a terminal to ask in (scripts, CI, agents), it stops with an error instead, so pass a free port with -p.
Leftover CMS processes
If a previousprimo dev was killed without shutting down its CMS, the next start tells you which process is in the way:
Options
Author Modes
--author controls which side wins when files and the CMS disagree. This is the most important concept in local dev — pick a mode that matches how you’re working.
- files (default)
- cms
- both (beta)
Files are source of truth.
- Edits to local files are imported into the CMS on save.
- The CMS UI is read-only for content that’s tracked in files — you’ll see a banner.
- Use this mode when you’re building blocks or page types in your editor.
Site Layout
Each site folder follows this layout:Uploads
Files dropped intosites/my-site/uploads/ are automatically imported into the CMS as site_uploads records:
- On import, new files become uploads with stable IDs.
- Hash comparison skips re-upload when content is unchanged.
- A file removed from
uploads/becomes an orphan upload warning — clean it up in the CMS or remove the record from the file system to delete it.
uploads/ and reference it from an image field without ever opening the CMS.
site.yaml
site.yaml holds the site’s name, its site_id, and its group:
site_id ties the folder to its record in the CMS. Primo writes it for you, so don’t edit or copy it by hand.
The site’s hostname is not stored in files. It’s per-environment (a *.localhost host in dev, your domain in production) and is set in the dashboard, so primo push can’t point your production site at localhost.
Adding Sites
primo new <name> creates a site folder under sites/, and primo dev imports it into the CMS. In an interactive terminal, primo new starts the CMS for you; with --skip-dev, or from a script, it only creates the files:
sites/ isn’t in the CMS until you register it with primo add:
primo add needs the workspace’s port, so stop primo dev first, then start it again afterward. If primo dev is running, primo add stops and tells you so rather than racing it.
primo status lists any folders on disk that aren’t registered yet:
Shared Library
The workspace’slibrary/ folder holds blocks shared across sites. library/groups.yaml lists the library’s groups and their folders:
library/<group-folder>/<block>/, with the same files as a site block. primo dev loads the library into the CMS, where editors add library blocks to a site from the block picker. To use a library block on a site in files, copy its folder into sites/<name>/blocks/ (leaving out its _id). The copy is independent afterwards.
Working with AI Agents
primo init, primo new, and primo pull write an AGENTS.md at the workspace root: a guide to the workspace layout, commands, content-design defaults, and valid field types. Most coding agents read it automatically. They also write a CLAUDE.md that imports it (@AGENTS.md) for Claude Code.
For validation and scaffolding tools, connect the Primo MCP server. primo dev also registers it in the workspace’s .mcp.json.
Site Groups
Groups organize sites in the dashboard (Home Services, Food & Hospitality, and so on). Declare them in the workspace’sserver.yaml, giving each a local id and a display name:
site.yaml by that local id:
Avatar Uploads
iPhone HEIC photos are automatically converted to JPEG when uploaded as avatars — no manual conversion required.Validating Your Work
Runprimo validate from the workspace root to check every site, or from a site folder to check just that site:
pages/index.yaml), or fields: under a repeater or group field, where nested fields must be listed under subfields:.
To rebuild a site’s preview while primo dev is running, pass the site folder:
validate_block, validate_page, validate_site) that agents call automatically.
Next Steps
Sync with a Hosted Server
Pull and push changes against a deployed Primo
Deploy
Ship your workspace to production