PocketBase on a VPS: Single-Binary Backend as a Service for Rapid App Development
PocketBase is an open-source backend-as-a-service in a single Go binary — it provides a SQLite database with a REST API, real-time subscriptions (SSE), built-in authentication (email/password + OAuth), file storage, and a admin dashboard, all running on 32 MB RAM. It’s the polar opposite of heavy BaaS stacks: no Docker, no multiple services, just one binary. For side projects, MVPs, and small-to-medium applications, PocketBase eliminates backend infrastructure complexity entirely.
PocketBase vs Appwrite vs Supabase
| Factor | PocketBase | Appwrite | Supabase |
|---|---|---|---|
| Min RAM | 32 MB (!) | 2 GB | 4 GB |
| Setup | Single binary | Docker Compose | Docker Compose |
| Database | SQLite | Document DB | PostgreSQL |
| Realtime | SSE | WebSocket | WebSocket |
| Scalability | Single server | Good | Excellent |
| Best for | Side projects, MVPs, small apps | Medium teams | Production scale |
Step 1: Install PocketBase
<code"># Download PocketBase (single binary) mkdir -p /opt/pocketbase && cd /opt/pocketbase wget https://github.com/pocketbase/pocketbase/releases/download/v0.22.0/pocketbase_0.22.0_linux_amd64.zip unzip pocketbase_0.22.0_linux_amd64.zip rm pocketbase_0.22.0_linux_amd64.zip chmod +x pocketbase # Start PocketBase ./pocketbase serve --http="127.0.0.1:8090" # That's it — no Docker, no dependencies, nothing else
<code">sudo nano /etc/systemd/system/pocketbase.service
<code">[Unit] Description=PocketBase Backend After=network.target [Service] Type=simple User=ubuntu WorkingDirectory=/opt/pocketbase ExecStart=/opt/pocketbase/pocketbase serve --http="127.0.0.1:8090" Restart=on-failure RestartSec=5 [Install] WantedBy=multi-user.target
<code">sudo systemctl daemon-reload sudo systemctl enable pocketbase sudo systemctl start pocketbase
Step 2: Nginx Reverse Proxy
<code">sudo nano /etc/nginx/sites-available/pocketbase
<code">server {
listen 443 ssl http2;
server_name api.yourdomain.com;
ssl_certificate /etc/letsencrypt/live/api.yourdomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/api.yourdomain.com/privkey.pem;
client_max_body_size 50M;
location / {
proxy_pass http://127.0.0.1:8090;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
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;
}
}
<code">sudo certbot --nginx -d api.yourdomain.com sudo systemctl reload nginx
Step 3: Admin Setup and Collections
<code"># First time setup: # Visit https://api.yourdomain.com/_/ # Create admin email and password # Create a "posts" collection: # Admin → Collections → New Collection → "posts" # Add fields: # - title (text, required) # - content (editor/text) # - author (relation → users, required) # - published (bool, default false) # - created_at (auto date) # API rules (set who can read/write): # List/View: "" (anyone can read) # Create: "@request.auth.id != ''" (logged-in users only) # Update: "@request.auth.id = author" (only the author) # Delete: "@request.auth.id = author || @request.auth.verified = true"
Step 4: JavaScript SDK Integration
<code">npm install pocketbase
<code">import PocketBase from 'pocketbase';
const pb = new PocketBase('https://api.yourdomain.com');
// Authentication
await pb.collection('users').authWithPassword('user@example.com', 'password');
console.log(pb.authStore.isValid); // true
console.log(pb.authStore.model.id); // user ID
// CRUD operations
// Create a post
const newPost = await pb.collection('posts').create({
title: 'Hello World',
content: 'My first PocketBase post!',
author: pb.authStore.model.id,
published: true,
});
// List posts with filter and sort
const posts = await pb.collection('posts').getList(1, 20, {
filter: 'published = true',
sort: '-created',
expand: 'author', // Include author details
});
console.log(posts.items);
// Real-time subscriptions (SSE)
pb.collection('posts').subscribe('*', function(e) {
console.log(e.action); // "create", "update", "delete"
console.log(e.record); // The changed record
});
// File upload
const formData = new FormData();
formData.append('document', fileInput.files[0]);
const result = await pb.collection('files').create(formData);
Step 5: Custom Logic with Hooks
<code"># PocketBase can be extended with Go code using the jsvm or Go embed pattern
# For server-side hooks without recompiling, use the JS VM:
# 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 webhook notification after post creation
$http.send({
url: "https://your-webhook.com/new-post",
method: "POST",
body: JSON.stringify({ title: e.record.get("title") }),
headers: { "Content-Type": "application/json" },
});
}, "posts");
Step 6: Backup
<code"># PocketBase data is in a single SQLite file: # /opt/pocketbase/pb_data/data.db # Backup (works while running — SQLite WAL mode): sqlite3 /opt/pocketbase/pb_data/data.db ".backup /opt/backups/pb-$(date +%Y%m%d).db" # Or use restic: restic backup /opt/pocketbase/pb_data --tag pocketbase # Admin UI backup: Settings → Export → downloads complete backup ZIP
Getting Started
PocketBase runs on literally any Ubuntu VPS at VPS.DO — even the smallest 512 MB RAM plan. At 32 MB RAM idle and a single file for all data, it’s the lightest-weight full BaaS available. For side projects, hackathon prototypes, and small production apps (under 10,000 active users), PocketBase eliminates the need to provision databases, configure auth, build file upload infrastructure, or write REST API boilerplate.
Conclusion
PocketBase compresses a full BaaS stack — database, REST API, realtime, authentication, file storage, and admin UI — into a single 30 MB binary that runs on 32 MB RAM. No containers, no dependencies, no configuration files beyond the admin UI. For developers who want to focus on frontend and product rather than backend infrastructure, PocketBase is the fastest path from database schema to functional API. Scale limits (SQLite on single server) are real, but for most applications, those limits are reached at millions of users — far beyond typical side project scale.