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.