The Problem
Teams often need a turnkey backend that handles authentication, file storage, and realtime subscriptions without the overhead of provisioning a separate database or API gateway. PocketBase addresses this by bundling an embedded SQLite database, a REST‑ish API, and realtime WebSocket support into a single Go binary.
What This Does
PocketBase provides an embedded SQLite database with realtime subscriptions (core/db.go, core/events.go). The API layer lives in apis/ – recordcrud.go implements CRUD operations, recordauth.go handles password, OTP, and OAuth2 flows, and middlewares/ adds CORS, gzip, and rate‑limit handling. The admin UI is built into the binary (cmd/serve.go), and the project ships as a standalone executable or as a Go library (core/app.go). Files and user management are baked in, so you can spin up a functional backend by running one command.
How To Use It
Setup
Prebuilt executable: Download the appropriate archive from the Releases page, extract, and run
./pocketbase serve Go library: Create a new module, then use the minimal example from the README (cmd/serve.go pattern):
package main
import ( "log" "github.com/pocketbase/pocketbase" "github.com/pocketbase/pocketbase/core" )
func main() { app := pocketbase.New() app.OnServe().BindFunc(func(se core.ServeEvent) error { se.Router.GET("/hello", func(re core.RequestEvent) error { return re.String(200, "Hello world!") }) return se.Next() }) if err := app.Start(); err != nil { log.Fatal(err) } }
Run go mod init myapp && go mod tidy, then go run main.go serve. To build a statically linked binary: CGOENABLED=0 go build.
Configuration
Configuration is exposed through command‑line flags and environment variables defined in cmd/serve.go (e.g., --dir, --http-address). No separate config.yaml is required for a basic deployment.
Running it
Standalone: ./pocketbase serve (entry point cmd/serve.go). Go framework: go run main.go serve after the minimal main.go above.
Real‑World Use
A mobile team can prototype a client‑server app in hours: PocketBase’s embedded realtime channel pushes new records to iOS/Android clients via the JavaScript or Dart SDKs. Example workflow – after pocketbase serve, a Dart client subscribes to a collection:
final pb = PocketBase('http://127.0.0.1:8090'); final stream = pb.collection('records').subscribe(); stream.listen((record) { print('New record: ${record.id}'); });
This eliminates the need to set up a separate Firebase or Supabase project for early‑stage prototypes.
Code Health & Issues
Tests & CI: 87 test files exist; GitHub Actions workflow (release.yaml) runs on every push, providing basic regression coverage. License & Metadata: LICENSE.md and CHANGELOG.md are present; a .goreleaser.yaml configures releases. Pre‑v1.0 risk: The README warns that full backward compatibility is not guaranteed before v1.0.0, so breaking changes may appear in future releases. Code structure: The repo is well‑organized into core/, apis/, and cmd/, with clear separation of database, API, and entry‑point concerns. No obvious missing error‑handling patterns were found in the reviewed files, though the extensive auth surface (apis/recordauth_*.go) warrants attention when customizing flows.
The Bottom Line
PocketBase delivers a compelling, single‑file realtime backend for solo developers and small teams that need quick user‑auth, file, and subscription capabilities without managing external services. Its Go‑native design, solid test suite, and active CI make it reliable for prototypes and internal tools, but the pre‑v1.0 status means breaking changes are possible—organizations building long‑term products should monitor the roadmap or pin to a specific release tag.