PocketBun Differences From PocketBase

This page tracks user-relevant differences between PocketBase and PocketBun.

Notation used in examples:

Quick links:

PocketBase To PocketBun Migration Checklist

Use this as a quick migration recipe for an existing PocketBase project.

  1. Switch executable and update flow.
    • Replace pocketbase commands with pocketbun.
    • There is no PocketBase-style binary self-update command; update via package manager.
  2. Create (or convert to) a Bun project.
    • Option A: convert your existing project directory:
      • initialize package metadata: bun init
      • add PocketBun dependency: bun add pocketbun
    • Option B: scaffold a new PocketBun project and copy your existing data/code:
      • create project: bun create pocketbun my-app
      • copy your PocketBase pb_* directories into the new project (pb_data, pb_hooks, pb_migrations, optional pb_public)
  3. Keep the project layout, but verify startup working directory.
    • Keep pb_data, pb_hooks, pb_migrations, and optional pb_public.
    • PocketBun resolves default paths from current working directory (CWD), so start from your project root or pass explicit dirs.
  4. Move hooks as-is, then fix hook-chain calls.
    • In handlers that call e.next(), return/await it:
      • sync: return e.next()
      • async: const err = await e.next(); if (err) return err; ...
    • This is especially important for onBootstrap when using async startup paths.
    • If you see OnBootstrap hook didn't fail but the app is still not bootstrapped, this is usually the cause.
    • In .pb.ts hooks, use standard import for neighboring files and dependencies when needed.
  5. Keep API clients and route assumptions.
    • Existing client SDK usage should continue to work with the same API base paths (/api/, /_/).
  6. If you embed PocketBun programmatically, prefer JSVM-compatible naming.
    • Prefer RegisterJSVM* / MustRegisterJSVM* for naming parity with PocketBase JSVM docs.
    • RegisterHooksPlugin* / MustRegisterHooksPlugin* remain aliases.
  7. Run a migration smoke test before deploying.
    • Start: pocketbun serve --dev
    • Verify health: GET /api/health
    • Verify custom hooks/routes and auth flows you use in production.
    • If you have older generated collection/schema migrations, update them to use app.forMigrations() as described below.
  8. If you used PocketBase’s automatic HTTPS mode, put PocketBun behind a reverse proxy.
    • Run PocketBun on HTTP, for example pocketbun serve --http 127.0.0.1:8090.
    • Terminate HTTPS in Caddy, NGINX, Traefik, a load balancer, or another reverse proxy.
  9. Review the sections below for details.
    • Use this checklist for the quick pass, then check each section in this page only where your app uses that feature.

Runtime And Distribution

PocketBase:

PocketBun:

CLI Defaults And Paths

PocketBase defaults are resolved relative to executable location. PocketBun resolves defaults from current working directory.

Default paths in PocketBun CLI:

This prevents accidental writes into node_modules-adjacent paths when used as package dependency.

Hooks Plugin Naming

PocketBase JS extension naming uses jsvm package naming. PocketBun keeps those names as primary and also provides aliases:

Both names map to the same plugin registration behavior.

Hooks API And Module Loading

PocketBun supports JSVM-style lowercase naming and keeps Go-style aliases where applicable:

For pb_hooks module loading:

For code-first BaseApp usage:

Migration Hook Behavior

PocketBase generated collection migrations save collections through the normal app save path. That means custom model/collection hooks can run when old migrations are replayed on a fresh database.

PocketBase does not acknowledge this as a bug; the upstream position is that model save hooks and validations are intentionally part of save. PocketBun disagrees for generated schema migrations because historical migrations should not depend on current application/business hooks. This is the same class of replay hazard described by Rails in Using Models in Your Migrations.

PocketBun generated JS collection migrations use app.forMigrations() instead. The returned app view skips user hooks registered after app construction while preserving PocketBun system hooks required for collection persistence, table sync, cache reloads, and view updates.

For older generated collection/schema migrations, update the migration to use a migration app view:

migrate((app) => {
  const migrationApp = app.forMigrations()

  const collection = migrationApp.findCollectionByNameOrId("posts")
  collection.fields.add(new TextField({
    name: "slug",
    required: false,
  }))

  return migrationApp.save(collection)
}, (app) => {
  const migrationApp = app.forMigrations()

  const collection = migrationApp.findCollectionByNameOrId("posts")
  collection.fields.removeByName("slug")

  return migrationApp.save(collection)
})

For generated collection snapshots, use:

return app.forMigrations().importCollections(snapshot, false)

Migration rule: migrations must be able to run years later with the current app code.

Async API Extensions

PocketBun keeps sync-compatible APIs but adds async alternatives for I/O-heavy paths.

Area PocketBase-compatible sync API PocketBun async extension
Archive helpers Create, Extract CreateAsync, ExtractAsync
App bootstrap/serve app.bootstrap(), serve(...) app.bootstrapAsync(), serveAsync(...)
Migration helper migrate(...) migrateAsync(...)
Hooks plugin register RegisterJSVM(...) RegisterJSVMAsync(...)
Filesystem factories NewFilesystem() NewFilesystemAsync()
JSVM helpers $http.send(...), $os.readFile(...) $http.sendAsync(...), $os.readFileAsync(...)

Operational Differences

HTTPS

PocketBase can run a public HTTPS server directly with automatic Let’s Encrypt certificates:

PocketBun does not include PocketBase’s built-in automatic HTTPS/Let’s Encrypt server mode. The equivalent PocketBun deployment pattern is to run PocketBun over HTTP and terminate HTTPS in a reverse proxy such as Caddy, NGINX, Traefik, a platform load balancer, or a CDN edge.

Recommended PocketBun backend command:

pocketbun serve --http 127.0.0.1:8090

Minimal Caddy example:

example.com {
  reverse_proxy 127.0.0.1:8090
}

The pocketbun serve domain arguments, the --https flag, and programmatic ServeConfig.httpsAddr / ServeConfig.certificateDomains settings are intentionally unsupported and return an explanatory error instead of starting a server.

Activity logs

PocketBun persists activity logs through a background worker to reduce main-thread blocking.

Cron Scheduling

PocketBun app cron scheduling uses Bun’s native Bun.cron(...) scheduler and interprets cron expressions in UTC.

Thumbnails

PocketBun uses Sharp for image resizing. Output bytes may differ from PocketBase Go image stack.

Templates

PocketBun $template helper supports common PocketBase template patterns.

For closer Go text/template parity, install optional go-text-template.

JSVM $filepath

PocketBun exposes the same $filepath method names as PocketBase, but it does not fully match Go path/filepath edge cases.

JSVM RequestEvent request/response surface

For custom routes, e below means the route event parameter passed to routerAdd(..., (e) => { ... }).

PocketBun supports the common PocketBase custom-route access patterns:

Incompatibilities in this area:

SQL placeholders and dbx rewriting

PocketBun supports dbx-style query marker rewriting for SQLite helpers. Logged placeholder formats can differ while query behavior is compatible.

Dev SQL logging format

In --dev mode, PocketBun prints SQL logs using a Bun-native format based on the executed rewritten SQL ([X.XXms] <sql>). The exact formatting may differ from PocketBase and is informational only.

Windows behavior

PocketBase Docs Topics That Do Not Apply Directly

These upstream topics are either intentionally excluded or need reinterpretation for PocketBun:

These are not bugs in PocketBun docs; they are product-level differences.

Intentional Omissions

Intentionally not provided in PocketBun:

Deferred until demand: