A developer wanted a backend for his mobile app. Firebase was overkill. Supabase needed a cloud account. Then he found PocketBase — a single executable file that gives you a database, auth, file storage, and real-time subscriptions. One file. Zero dependencies.
What PocketBase Offers
PocketBase (open source, free):
- SQLite database with REST API
- Authentication — email/password, OAuth2 (Google, GitHub, etc.)
- File storage — upload and serve files
- Real-time subscriptions — Server-Sent Events
- Admin dashboard — web UI for managing data
- Hooks — JavaScript/Go hooks for custom logic
- Single binary — no dependencies, ~30MB download
- Embedded — runs on a $5 VPS, Raspberry Pi, or your laptop
Quick Start
# Download (one file!)
wget https://github.com/pocketbase/pocketbase/releases/latest/download/pocketbase_linux_amd64.zip
unzip pocketbase_linux_amd64.zip
# Run
./pocketbase serve
# API at http://127.0.0.1:8090
# Admin UI at http://127.0.0.1:8090/_/
REST API
# List records
curl 'http://127.0.0.1:8090/api/collections/posts/records?page=1&perPage=20&sort=-created'
# Get single record
curl 'http://127.0.0.1:8090/api/collections/posts/records/RECORD_ID'
# Create record
curl -X POST 'http://127.0.0.1:8090/api/collections/posts/records' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer USER_TOKEN' \
-d '{
"title": "My First Post",
"content": "Hello, PocketBase!",
"published": true
}'
# Update record
curl -X PATCH 'http://127.0.0.1:8090/api/collections/posts/records/RECORD_ID' \
-H 'Authorization: Bearer USER_TOKEN' \
-H 'Content-Type: application/json' \
-d '{"title": "Updated Title"}'
# Delete record
curl -X DELETE 'http://127.0.0.1:8090/api/collections/posts/records/RECORD_ID' \
-H 'Authorization: Bearer USER_TOKEN'
# Filter records
curl 'http://127.0.0.1:8090/api/collections/posts/records?filter=(published=true%26%26author="alice")'
JavaScript SDK
import PocketBase from 'pocketbase';
const pb = new PocketBase('http://127.0.0.1:8090');
// Auth
await pb.collection('users').authWithPassword('alice@example.com', 'password');
// Create
const post = await pb.collection('posts').create({
title: 'Hello World',
content: 'My first post',
author: pb.authStore.model.id
});
// List with filter
const posts = await pb.collection('posts').getList(1, 20, {
filter: 'published = true',
sort: '-created',
expand: 'author'
});
// Real-time subscriptions
pb.collection('posts').subscribe('*', (e) => {
console.log(`${e.action}: ${e.record.title}`);
// 'create', 'update', or 'delete'
});
// File upload
const formData = new FormData();
formData.append('title', 'Photo Post');
formData.append('image', fileInput.files[0]);
const record = await pb.collection('posts').create(formData);
const imageUrl = pb.files.getUrl(record, record.image);
OAuth2 Auth
// Google login
await pb.collection('users').authWithOAuth2({ provider: 'google' });
// GitHub login
await pb.collection('users').authWithOAuth2({ provider: 'github' });
// Check auth state
if (pb.authStore.isValid) {
console.log(`Logged in as ${pb.authStore.model.email}`);
}
Custom Hooks (JavaScript)
// pb_hooks/main.pb.js
onRecordBeforeCreateRequest((e) => {
// Auto-set author to current user
e.record.set('author', e.httpContext.get('authRecord').id);
}, 'posts');
onRecordAfterCreateRequest((e) => {
// Send notification after post created
console.log(`New post: ${e.record.get('title')}`);
}, 'posts');
Deploy
# It's one file — copy it anywhere
scp pocketbase user@server:/opt/pocketbase/
ssh user@server '/opt/pocketbase/pocketbase serve --http=0.0.0.0:8090'
# Or Docker
docker run -v ./pb_data:/pb_data ghcr.io/muchobien/pocketbase:latest
PocketBase vs Firebase
| PocketBase | Firebase |
|---|---|
| Self-hosted (own data) | Google-hosted |
| SQLite (simple) | Firestore (complex) |
| Single binary | SDK + Cloud console |
| Free forever | Free tier limits |
| 30MB download | Heavy SDK |
Need to scrape data into your backend? Check out my web scraping actors on Apify.
Need a lightweight backend? Email me at spinov001@gmail.com.
Top comments (0)