Skip to content
DocsCI/CDDeploying from GitHub Actions

Deploying from GitHub Actions

Make every push to main deploy your app. About five minutes to set up, one command.

The idea: deploy once from your computer, then let GitHub repeat it. That first deploy answers all the questions — which project, what to call it, how to build it — and writes the answers into a pbc.json file. pbc ci init then writes the workflow that repeats it, using the official pocketbasecloud/cli/action.


Step 1 — Deploy once from your computer

In the folder you want to deploy:

pbc login
pbc frontend deploy

Use pbc pocketbase deploy for a PocketBase instance, or pbc backend deploy for a backend. If you have no project yet, run pbc project create my-app first.

It will ask you a few things — which project, what to name this. Answer them; this is the only time anyone has to.

If it asks about an env file, choose “Don’t push env vars.” Your .env is almost certainly not committed to git, so GitHub would not find it and the deploy would fail. There is a better place for secrets — see Step 6.

When it finishes, commit the file it wrote:

git add pbc.json
git commit -m "Link this folder to PocketBase Cloud"

That file is what makes the GitHub side need no options at all.

Step 2 — Write the workflow file

pbc ci init

This writes .github/workflows/deploy.yml (or deploy-<path>.yml if this folder is not the repo root — nested folders become deploy-apps-web.yml), wired to the official action and to the branch you’re currently on. Pass --branch to pin a different one. It infers everything else from pbc.json — nothing to fill in.

It ends by printing the two steps still left, both one-time: where to copy your access token from, and the name of the repository secret to save it as. Do both now — Steps 3 and 4 below are the same two steps, spelled out.

Edit the generated file freely — running pbc ci init again will not touch it. It prints the path and stops; pass --force when you actually want it regenerated.

Step 3 — Copy your access token

GitHub has no browser to log in with, so it needs a token instead.

In the portal, go to AccountCLI access tokenCopy.

Step 4 — Save the token in GitHub

In your repository: Settings → Secrets and variables → Actions → New repository secret.

  • Name: PBC_TOKEN
  • Secret: paste the token

The name has to be exactly PBC_TOKEN.

Step 5 — Push

git add .github/workflows/deploy.yml pbc.json
git commit -m "Deploy on push"
git push

Watch it run under the Actions tab. Build output (npm, and so on) is in the job log; the CLI’s own upload and provision steps stay quiet under --json, so they do not mix with the deploy record. The URL lands in the job summary when it finishes. If the deploy fails, the job fails; you do not have to check anything yourself.

That’s it. Every push to that branch now deploys.

Step 6 — If your app needs secrets

PocketBase instances and backends can hold environment variables. Set them once from your computer and GitHub will leave them alone:

pbc env set 'STRIPE_KEY=sk_live_…' --target backend --name my-api

They live on the platform, encrypted, and survive every redeploy. Quote the whole KEY=VALUE so your shell does not split it.

Frontends are different: their variables are baked in when the site is built, so they belong in the build step, not on the platform.

      - name: Build
        working-directory: web
        env:
          VITE_API_URL: https://my-api.pocketbasecloud.com
        run: |
          npm ci
          npm run build

      - uses: pocketbasecloud/cli/[email protected]
        with:
          token: ${{ secrets.PBC_TOKEN }}
          working-directory: web
          kind: frontend
          skip-build: "true"

skip-build: "true" tells the action to upload what you just built instead of building again.


What the generated workflow actually is

pbc ci init writes something close to this — read it once, it is short:

name: Deploy

on:
  push:
    branches: [main]
  workflow_dispatch:

concurrency:
  group: deploy-production
  cancel-in-progress: false

permissions:
  contents: read

jobs:
  deploy:
    runs-on: ubuntu-latest
    timeout-minutes: 20
    steps:
      - uses: actions/checkout@v4

      - uses: pocketbasecloud/cli/[email protected]
        with:
          token: ${{ secrets.PBC_TOKEN }}
          kind: frontend

concurrency and timeout-minutes matter for reasons that are easy to overlook and expensive to hit: without the first, two pushes in quick succession can deploy the same resource at the same time; without the second, GitHub’s default lets a wedged deploy run for six hours rather than failing fast. Both are set for you.

If something goes wrong

Message What it means Fix
No token. Set 'token' to a repository secret… The action’s token input was empty Check the secret is named exactly PBC_TOKEN and that with: token: ${{ secrets.PBC_TOKEN }} is on the action step. Forked pull requests get no secrets at all — see below.
Not authenticated. Run 'pbc login'. The token is expired or wrong Copy a new one (Step 3) and update the secret.
No project selected. Pass --project… No pbc.json in the checkout, or the job is running from the wrong directory Commit pbc.json (Step 1) and check working-directory.
Pass --name to create the first frontend… The directory is not bound to a resource Deploy once from your computer (Step 1) so pbc.json records the name, or add name: to the action.
No pbc.json here. Local pbc ci init found nothing to write a workflow for Run Step 1 in this folder first. This sentence is from ci init, not from a failed job.
Env file not found: .env pbc.json names a .env that is not in git Delete the "envFile" line from pbc.json, and use Step 6 instead. pbc ci init warns about this at generation time if it can.
Build failed (npm run build exited 1) Your build broke, not the deploy Run the same command locally; it will fail there too.
Backend deployments require a Pro plan Plan limit Backends need Pro. Free and Starter get 1 PocketBase and 5 frontends.
This project has 2 computes — pass --compute Pro account with more than one compute Run pbc compute ls and add compute: <id> to the action’s with: block.

Nothing at all happened? GitHub only runs a workflow that is on the branch you pushed to, and only for the branches listed under on:. pbc ci init writes .github/workflows/deploy.yml at the repo root, or .github/workflows/deploy-<path>.yml from a subdirectory (nested folders become deploy-apps-web.yml). A subdirectory workflow also has a paths: filter, so it only runs when the push includes that directory or the workflow file itself. If you ran ci init on a feature branch, the file only lists that branch — a push to main will not run it. Pass --branch main if you wanted main.

Forked pull requests get no secrets. PBC_TOKEN arrives empty and the action stops before touching anything. Deploy from push on your own branches, or gate the job:

if: github.event.pull_request.head.repo.full_name == github.repository

Two things not to do

  • Don’t paste the token into the workflow file. It belongs in Secrets. A workflow file is in your repository, and anyone who can read the repository can read it.
  • Don’t call pbc ... --json yourself and print the result. The official action already handles this for you — it masks a PocketBase deploy’s admin credentials before it reads anything else, and only ever publishes the six fields (url, id, name, status, kind, environment) as step outputs. A hand-rolled pbc pocketbase deploy --json step has none of that: adminUsername/adminPassword are fields on the record, GitHub only masks values it has been told are secret, and a generated password is not one it can guess. If you need the deploy’s URL in a later step, read it from steps.<id>.outputs.url instead.

Going further

Staging and production from the same folder, deploying several things in order, preview deploys on pull requests, exit codes, and every error message with its cause: the CI/CD reference.