Self-Hosting PocketBase with Docker Compose
What Is PocketBase?
PocketBase is a self-hosted backend in a single binary. It bundles a SQLite database, realtime subscriptions, user authentication (password, OAuth2, OTP), file storage, and a full admin dashboard. You get a Firebase-like backend without vendor lock-in, external dependencies, or monthly bills. It’s written in Go, weighs about 15 MB, and can run on a Raspberry Pi. Official site.
Prerequisites
- A Linux server (Ubuntu 22.04+ recommended)
- Docker and Docker Compose installed (guide)
- 256 MB of free RAM (PocketBase uses ~15-30 MB idle)
- Understanding: PocketBase is pre-1.0 software (v0.36.x) — breaking changes can occur between versions
Docker Compose Configuration
PocketBase doesn’t have an official Docker image. The recommended approach is building from the official Dockerfile. Create a project directory:
mkdir pocketbase && cd pocketbase
Create a Dockerfile:
FROM alpine:3.21
ARG PB_VERSION=0.36.5
RUN apk add --no-cache \
unzip \
ca-certificates
ADD https://github.com/pocketbase/pocketbase/releases/download/v${PB_VERSION}/pocketbase_${PB_VERSION}_linux_amd64.zip /tmp/pb.zip
RUN unzip /tmp/pb.zip -d /pb/ && \
rm /tmp/pb.zip
EXPOSE 8090
CMD ["/pb/pocketbase", "serve", "--http=0.0.0.0:8090"]
Create docker-compose.yml:
services:
pocketbase:
build:
context: .
args:
PB_VERSION: "0.36.5"
container_name: pocketbase
restart: unless-stopped
ports:
- "8090:8090"
volumes:
- pocketbase-data:/pb/pb_data # SQLite database, uploaded files, backups
# - ./pb_hooks:/pb/pb_hooks # Uncomment for JavaScript hooks
# - ./pb_migrations:/pb/pb_migrations # Uncomment for schema migrations
environment:
- GOMEMLIMIT=512MiB # Prevents OOM in constrained environments
ulimits:
nofile:
soft: 4096
hard: 4096
volumes:
pocketbase-data:
Build and start:
docker compose up -d --build
Initial Setup
The admin dashboard is at http://your-server:8090/_/.
Create a superuser account via CLI (the recommended approach since v0.23):
docker exec -it pocketbase /pb/pocketbase superuser upsert [email protected] YourSecurePassword123
Log in to the dashboard with these credentials.
Create Your First Collection
Collections are PocketBase’s version of database tables.
- Click New collection in the dashboard
- Name it (e.g., “posts”)
- Add fields — PocketBase supports text, number, bool, email, URL, date, file, relation, JSON, select, and GeoPoint fields
- Set API Rules to control who can read, create, update, and delete records
- Save — your REST API is immediately available at
/api/collections/posts/records
Key Features
| Feature | Details |
|---|---|
| Database | Embedded SQLite with WAL mode (concurrent reads, serialized writes) |
| Authentication | Password, OAuth2 (Google, GitHub, Microsoft, Apple, Discord, etc.), OTP, MFA |
| Realtime | Server-Sent Events for live data — subscribe to collection or record changes |
| File storage | Local filesystem (default) or S3-compatible (AWS, MinIO, Wasabi, DigitalOcean Spaces) |
| Admin dashboard | Full web UI for managing collections, records, settings, backups, and logs |
| REST API | CRUD with filtering, sorting, pagination, field selection, relation expansion, batch operations |
| API rules | Per-collection access control with filter expressions |
| JavaScript hooks | Extend PocketBase with JS files in pb_hooks/ |
| Auto HTTPS | Pass a domain to pocketbase serve example.com for automatic Let’s Encrypt |
| Backups | Built-in dashboard backup creates ZIP snapshots |
| Rate limiting | Built-in (v0.23.0+), configurable via dashboard |
Configuration
S3 File Storage
Switch from local filesystem to S3-compatible storage via the admin dashboard:
- Go to Settings → Files storage
- Select S3 and enter your credentials
- Existing local files are NOT migrated automatically
SMTP for Email
Configure email sending for password resets and verification:
- Go to Settings → Mail settings
- Enter your SMTP server details
- Test with the built-in test button
Encryption
Encrypt sensitive settings (SMTP passwords, S3 keys) stored in the database:
environment:
- PB_ENCRYPTION_KEY=your-random-32-character-string-here
Then start PocketBase with --encryptionEnv=PB_ENCRYPTION_KEY.
JavaScript Hooks
Extend PocketBase’s behavior with JavaScript files:
mkdir pb_hooks
Create pb_hooks/main.pb.js:
onRecordAfterCreateSuccess((e) => {
console.log("New record created:", e.record.id)
}, "posts")
Mount the hooks directory in your Compose file and restart.
What PocketBase Replaces
| Cloud Service | PocketBase Equivalent |
|---|---|
| Firebase Firestore | Collections + REST API |
| Firebase Auth | Built-in auth (password, OAuth2, OTP) |
| Firebase Storage | File fields + local or S3 storage |
| Firebase Realtime Database | SSE subscriptions |
| Supabase | Full backend (minus edge functions) |
| Airtable | Collections with the admin dashboard |
Reverse Proxy
PocketBase serves everything on a single port. Standard reverse proxy configuration works:
location / {
proxy_pass http://127.0.0.1:8090;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Connection '';
proxy_buffering off; # Required for SSE realtime
client_max_body_size 50M; # Adjust for file uploads
}
See Reverse Proxy Setup.
Backup
PocketBase stores everything in pb_data/:
# Use the built-in backup from the admin dashboard
# Or back up the volume directly:
docker compose stop pocketbase
docker run --rm -v pocketbase-data:/data -v $(pwd):/backup alpine \
tar czf /backup/pocketbase-backup-$(date +%Y%m%d).tar.gz /data
docker compose start pocketbase
For databases over 2 GB, use sqlite3 .backup instead of the built-in ZIP backup — it’s faster and produces a consistent snapshot. See Backup Strategy.
Troubleshooting
”database is locked” errors
Symptom: Requests fail with SQLite busy/locked errors.
Fix: Never store pb_data on network-attached storage (NFS, SMB, Azure Files). SQLite requires proper file locking that network filesystems don’t provide. Use local block storage or Docker named volumes backed by local disk.
Superuser creation token not visible
Symptom: First run prints a secure link to stderr, but you can’t see it in Docker.
Fix: Check container logs with docker logs pocketbase. Or skip the link entirely and create a superuser via CLI: docker exec -it pocketbase /pb/pocketbase superuser upsert email password.
File uploads fail with large files
Symptom: Uploads over 5 MB fail.
Fix: The default upload limit is 5 MB per file. Adjust in the admin dashboard under collection settings → file field → Max file size. Also increase client_max_body_size in your reverse proxy.
High memory usage under load
Symptom: Container memory grows beyond expected limits.
Fix: Set GOMEMLIMIT=512MiB (or appropriate for your environment) to force Go’s garbage collector to be more aggressive. Also reduce log retention in Settings → Logs.
Container file descriptor limit
Symptom: Errors about “too many open files” with many realtime connections.
Fix: Set ulimits.nofile in your Compose file to at least 4096. The default 1024 is insufficient for applications with many concurrent SSE connections.
Resource Requirements
- RAM: ~15-30 MB idle, ~50-100 MB under load
- CPU: Minimal — single core sufficient
- Disk: 15 MB binary. Database and files grow with usage. 1 GB minimum recommended.
- Limitation: Single-writer SQLite. Cannot horizontally scale — one instance only.
Verdict
PocketBase is the fastest way to self-host a backend. For personal projects, internal tools, and small-to-medium apps, it replaces Firebase, Supabase, and Airtable with zero monthly cost and zero external dependencies. The admin dashboard is polished, the API is well-designed, and the realtime support works out of the box.
The limitations are real: pre-1.0 software (expect breaking changes), SQLite-only (no horizontal scaling), and no official Docker image. For production applications at scale, look at Appwrite or Supabase. For everything else, PocketBase is exceptional.
Related
Get self-hosting tips in your inbox
New guides, comparisons, and setup tutorials — delivered weekly. No spam.