Local Development
There are two ways to build on PocketBase Cloud, and both are supported all the way to production:
- In the portal — deploy an instance first and edit collections, API rules, and hooks in its admin panel. Nothing to install; changes are live immediately.
- On your machine — run PocketBase locally with the
pbCLI, keep your schema and hooks in Git, and deploy the directory when it’s ready. Slower to start, but you get version control, offline work, and repeatable deploys.
Most teams use both: the portal to try something, the local project as the source of truth.
Using the portal
Deploy an instance (Deploying PocketBase),
then work directly in its admin panel at
https://<your-instance>.pocketbasecloud.com/_/:
- Create collections and fields under Collections
- Set API rules from the lock icon on each collection
- Add server-side logic from the instance’s Hooks tab in the portal
- Point your app at the instance URL and start building
This is the shortest path to a working backend. Its trade-off is that the schema
lives only on that instance — export it (Settings → Export collections, or
pb collections export) if you want it in your repository.
Using the CLI
Step 1: Scaffold a local project
mkdir db && cd db
pb init
pb init downloads the PocketBase binary for your OS and CPU, then creates:
| Path | What it is |
|---|---|
pocketbase |
The server binary. Git-ignored — installed per machine. |
pb_hooks/ |
JavaScript hooks, loaded on start |
pb_migrations/ |
Schema migrations, applied automatically on start |
pb_data/ |
Database and uploads. Git-ignored. |
pb.json |
Records the pinned PocketBase version |
Existing files are never overwritten, so it’s safe to run in a directory that already has code.
Step 2: Run it
./pocketbase serve
The admin UI is at http://127.0.0.1:8090/_/ and the REST API at http://127.0.0.1:8090/api/. On the first run PocketBase prints a one-time link for creating your superuser account.
Step 3: Build your schema
Use the local admin UI, or drive the local instance with the same CLI commands you’d use against a deployed one:
pb use http://127.0.0.1:8090
pb login
pb collections create '{"name":"posts","fields":[
{"name":"title","type":"text","required":true},
{"name":"body","type":"editor"}]}'
pb rules set posts --list-rule '@request.auth.id != ""'
PocketBase writes a migration file into pb_migrations/ for every schema change
you make locally, so committing that directory keeps your schema in Git.
Step 4: Write hooks
Hook files are plain JavaScript in pb_hooks/, named *.pb.js. The instance
reloads them automatically when you save:
// pb_hooks/main.pb.js
routerAdd("GET", "/api/hello/{name}", (e) => {
return e.json(200, { message: `Hello ${e.request.pathValue("name")}!` });
});
See Extending with Hooks for the full API.
Step 5: Deploy the same directory
pb cloud login
pb cloud project create my-app
pb cloud project use my-app
pb cloud pb deploy --name my-app-db
The first deploy packages pb_public, pb_hooks, and pb_migrations and ships
them with the new instance — your local schema and hooks are applied on
creation. It also links the directory, so later redeploys are just:
pb cloud pb deploy
Note: a redeploy ships all three again.
pb_hooks/*.pb.jsgo through the hooks route, and the archive’spb_migrationsandpb_publicare installed on the running instance — migrations are merged with the ones already there (nothing is deleted) andpb_publicis replaced wholesale. New migrations run on the restart that follows. Schema changes made in the instance’s admin panel or throughpb collectionskeep working alongside this.
Pinning the PocketBase version
pb init records the version it installed in pb.json, and deploys use that
pin — so your cloud instance runs the same build you developed against.
pb versions # list available releases
pb install 0.39.9 # switch this directory's binary and update the pin
pb which # show the installed binary and its pin
Override it for one deploy with pb cloud pb deploy --pb-version 0.39.9. With
no pin and no flag, deploys use the newest published release.
A fresh clone has no binary (it’s git-ignored) — pb init in the clone
installs the pinned version.
Frontends and backends
The same pattern applies to the rest of your stack: one directory per resource, each linked once, all in the same project.
cd ../web && npm run dev # develop however you normally do
pb cloud frontend deploy --name my-app-web
cd ../api && npm run dev
pb cloud backend deploy --name my-app-api # Pro plan only
deploy infers how to build and package each directory (build command, output
directory, backend runtime) and records the guess in that directory’s
pb.json. Run pb cloud init to produce that block on its own and review it
before anything ships:
cd web
pb cloud init frontend # writes the build block, touches nothing in the cloud
Edit the block whenever the guess is wrong — it is never overwritten.
Environment variables
A .env beside pb.json is pushed to the PocketBase instance or backend it
belongs to on every deploy. Keys in the file are written; keys that exist only
in the cloud are left alone. Values are never printed, and the file itself never
goes into the ZIP.
pb cloud backend deploy # pushes .env
pb cloud backend deploy --skip-env # leaves cloud env vars alone
pb cloud backend deploy --env-file .env.prod # pushes a different file
Frontends have no cloud env store — their variables are baked in at build time.