Skip to main contentSkip to navigation
[email protected]
Client AreaSupport
Hosting Mammoth
HostingMammothYour Data, Our Responsibility
Home
Solutions
Hosting Services
Store
Pricing
About
Blog
API
Contact

Stay Ahead of the Curve

Get the latest insights on cybersecurity, AI innovations, and enterprise data solutions delivered to your inbox.

Hosting Mammoth
HostingMammothEnterprise Solutions

Enterprise-grade data solutions. Hosting, recovery, cybersecurity, and AI-powered services for businesses worldwide.

[email protected]
Sun - Fri, 9:00am - 5:00pm

Services

  • Cloud Hosting
  • Data Recovery
  • Cybersecurity
  • Legal Support
  • MSP Services
  • Web Development
  • AI Services
  • Free Server Migration

Hosting

  • VPS Hosting (NVMe SSD)
  • VDS Hosting (NVMe)
  • Storage VPS (High SSD)
  • GPU Servers
  • Managed Services
  • Cloud Firewall
  • Load Balancer
  • One-Click Apps
  • n8n Hosting
  • Object Storage
  • FAQ

Company

  • Store
  • Pricing
  • About Us
  • Locations
  • Blog
  • Testimonials
  • Contact
  • Affiliate Program
  • White-Label
  • Terms of Service
  • Privacy Policy
  • Browser Cookies
  • SLA

Support

  • Client Area
  • Submit Ticket
  • Knowledge Base
  • Server Status
  • API Documentation

© 2026 Hosting Mammoth. All rights reserved.

Knowledge Base
Getting StartedAccount ManagementVPS HostingGPU ServersStorage VPSCloud FirewallLoad BalancerServer ManagementBilling & PaymentsSupport & TicketsAffiliate ProgramReseller ProgramMarketplace & Appsn8n HostingManaged ServicesServer MigrationAPI & DevelopersSecurityTroubleshootingGlossaryInstall Guides
  1. Home
  2. /
  3. Support
  4. /
  5. Install Guides
  6. /
  7. How To Install Pocketbase Ubuntu
GUIDEInstall Guides

How to Install PocketBase on Ubuntu 24.04 VPS: Single-Binary Backend with SQLite, Auth, Realtime

24 min read

How to Install PocketBase on Ubuntu 24.04 VPS: Single-Binary Backend with SQLite, Auth, Realtime

PocketBase is the fastest way to get a real backend running on a Linux box. It is one open-source Go binary — about 20 MB — that bundles a SQLite database, user authentication, realtime subscriptions, file storage, an admin dashboard and a fully documented REST and JavaScript SDK. No Docker Compose, no Postgres, no Redis, no Kafka, no docker-compose.yml with seventeen services. Download it, run it, point a domain at it.

This tutorial walks you through a production-style install on Ubuntu 24.04 LTS: a dedicated Linux user, a systemd unit, admin UI at /_/, your first collection and auth flow, realtime subscriptions, file uploads, custom JavaScript hooks, Nginx reverse proxy with Let's Encrypt TLS, and automated SQLite backups.

Skip the manual install? A CloudCore Starter VPS has more than enough headroom for PocketBase plus your frontend, and you will be online in under ten minutes.

Table of Contents

  • What is PocketBase?
  • Why Self-Host PocketBase Instead of Supabase or Firebase?
  • Prerequisites
  • Step 1: Update System Packages
  • Step 2: Create a Dedicated User and Directory
  • Step 3: Download the Latest PocketBase Release
  • Step 4: Create the systemd Service
  • Step 5: Access the Admin UI at /\_/
  • Step 6: Create Collections and Schema
  • Step 7: Authentication and API Rules
  • Step 8: Realtime Subscriptions
  • Step 9: File Uploads
  • Step 10: Extend with JavaScript Hooks
  • Step 11: Nginx Reverse Proxy and Let's Encrypt TLS
  • Step 12: Backups of pb\_data
  • Troubleshooting
  • FAQ
  • Next Steps
  • What is PocketBase?

    PocketBase is an open-source backend-as-a-service written in Go. It gives you, out of a single binary:

    • An embedded SQLite database with WAL journaling
    • User authentication (email/password, OAuth2 for Google, GitHub, Apple, Discord, GitLab, and more, plus OTP and magic links)
    • REST API auto-generated from your collection schema, including filter syntax, sorting, pagination and expand relations
    • Realtime subscriptions over Server-Sent Events for live UIs
    • File storage on local disk or S3-compatible object storage
    • An admin dashboard at /_/ for managing collections, records, users, logs and settings
    • Official JavaScript, Dart and community SDKs
    • An embedded JavaScript VM for extending the server with custom hooks and routes
    Because everything is one binary and one SQLite file, deploying PocketBase is closer to copying a script than running a platform.

    Why Self-Host PocketBase Instead of Supabase or Firebase?

    Supabase and Firebase are powerful but optimised for teams that want a managed cloud. Self-hosting PocketBase gives you something different: radical simplicity.

    • One binary, one file. A PocketBase install is literally a binary plus a pb_data directory. Supabase self-hosted is a Docker Compose stack with Postgres, GoTrue, PostgREST, Realtime, Storage, Kong, ImgProxy and Studio — eight moving parts you have to monitor, back up and upgrade independently.
    • Resource-cheap. PocketBase idles at ~30 MB of RAM. A comparable Supabase stack easily consumes 2 GB before you run your first query. This is why PocketBase is happy on a $5-10/mo VPS while Supabase pushes you toward larger plans.
    • No vendor lock-in. Firebase ties you to Google Cloud; your data, auth records and functions live inside their proprietary platform. PocketBase lives on your VPS, your data is a SQLite file you can scp anywhere, and the admin UI lets you export JSON on demand.
    • Flat, predictable pricing. Firebase bills per read, per write, per stored byte, per egress byte and per auth action. A PocketBase VPS costs the same whether you serve 100 or 10 million requests a month.
    • Privacy and compliance. Your users' emails, password hashes, uploads and telemetry never leave your server. GDPR, HIPAA and data-residency audits become straightforward: point to the machine.
    • Offline-friendly development. The whole stack runs locally on your laptop — no emulator quirks, no API key juggling, no staging project to reset.

    When PocketBase is the Right Choice

    PocketBase fits neatly in the gap between "Firebase for a weekend prototype" and "we run our own Postgres cluster". It is a great pick for:

    • Indie SaaS products and micro-apps
    • Mobile and Flutter app backends
    • Realtime dashboards (chat, collaborative editors, live leaderboards)
    • Internal admin tools
    • MVPs that need auth, a database and file uploads on day one
    For write-heavy multi-region systems, multi-hundred-GB datasets, or complex OLAP workloads, you will eventually want Postgres. See our Supabase install guide for that path.

    Prerequisites

    Before starting, make sure you have:

    • A VPS running Ubuntu 24.04 LTS with root or sudo access
    • SSH access to the server
    • A domain name (e.g. api.example.com) with an A record pointing at your VPS IP — required for TLS in Step 11
    • 1 GB RAM minimum (2 GB+ recommended once you add your frontend, Nginx and cron jobs)
    • 10 GB free disk for the OS, PocketBase binary and initial pb_data
    Recommended Plan: CloudCore Starter
    >
    PocketBase is so lightweight that the CloudCore Starter plan is more than enough for a production deployment:
    >
    - 2 vCPU cores
    - 4 GB RAM
    - 50 GB NVMe SSD
    - Unmetered bandwidth
    >
    This leaves plenty of headroom for Nginx, Certbot, a Node frontend and automated backups on the same box. If you plan to run a separate Node.js API or Next.js app alongside PocketBase, also see our Node.js install guide.

    Connect to your server:

    bash
    ssh root@your-server-ip

    Step 1: Update System Packages

    Bring the base image up to date before installing anything:

    bash
    sudo apt update && sudo apt upgrade -y
    sudo apt install -y unzip curl ca-certificates

    If the kernel was upgraded, reboot once:

    bash
    sudo reboot

    Reconnect via SSH and continue.

    Step 2: Create a Dedicated User and Directory

    Running PocketBase as root is a bad habit. Create a system user that owns only the PocketBase tree:

    bash
    sudo useradd --system --home /opt/pocketbase --shell /usr/sbin/nologin pocketbase
    sudo mkdir -p /opt/pocketbase
    sudo chown pocketbase:pocketbase /opt/pocketbase

    --system gives a low UID and no aging policy. --shell /usr/sbin/nologin prevents interactive logins even if the account is compromised.

    Step 3: Download the Latest PocketBase Release

    PocketBase ships as prebuilt ZIPs on GitHub Releases. Check the latest version at pocketbase.io/docs/ or the releases page and substitute the version string below.

    bash
    cd /tmp
    PB_VERSION="0.22.21"
    curl -L -o pocketbase.zip \
      "https://github.com/pocketbase/pocketbase/releases/download/v${PB_VERSION}/pocketbase_${PB_VERSION}_linux_amd64.zip"
    unzip pocketbase.zip
    sudo mv pocketbase /opt/pocketbase/pocketbase
    sudo chown pocketbase:pocketbase /opt/pocketbase/pocketbase
    sudo chmod +x /opt/pocketbase/pocketbase

    Verify:

    bash
    /opt/pocketbase/pocketbase --version

    Expected output:

    text
    PocketBase v0.22.21
    ARM64 VPS? Swap linux_amd64 for linux_arm64 in the URL. PocketBase ships both architectures officially.

    Step 4: Create the systemd Service

    A proper systemd unit makes PocketBase start on boot, restart on crash and stream logs to journalctl.

    bash
    sudo tee /etc/systemd/system/pocketbase.service > /dev/null <<'EOF'
    [Unit]
    Description=PocketBase
    After=network.target

    [Service] Type=simple User=pocketbase Group=pocketbase WorkingDirectory=/opt/pocketbase ExecStart=/opt/pocketbase/pocketbase serve --http=127.0.0.1:8090 Restart=always RestartSec=5 LimitNOFILE=4096

    Hardening

    NoNewPrivileges=true PrivateTmp=true ProtectSystem=full ProtectHome=true ReadWritePaths=/opt/pocketbase

    [Install] WantedBy=multi-user.target EOF

    A couple of important choices:

    • --http=127.0.0.1:8090 binds PocketBase to localhost. Nginx will terminate TLS in Step 11 and proxy to this port. Never expose 8090 directly to the public internet.
    • The Protect* directives sandbox the process so a compromised PocketBase binary cannot read /home, write outside /opt/pocketbase, or gain new privileges.
    Enable and start:

    bash
    sudo systemctl daemon-reload
    sudo systemctl enable --now pocketbase
    sudo systemctl status pocketbase

    Expected Active: line:

    text
    Active: active (running) since Thu 2026-04-16 10:12:03 UTC; 3s ago

    Stream logs with:

    bash
    sudo journalctl -u pocketbase -f

    Step 5: Access the Admin UI at /\_/

    PocketBase's admin dashboard lives at /_/ on the same port as the API. Since we bound to localhost, you have two options to reach it before Nginx is in place:

    Option A — SSH tunnel (recommended for initial setup):

    From your laptop:

    bash
    ssh -L 8090:127.0.0.1:8090 root@your-server-ip

    Then visit http://127.0.0.1:8090/_/ in your browser.

    Option B — Temporarily bind to all interfaces:

    Edit the service (ExecStart=/opt/pocketbase/pocketbase serve --http=0.0.0.0:8090), open port 8090 in UFW, create your admin, then revert. Do not leave this open.

    On first visit, PocketBase prompts you to create the first admin account (email + password). This account has full access to collections, records and settings, so use a strong password and enable a password manager.

    After login you land on the Collections screen, with two default collections: users (authentication records) and a built-in _superusers table for admins.

    Step 6: Create Collections and Schema

    Collections are PocketBase's equivalent of tables. Each collection has a typed schema and generates a REST API automatically.

    Click New collection and create a posts collection with these fields:

    Field nameTypeOptions
    titleTextrequired, min length 3, max length 200
    slugTextrequired, unique, pattern ^[a-z0-9-]+$
    contentEditorrequired
    coverFilesingle file, images only, max 5 MB
    publishedBooldefault false
    authorRelation→ users, single, required
    tagsJSONoptional
    Save it. PocketBase now exposes these endpoints automatically:

    • GET /api/collections/posts/records — list with filter, sort, expand
    • GET /api/collections/posts/records/:id — fetch one
    • POST /api/collections/posts/records — create
    • PATCH /api/collections/posts/records/:id — update
    • DELETE /api/collections/posts/records/:id — delete
    Try a quick list call from the server itself:

    bash
    curl http://127.0.0.1:8090/api/collections/posts/records

    If List API rules are empty, the response will be an authorization error — which is exactly what we want. Let's fix that with rules next.

    Step 7: Authentication and API Rules

    PocketBase auth works out of the box. The built-in users collection supports email/password signup, email verification, password reset and OAuth2.

    Signup and Login from the API

    bash
    # Create an account
    curl -X POST http://127.0.0.1:8090/api/collections/users/records \
      -H "Content-Type: application/json" \
      -d '{
        "email": "[email protected]",
        "password": "supersecret123",
        "passwordConfirm": "supersecret123",
        "name": "Alice"
      }'

    Log in and receive a JWT

    curl -X POST http://127.0.0.1:8090/api/collections/users/auth-with-password \ -H "Content-Type: application/json" \ -d '{ "identity": "[email protected]", "password": "supersecret123" }'

    The login response contains a token (JWT) and the full user record. Pass the token in subsequent requests as:

    text
    Authorization: Bearer <token>

    Enabling OAuth2

    In the admin UI, open the users collection → Options → OAuth2 and toggle providers like Google or GitHub. Paste each provider's Client ID and Client Secret, set the redirect URL to https://api.example.com/api/oauth2-redirect, and the PocketBase JS SDK will handle the full flow with pb.collection('users').authWithOAuth2({ provider: 'google' }).

    API Rules (Per-Collection Permissions)

    Every collection has five rule slots: List, View, Create, Update, Delete. Rules are filter expressions evaluated against the authenticated request. An empty rule means "admins only"; "" (literally the empty string) means "public".

    For the posts collection, configure:

    ActionRuleMeaning
    Listpublished = true \</td><td>\</td><td>author = @request.auth.idAnyone can see published posts, authors see their own drafts
    Viewpublished = true \</td><td>\</td><td>author = @request.auth.idSame as list
    Create@request.auth.id != ""Any logged-in user can create
    Updateauthor = @request.auth.idOnly the author can edit
    Deleteauthor = @request.auth.idOnly the author can delete
    Save. The same curl list call now returns published posts without authentication.

    Step 8: Realtime Subscriptions

    PocketBase pushes record changes over Server-Sent Events. The JavaScript SDK exposes this as pb.collection('posts').subscribe().

    Install the SDK in your frontend:

    bash
    npm install pocketbase

    Minimal browser example:

    javascript
    import PocketBase from 'pocketbase';

    const pb = new PocketBase('https://api.example.com');

    await pb.collection('users').authWithPassword('[email protected]', 'supersecret123');

    // Subscribe to all create/update/delete events on posts pb.collection('posts').subscribe('*', (e) => { console.log(e.action, e.record); // e.action is one of: 'create' | 'update' | 'delete' });

    // Or subscribe to a single record pb.collection('posts').subscribe('RECORD_ID', (e) => { console.log('This post changed:', e.record); });

    Subscriptions respect the same API rules as REST. If a user cannot View a record, they will not receive realtime events for it. Behind the scenes this is a long-lived GET /api/realtime connection — Nginx configuration below includes the right settings for SSE.

    Step 9: File Uploads

    The cover field on our posts collection accepts image uploads. PocketBase handles multipart form data natively.

    Upload a cover image with curl:

    bash
    TOKEN="eyJhbGciOi..."  # from auth-with-password
    AUTHOR_ID="k9aljg32hjkl1p"

    curl -X POST http://127.0.0.1:8090/api/collections/posts/records \ -H "Authorization: Bearer ${TOKEN}" \ -F "title=My First Post" \ -F "slug=my-first-post" \ -F "content=<p>Hello world</p>" \ -F "author=${AUTHOR_ID}" \ -F "published=true" \ -F "cover=@./cover.jpg"

    Files are stored under /opt/pocketbase/pb_data/storage/<collectionId>/<recordId>/. Access them via:

    text
    https://api.example.com/api/files/<collectionId>/<recordId>/<filename>

    PocketBase also supports on-the-fly thumbnails: append ?thumb=100x100 (or 100x100f, 100x100b, 0x100) to resize images without storing extra derivatives.

    S3-Compatible Storage

    For production, you can switch storage from local disk to any S3-compatible provider (AWS S3, Backblaze B2, Cloudflare R2, Contabo Object Storage). In the admin UI: Settings → Files storage → Use S3 storage. Paste the endpoint, bucket, region, access key and secret. PocketBase will migrate new uploads to S3; existing files stay local until you copy them.

    Step 10: Extend with JavaScript Hooks

    This is where PocketBase goes from "nice little BaaS" to "real backend". An embedded Goja JavaScript runtime lets you register hooks, custom routes and scheduled jobs without recompiling.

    Create the hooks directory:

    bash
    sudo -u pocketbase mkdir -p /opt/pocketbase/pb_hooks

    Example hook: send a welcome email when a new user signs up, and add a custom /api/hello route:

    bash
    sudo -u pocketbase tee /opt/pocketbase/pb_hooks/main.pb.js > /dev/null <<'EOF'
    // Fires after a new record is created in the "users" collection
    onRecordAfterCreateRequest((e) => {
      console.log([hook] new user signed up: ${e.record.get(&quot;email&quot;)});

    // Send a welcome email using PocketBase's built-in mailer const message = new MailerMessage({ from: { address: e.app.settings().meta.senderAddress, name: e.app.settings().meta.senderName }, to: [{ address: e.record.get("email") }], subject: "Welcome!", html: &lt;p&gt;Hi ${e.record.get(&quot;name&quot;) || &quot;there&quot;}, thanks for signing up.&lt;/p&gt;, });

    try { e.app.newMailClient().send(message); } catch (err) { console.log("mailer error:", err); } }, "users");

    // Custom route: GET /api/hello?name=Bob → {"message":"Hello, Bob"} routerAdd("GET", "/api/hello", (c) => { const name = c.queryParam("name") || "world"; return c.json(200, { message: Hello, ${name} }); });

    // Daily cron job at 03:00 server time cronAdd("dailyCleanup", "0 3 *", () => { console.log("[cron] running daily cleanup"); // e.g. delete unverified users older than 30 days }); EOF

    Reload PocketBase to pick up the hook file:

    bash
    sudo systemctl restart pocketbase

    Test the custom route:

    bash
    curl http://127.0.0.1:8090/api/hello?name=Alice
    

    {"message":"Hello, Alice"}

    For heavier workloads — sharp image processing, native dependencies, SDKs not available in Goja — you can also use PocketBase as a Go library and compile your own binary. The same hooks API exists in Go.

    Step 11: Nginx Reverse Proxy and Let's Encrypt TLS

    Now put a proper front door on PocketBase. If you have not installed Nginx yet, see our Nginx install guide for the full walkthrough.

    Install Nginx and Certbot:

    bash
    sudo apt install -y nginx certbot python3-certbot-nginx

    Create the site config. Replace api.example.com with your domain:

    bash
    sudo tee /etc/nginx/sites-available/pocketbase > /dev/null <<'EOF'
    server {
        listen 80;
        server_name api.example.com;
        return 301 https://$host$request_uri;
    }

    server { listen 443 ssl http2; server_name api.example.com;

    # Certbot will fill these in ssl_certificate /etc/letsencrypt/live/api.example.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/api.example.com/privkey.pem;

    # Security headers add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; add_header X-Content-Type-Options nosniff always; add_header X-Frame-Options DENY always; add_header Referrer-Policy strict-origin-when-cross-origin always;

    # Raise upload size to match your largest file field (5 MB in our posts.cover) client_max_body_size 50m;

    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;

    # Required for PocketBase realtime (SSE) and long uploads proxy_buffering off; proxy_cache off; proxy_read_timeout 600s; proxy_send_timeout 600s; } } EOF

    sudo ln -sf /etc/nginx/sites-available/pocketbase /etc/nginx/sites-enabled/pocketbase sudo nginx -t

    Obtain a certificate:

    bash
    sudo certbot --nginx -d api.example.com
    sudo systemctl reload nginx

    Certbot schedules automatic renewal via a systemd timer. Verify it:

    bash
    sudo systemctl list-timers | grep certbot

    You can now hit https://api.example.com/_/ in your browser to reach the admin UI over TLS, and https://api.example.com/api/... for your REST endpoints.

    Locking Down the Admin UI

    The /_/ path is only protected by the admin password. Options to further reduce attack surface:

    • Add a second Nginx location /_/ block with allow <your-ip>; deny all;
    • Add HTTP Basic Auth in front of /_/ on top of the admin login
    • Move /_/ behind a VPN or Tailscale

    Step 12: Backups of pb_data

    Everything stateful in PocketBase lives under /opt/pocketbase/pb_data:

    • data.db — the main SQLite database (records, collections, schema)
    • auxiliary.db — logs and internal metrics
    • storage/ — uploaded files
    • logs.db — request logs (if enabled)
    • backups/ — built-in snapshot archives

    Option A: Built-in Backup API

    PocketBase has a first-class backup feature. In the admin UI: Settings → Backups → Create new backup. It produces a .zip with the entire pb_data directory. You can also trigger it from the API:

    bash
    curl -X POST https://api.example.com/api/backups \
      -H "Authorization: Bearer <admin-token>" \
      -H "Content-Type: application/json" \
      -d '{"name":"daily.zip"}'

    Settings → Backups can upload each backup automatically to any S3-compatible bucket — combine this with Cloudflare R2 or Backblaze B2 for $0 egress offsite storage.

    Option B: Scheduled SQLite Snapshots

    For finer-grained backups, use SQLite's online backup from cron. SQLite is safe to snapshot while PocketBase is running (WAL mode handles concurrency).

    bash
    sudo apt install -y sqlite3
    sudo mkdir -p /opt/pocketbase/snapshots
    sudo chown pocketbase:pocketbase /opt/pocketbase/snapshots

    sudo tee /opt/pocketbase/snapshot.sh > /dev/null <<'EOF' #!/usr/bin/env bash set -euo pipefail TS=$(date +%Y%m%d-%H%M%S) OUT="/opt/pocketbase/snapshots/data-${TS}.db" sqlite3 /opt/pocketbase/pb_data/data.db ".backup ${OUT}" gzip -9 "${OUT}"

    Keep last 14 snapshots

    ls -1t /opt/pocketbase/snapshots/data-*.db.gz | tail -n +15 | xargs -r rm -- EOF sudo chmod +x /opt/pocketbase/snapshot.sh sudo chown pocketbase:pocketbase /opt/pocketbase/snapshot.sh

    Schedule it daily at 04:00:

    bash
    sudo crontab -u pocketbase -e
    

    add this line:

    0 4 * /opt/pocketbase/snapshot.sh >> /opt/pocketbase/snapshots/snapshot.log 2>&1

    Pair this with rclone or rsync to push /opt/pocketbase/snapshots/ and /opt/pocketbase/pb_data/storage/ to offsite object storage. A complete disaster-recovery plan is: hourly local SQLite backups, daily tarball of storage/ to S3, weekly full pb_data zip to a different region.

    Troubleshooting

    ProblemCauseSolution
    Active: failed after systemctl start pocketbaseWrong path or missing execute bit on the binaryls -la /opt/pocketbase/pocketbase; ensure owner pocketbase and mode 0755. Run sudo journalctl -u pocketbase -n 50 for the actual error.
    Admin UI reachable but API returns 404Nginx stripped the path, or hitting the wrong portConfirm proxy_pass http://127.0.0.1:8090; (no trailing slash) and that the service listens on 8090 with ss -tlnp \</td><td>grep 8090.
    Realtime subscription disconnects every few secondsNginx is buffering SSEMake sure proxy_buffering off; is inside the location / block and proxy_read_timeout is at least 600s.
    413 Request Entity Too Large on uploadsDefault Nginx body limit is 1 MBRaise client_max_body_size to cover your largest file field (e.g. 50m).
    database is locked under loadToo-aggressive concurrent writes, or pb_data on a network mountKeep pb_data on local NVMe, never NFS/SMB. Consider batching writes.
    Backups fail with unable to open database fileCron user cannot read pb_dataEnsure the cron runs as pocketbase (crontab -u pocketbase), not root, and pb_data is 0700 owned by pocketbase.
    High CPU after a JS hook changeInfinite loop or recursive create → hook → createDisable the hook (rename main.pb.js), restart, inspect logs with journalctl -u pocketbase.
    Cannot reach /_/ after enabling NginxCertbot modified the server block, removed the generic locationsudo nginx -t and re-check the file under /etc/nginx/sites-available/pocketbase.

    FAQ

    Is PocketBase production-ready?

    Yes. PocketBase has been in active development since 2022, is used by thousands of production apps, and the Go/SQLite combination has decades of proven reliability. The main caveats are the ones inherent to any single-node SQLite architecture: you scale vertically, and you should not put pb_data on a networked filesystem.

    How many requests per second can PocketBase handle?

    On a single 2 vCPU / 4 GB VPS (our CloudCore Starter), PocketBase comfortably sustains several thousand read requests per second and hundreds of writes per second thanks to SQLite WAL mode. Realtime subscriptions use long-lived connections and scale to tens of thousands of concurrent clients on modest hardware. Benchmarks on the PocketBase docs site and community runs show it outperforming many heavier Node.js backends because SQLite avoids cross-process network calls.

    Can I use PocketBase with a Next.js or SvelteKit frontend?

    Absolutely — this is its canonical use case. Install the pocketbase npm package, initialise new PocketBase('https://api.example.com'), and call pb.collection('...') from both server components (SSR) and the browser. For server-side rendering, pass the user's auth cookie into PocketBase on each request so API rules evaluate correctly. See the official SDK docs at pocketbase.io/docs for SSR patterns.

    How do I migrate from Firebase to PocketBase?

    Export your Firestore collections as JSON, map each collection to a PocketBase collection with equivalent fields, then import records through the admin UI's CSV/JSON import or via the REST API. Firebase Auth users can be imported into PocketBase's users collection — you will need to force a password reset on first login because Firebase's password hashes are not compatible. Firebase Storage files are easy to migrate: download and re-upload to PocketBase's file fields (or point PocketBase at an S3 bucket that already holds them).

    Can I run multiple PocketBase instances on one VPS?

    Yes — each instance is a separate binary pointing at a separate pb_data directory and port. Copy the systemd unit (pocketbase-app2.service), change WorkingDirectory and the --http port (e.g. 8091), and add a matching Nginx server block. This is a clean way to host multiple small apps on one VPS without container overhead.

    How do I monitor PocketBase?

    Out of the box, journalctl -u pocketbase captures all application logs. For metrics, wrap PocketBase with a small Go binary that exposes Prometheus endpoints, or scrape Nginx access logs with Loki + Grafana. The admin UI's Logs tab shows recent HTTP requests with filters, which is often enough for small apps.

    Next Steps

    PocketBase is live, authenticated and backed up. Here is what to build on top:

    • Connect a frontend — scaffold a SvelteKit, Next.js, Nuxt or Flutter app with the PocketBase SDK and point it at https://api.example.com.
    • Add OAuth2 providers — enable Google, GitHub, Apple or Discord logins in the users collection options for one-click signup.
    • Set up transactional email — in Settings → Mail settings, configure SMTP (Postmark, Resend, AWS SES, or self-hosted Mailcow) so password resets and the welcome hook from Step 10 actually send.
    • Automate deployments — build a small CI pipeline that uploads new pb_hooks files with rsync over SSH, then calls systemctl restart pocketbase.
    • Graduate to Postgres when needed — if you hit true multi-writer scale, you can export your PocketBase JSON and import it into Supabase. Until then, enjoy the simplicity.
    • Read the official docs — the best reference is still pocketbase.io/docs/, which covers the full JS hooks API, filter syntax, and SDK patterns.

    Your Backend, Your VPS, 15 Minutes
    >
    PocketBase pairs perfectly with a CloudCore Starter VPS:
    >
    - Ubuntu 24.04 LTS pre-installed
    - 2 vCPU, 4 GB RAM, 50 GB NVMe
    - Unmetered bandwidth
    - Root SSH access in under 60 seconds
    >
    Deploy a VPS and run the commands in this guide end to end in about 25 minutes.

    Was this article helpful?

    ← Back to Install GuidesBrowse all categories →

    Still have questions?

    Contact Support →Submit a Ticket