Deploying a Backend
Backends let you run arbitrary server-side code next to your PocketBase instance — REST APIs, webhook handlers, background workers, or a full Next.js app. Each backend runs in its own container with HTTPS and a dedicated subdomain.
Note: backend deployments require the Pro plan.
Supported runtimes
- Node.js — the standard JavaScript runtime
- Deno — TypeScript out of the box
- Bun — fast all-in-one runtime
- Next.js — deploy a full Next.js application with server-side rendering
Step 1: Prepare your app
Your app must listen on the port provided in the PORT environment
variable — PocketBase Cloud injects it automatically:
// Node.js / Bun
const port = process.env.PORT || 3000;
app.listen(port);
// Deno
Deno.serve({ port: Number(Deno.env.get("PORT")) || 3000 }, handler);
Step 2: Deploy
Using the portal
- Open your project and go to the Backends tab
- Click New Backend
- Enter a name, choose a runtime, and set the start command
(e.g.,
node index.js,deno run -A main.ts, orbun run index.ts) - Click Create
- Zip your project directory (source files,
package.json, lockfile —node_modulesis not needed; dependencies install during deployment) - On the backend’s Deploy page, upload the ZIP and click Deploy
Re-deploying is the same flow — upload a new ZIP and the container is replaced with the new version.
Using the CLI
cd api
pb cloud backend deploy --name my-app-api
One command builds, packages, uploads, creates the backend if needed, pushes a
neighbouring .env, and waits for the container to reach running.
pb cloud deploy --name my-app-api does the same: a deno.json, a server
dependency such as express or hono, a start script, or a next.config.*
without output: "export" all identify a backend. See
One deploy command for all three.
The first deploy records the backend in api/pb.json, so afterwards:
pb cloud backend deploy # redeploy the linked backend
The runtime, build command, and start command are inferred from the directory
(deno.json, bun.lockb, next.config.*, package.json) and written into
pb.json. Review the guess before shipping with pb cloud init backend:
// api/pb.json
{
"projectId": "dhs4xnprgplurvo",
"kind": "backends",
"defaultEnvironment": "production",
"environments": {
"production": { "id": "…", "name": "my-app-api" }
},
"build": {
"runtime": "nodejs",
"startCommand": "node index.js"
}
}
Useful flags:
| Flag | What it does |
|---|---|
--name <name> |
Which backend to deploy. Asked for when omitted and nothing is linked. |
--runtime <deno|bun|nodejs|nextjs> |
Overrides build.runtime |
--start <cmd> |
The command that boots the container |
--compute <id> |
Compute to deploy onto. Asked for when the project owner has more than one — list them with pb cloud compute ls. (Formerly --server, still accepted.) |
--skip-build |
Package without running the build command |
--skip-env |
Don’t push the neighbouring .env |
--zip <file> |
Upload an archive you built yourself |
--env <name> |
Which pb.json environment to target |
What gets packaged
deno, bun, and nodejs ship their source — the platform installs
dependencies on start, so node_modules is excluded from the archive.
nextjs is different: the platform does not run next build (it exhausts
memory on a shared host), so you ship a prebuilt bundle. That bundle exists only
when the build asks for it, so the CLI adds output: "standalone" to
next.config.* for you — before the build runs, printing the change — and
writes a next.config.js if the project has none. A config that already sets
output is left as it is. The CLI then assembles .next/standalone,
.next/static, and public into the layout the runtime expects, starting it
with node server.js:
// api/pb.json
{
"build": { "command": "npm run build", "runtime": "nextjs" }
}
A config setting output: "export" is refused rather than rewritten — that is a
static site, not a server, so deploy it as a
frontend instead.
Uploading a Next.js ZIP by hand in the portal requires assembling that same layout yourself, which is the main reason to prefer the CLI for Next.js.
Your backend is live
Once the status turns running, your backend is live at:
https://<backend-name>.pocketbasecloud.com
Watch for that status on the Backends tab in the portal, or from the terminal:
pb cloud backend ls
pb cloud backend info --name my-app-api
Step 3: Connect to your PocketBase instance
Backends talk to PocketBase over its public URL like any other client, but with server-side credentials kept in environment variables:
import PocketBase from "pocketbase";
const pb = new PocketBase(process.env.POCKETBASE_URL);
await pb
.collection("_superusers")
.authWithPassword(process.env.PB_ADMIN_EMAIL, process.env.PB_ADMIN_PASSWORD);
Viewing logs
Using the portal
The backend detail page streams your container’s stdout/stderr in real time.
Using the CLI
pb cloud logs backend --name my-app-api # last 50 lines
pb cloud logs backend --name my-app-api --lines 500
pb cloud logs backend --name my-app-api -f # follow
Either way, the most common cause of a crash loop is listening on a hard-coded
port instead of process.env.PORT.
Deleting a backend
Using the portal
Open the backend, go to its Delete page, and confirm.
Using the CLI
pb cloud backend rm --name my-app-api # asks for confirmation
pb cloud backend rm --name my-app-api --yes # scripted