Pocketbase Lightweight Backend Installation
PocketBase is a single-binary backend that bundles SQLite, authentication, real-time subscriptions, file storage, and a REST API into one executable — no Docker, no databases to install separately. Deploying PocketBase on a Linux VPS is as simple as downloading a binary, making it ideal for lightweight applications and rapid prototyping.
Prerequisites
- Linux VPS (Ubuntu/Debian or CentOS/Rocky)
- At least 512 MB RAM and 1 vCPU (PocketBase is very lightweight)
- A domain name (optional but recommended for production)
- Nginx or Caddy for reverse proxy (optional)
Installing PocketBase
PocketBase is a single binary with no runtime dependencies:
# Create a directory for PocketBase
sudo mkdir -p /opt/pocketbase
cd /opt/pocketbase
# Download the latest release (check https://github.com/pocketbase/pocketbase/releases)
LATEST=$(curl -s https://api.github.com/repos/pocketbase/pocketbase/releases/latest | grep -o '"tag_name": *"[^"]*"' | cut -d'"' -f4)
wget "https://github.com/pocketbase/pocketbase/releases/download/${LATEST}/pocketbase_${LATEST#v}_linux_amd64.zip"
# Extract the binary
unzip "pocketbase_${LATEST#v}_linux_amd64.zip"
chmod +x pocketbase
# Verify it works
./pocketbase --version
# Run temporarily to test (Ctrl+C to stop)
./pocketbase serve
Access the admin dashboard at http://your-server-ip:8090/_/.
Firewall Configuration
# Ubuntu/Debian
sudo ufw allow 8090/tcp
sudo ufw reload
# Or if using Nginx reverse proxy, only allow 80/443
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
Running as a Systemd Service
Create a dedicated user and systemd service:
# Create a system user for PocketBase
sudo useradd --system --shell /sbin/nologin --home /opt/pocketbase pocketbase
# Set ownership
sudo chown -R pocketbase:pocketbase /opt/pocketbase
# Create the systemd service file
sudo tee /etc/systemd/system/pocketbase.service << 'EOF'
[Unit]
Description=PocketBase Backend
After=network.target
[Service]
Type=simple
User=pocketbase
Group=pocketbase
WorkingDirectory=/opt/pocketbase
ExecStart=/opt/pocketbase/pocketbase serve \
--http="0.0.0.0:8090" \
--dir=/opt/pocketbase/pb_data
Restart=always
RestartSec=5
StandardOutput=journal
StandardError=journal
# Security hardening
PrivateTmp=true
NoNewPrivileges=true
ProtectSystem=strict
ReadWritePaths=/opt/pocketbase/pb_data
[Install]
WantedBy=multi-user.target
EOF
# Enable and start the service
sudo systemctl daemon-reload
sudo systemctl enable pocketbase
sudo systemctl start pocketbase
sudo systemctl status pocketbase
Nginx Reverse Proxy
# /etc/nginx/sites-available/pocketbase
server {
server_name api.yourdomain.com;
location / {
proxy_pass http://127.0.0.1:8090;
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;
# WebSocket support for real-time
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}
sudo ln -s /etc/nginx/sites-available/pocketbase /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx
sudo certbot --nginx -d api.yourdomain.com
Collection Schema Design
PocketBase uses "collections" as its data model. Create them via the admin UI or API:
# Authenticate as admin
TOKEN=$(curl -s -X POST "http://localhost:8090/api/admins/auth-with-password" \
-H "Content-Type: application/json" \
-d '{"identity":"[email protected]","password":"adminpassword"}' \
| python3 -c "import sys,json; print(json.load(sys.stdin)['token'])")
# Create a collection via API
curl -X POST "http://localhost:8090/api/collections" \
-H "Authorization: $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "posts",
"type": "base",
"schema": [
{
"name": "title",
"type": "text",
"required": true,
"options": {"min": 3, "max": 200}
},
{
"name": "content",
"type": "editor"
},
{
"name": "status",
"type": "select",
"options": {
"maxSelect": 1,
"values": ["draft", "published", "archived"]
}
},
{
"name": "author",
"type": "relation",
"options": {
"collectionId": "_pb_users_auth_",
"maxSelect": 1
}
}
]
}'
Collection API Rules
# Set collection access rules
curl -X PATCH "http://localhost:8090/api/collections/posts" \
-H "Authorization: $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"listRule": "status = \"published\"",
"viewRule": "status = \"published\"",
"createRule": "@request.auth.id != \"\"",
"updateRule": "author = @request.auth.id",
"deleteRule": "author = @request.auth.id"
}'
Authentication Configuration
PocketBase has a built-in users auth collection:
# Register a new user
curl -X POST "http://localhost:8090/api/collections/users/records" \
-H "Content-Type: application/json" \
-d '{
"username": "johndoe",
"email": "[email protected]",
"password": "SecurePass123!",
"passwordConfirm": "SecurePass123!",
"name": "John Doe"
}'
# Login with email/password
curl -X POST "http://localhost:8090/api/collections/users/auth-with-password" \
-H "Content-Type: application/json" \
-d '{"identity": "[email protected]", "password": "SecurePass123!"}'
# OAuth2 providers can be configured in Admin UI:
# Settings → Auth Providers → Enable Google/GitHub/etc.
Custom Auth Collections
# Create a separate auth collection for a different user type (e.g., sellers)
curl -X POST "http://localhost:8090/api/collections" \
-H "Authorization: $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "sellers",
"type": "auth",
"schema": [
{"name": "store_name", "type": "text", "required": true},
{"name": "verified", "type": "bool"}
]
}'
Real-Time Subscriptions
PocketBase supports Server-Sent Events (SSE) for real-time updates:
// JavaScript client example
import PocketBase from 'pocketbase';
const pb = new PocketBase('https://api.yourdomain.com');
// Subscribe to all changes in the posts collection
await pb.collection('posts').subscribe('*', function(e) {
console.log(e.action); // "create", "update", or "delete"
console.log(e.record);
});
// Subscribe to a specific record
await pb.collection('posts').subscribe('RECORD_ID', function(e) {
console.log('Post updated:', e.record);
});
// Unsubscribe
await pb.collection('posts').unsubscribe();
File Storage
Collections can have file fields for attachments:
# Create a collection with a file field
curl -X POST "http://localhost:8090/api/collections" \
-H "Authorization: $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "documents",
"type": "base",
"schema": [
{"name": "title", "type": "text", "required": true},
{
"name": "attachment",
"type": "file",
"options": {
"maxSelect": 1,
"maxSize": 10485760,
"mimeTypes": ["application/pdf", "image/*"],
"thumbs": ["100x100"]
}
}
]
}'
# Upload a file with the record
curl -X POST "http://localhost:8090/api/collections/documents/records" \
-H "Authorization: Bearer user-token" \
-F "title=My Document" \
-F "attachment=@/path/to/file.pdf"
# Files are stored at: /opt/pocketbase/pb_data/storage/{collection}/{record_id}/
# File URL pattern: https://api.yourdomain.com/api/files/{collection}/{record_id}/{filename}
Backup Strategies
Manual Backup
# Stop PocketBase for a clean backup
sudo systemctl stop pocketbase
# Backup the entire data directory
tar -czf "/backup/pocketbase-$(date +%Y%m%d-%H%M%S).tar.gz" /opt/pocketbase/pb_data
# Restart after backup
sudo systemctl start pocketbase
# SQLite hot backup (no downtime required)
sqlite3 /opt/pocketbase/pb_data/data.db ".backup /backup/pocketbase-$(date +%Y%m%d).db"
Automated Backups via Admin API
# PocketBase has built-in backup via admin API
curl -X POST "http://localhost:8090/api/backups" \
-H "Authorization: $TOKEN"
# List available backups
curl "http://localhost:8090/api/backups" \
-H "Authorization: $TOKEN"
# Restore from a backup
curl -X POST "http://localhost:8090/api/backups/backup-name.zip/restore" \
-H "Authorization: $TOKEN"
Cron-Based Backup to S3
# Install s3cmd
sudo apt-get install -y s3cmd
# Create backup script
sudo tee /opt/pocketbase/backup.sh << 'EOF'
#!/bin/bash
BACKUP_FILE="/tmp/pocketbase-$(date +%Y%m%d-%H%M%S).tar.gz"
tar -czf "$BACKUP_FILE" /opt/pocketbase/pb_data
s3cmd put "$BACKUP_FILE" s3://your-bucket/backups/
rm "$BACKUP_FILE"
# Keep only last 30 days
s3cmd ls s3://your-bucket/backups/ | awk '{print $4}' | sort | head -n -30 | xargs -r s3cmd del
EOF
chmod +x /opt/pocketbase/backup.sh
# Schedule daily backups
echo "0 3 * * * /opt/pocketbase/backup.sh" | sudo crontab -
Troubleshooting
PocketBase fails to start:
# Check service logs
sudo journalctl -u pocketbase -n 100 --no-pager
# Run manually to see errors
sudo -u pocketbase /opt/pocketbase/pocketbase serve
# Check port is not in use
netstat -tlnp | grep 8090
Admin UI inaccessible:
# Verify PocketBase is running
systemctl status pocketbase
# Test locally
curl -I http://localhost:8090/_/
# Check Nginx proxy config if using reverse proxy
sudo nginx -t
Real-time subscriptions disconnecting:
# Ensure Nginx has long timeout for SSE
# Add to nginx location block:
# proxy_read_timeout 86400;
# proxy_buffering off;
SQLite locked errors under high load:
# PocketBase handles concurrent writes via WAL mode by default
# For write-heavy workloads, consider reducing concurrent writers
# Check for long-running queries in admin dashboard
Conclusion
PocketBase's single-binary design makes it the simplest self-hosted backend available — download, run, and you have a complete API with authentication, real-time events, and file storage. While it won't replace a full-stack database cluster for high-throughput applications, it's a powerful foundation for small-to-medium applications, internal tools, and rapid prototyping where operational simplicity is a priority.


