Skip to content
Guides · Recipes

How to Run PocketBase in a Sandbox With a Private HTTPS URL

Updated October 27, 2026 .md

Short answer: Download the PocketBase binary into a small Sandboxer sandbox, create a superuser with its CLI and run it as a systemd service on port 8090. sbx host gives the dashboard and API a private HTTPS URL. Clients send the __sbx_auth cookie plus PocketBase's own token. Records and a live realtime stream survived stop and resume.

PocketBase is a backend in one Go binary: SQLite, auth, file storage, a REST API, realtime subscriptions and an admin dashboard. No Docker and no separate database, so it fits in a small sandbox with room to spare.

What we ran#

Everything below ran on production on October 27, 2026, in one small sandbox (2 vCPU, 4 GB RAM, 12 GB disk, $0.018/hour, region eu-central) with PocketBase 0.40.6. Tokens are replaced with <token>. Times are single runs unless the table says otherwise.

Step Result
sbx new --type small 3.2 s to ready
Binary size 32 MB, one file
systemctl enable --now to a passing /api/health 165 ms
Memory after start 28 MB resident
List records through the hosted URL, from our test machine median 0.19 s over 20 calls (0.19 to 0.20 s)
Same call inside the sandbox on localhost:8090 median 0.3 ms over 20 calls
Realtime event through the hosted URL arrived 197 ms after the create request was sent
sbx stop --wait, then sbx resume 2.2 s and 1.3 s, memoryRestored: true, same PID
Kill the process, systemd restarts it back after 5.1 s (RestartSec=5s)

1. Install the binary and a superuser#

PocketBase ships prebuilt zips on its releases page. Unpack it into its own folder; PocketBase keeps its data in pb_data next to the binary.

sbx new --name pocketbase --type small --no-env
id=sb_v6tllk1ul8    # the id that sbx new printed; its subdomain was early-stormy-puffin

sbx exec $id --timeout 600 "mkdir -p ~/pb && cd ~/pb \
  && curl -fsSLo pb.zip https://github.com/pocketbase/pocketbase/releases/download/v0.40.6/pocketbase_0.40.6_linux_amd64.zip \
  && python3 -m zipfile -e pb.zip . && chmod +x pocketbase && rm pb.zip && ./pocketbase --version"
pocketbase version 0.40.6

unzip is also installed in the sandbox if you prefer it. sbx exec stops a command after 30 seconds by default, so the download gets --timeout 600.

Create the first superuser from the command line, before the server ever starts. Then nobody can claim the dashboard by opening it first:

sbx exec $id "cd ~/pb && ./pocketbase superuser upsert admin@example.com '<password>'"
Successfully saved superuser "admin@example.com"!

2. Run it as a systemd service#

This is the unit from PocketBase's going to production page, changed to run as the sandbox's user account and to listen on 0.0.0.0:8090 instead of serving a domain itself. TLS comes from the hosted URL, so PocketBase doesn't need its own certificate.

# pocketbase.service
[Unit]
Description = pocketbase

[Service]
Type             = simple
User             = user
Group            = user
LimitNOFILE      = 4096
Restart          = always
RestartSec       = 5s
StandardOutput   = append:/home/user/pb/std.log
StandardError    = append:/home/user/pb/std.log
WorkingDirectory = /home/user/pb
ExecStart        = /home/user/pb/pocketbase serve --http=0.0.0.0:8090

[Install]
WantedBy = multi-user.target
sbx scp pocketbase.service $id:/home/user/pb/
sbx exec $id "sudo cp ~/pb/pocketbase.service /etc/systemd/system/ && sudo systemctl daemon-reload && sudo systemctl enable --now pocketbase \
  && until curl -fs localhost:8090/api/health >/dev/null; do sleep 0.05; done; curl -s localhost:8090/api/health; ps -o pid,rss,cmd -C pocketbase"
Created symlink /etc/systemd/system/multi-user.target.wants/pocketbase.service → /etc/systemd/system/pocketbase.service.
{"message":"API is healthy.","code":200,"data":{}}
    PID   RSS CMD
    657 27956 /home/user/pb/pocketbase serve --http=0.0.0.0:8090

From enable --now to a healthy API took 165 ms. enabled means PocketBase also starts on a boot from disk, and Restart=always brings it back if it dies. We killed it with sudo systemctl kill -s KILL pocketbase, and it answered again 5.1 seconds later, after the 5-second RestartSec.

3. Host the dashboard and API on one private URL#

sbx host $id 8090 --title PocketBase
{"ok":true,"type":"port.hosted","sandboxId":"sb_v6tllk1ul8","port":8090,"url":"https://early-stormy-puffin-8090.box.sandboxer.com?_token=<token>","access":"private",…}

The dashboard is at /_/ and the API at /api/ on the same host:

Request Result
/_/ or /api/health, no token 401 from the Sandboxer proxy; PocketBase never sees it
/_/?_token=<token> 302 to /_/, sets the __sbx_auth cookie
/_/ with the cookie 200, the dashboard; sign in as the superuser
/api/health with the cookie 200, {"message":"API is healthy."…}

In a browser, open the full ?_token= link once and the cookie does the rest.

4. Call the API from outside the sandbox#

There are two gates. The proxy wants the __sbx_auth cookie (its value is the URL's token). PocketBase wants its own auth token in the Authorization header for anything its API rules don't open up. The proxy passes Authorization through untouched, so both work together. The Gitea guide explains how to tell the proxy's 401 from the app's.

Sign in as the superuser through the hosted URL (auth API):

B=https://early-stormy-puffin-8090.box.sandboxer.com
curl -s -o /dev/null -c jar "$B/?_token=<token>"          # trade the token for the cookie once

curl -s -b jar -H 'Content-Type: application/json' \
  -d '{"identity":"admin@example.com","password":"<password>"}' \
  $B/api/collections/_superusers/auth-with-password > auth.json
PB=$(python3 -c "import json; print(json.load(open('auth.json'))['token'])")

Create a collection with the collections API. The empty-string rules let anyone who gets past the proxy list, view and create notes. null keeps update and delete for superusers only:

{"name":"notes","type":"base","fields":[{"name":"text","type":"text","required":true}],
 "listRule":"","viewRule":"","createRule":"","updateRule":null,"deleteRule":null}
curl -s -b jar -H "Authorization: $PB" -H 'Content-Type: application/json' -d @notes.json $B/api/collections
curl -s -b jar -H 'Content-Type: application/json' -d '{"text":"hello from my laptop"}' $B/api/collections/notes/records
curl -s -b jar "$B/api/collections/notes/records?fields=id,text"
{"id":"pbc_3395098727","listRule":"","viewRule":"","createRule":"",…,"name":"notes","type":"base",…}
{"collectionId":"pbc_3395098727","collectionName":"notes","id":"doqgofal7a1xky6","text":"hello from my laptop"}
{"items":[{"id":"doqgofal7a1xky6","text":"hello from my laptop"}],"page":1,"perPage":30,"totalItems":1,"totalPages":1}

Creating a collection with the cookie but without Authorization got PocketBase's own answer:

{"data":{},"message":"The request requires valid record authorization token.","status":401}

Over 20 list calls from our test machine, the median was 0.19 s. Inside the sandbox the same call took 0.3 ms, so the time is the network path to eu-central, not PocketBase. Code that runs in the sandbox should call http://localhost:8090 directly.

5. Realtime works through the proxy#

PocketBase pushes record changes over server-sent events: open /api/realtime, read the PB_CONNECT event for a client id, then post the topics you want. We did this with curl from our test machine, through the private URL:

curl -sN -b jar $B/api/realtime > sse.log &        # keep the stream open
cid=$(grep -m1 '^id:' sse.log | cut -d: -f2)
curl -s -b jar -H 'Content-Type: application/json' \
  -d "{\"clientId\":\"$cid\",\"subscriptions\":[\"notes\"]}" $B/api/realtime    # 204
curl -s -b jar -H 'Content-Type: application/json' -d '{"text":"pushed over SSE"}' \
  $B/api/collections/notes/records
event:PB_CONNECT
data:{"clientId":"<client id>"}

event:notes
data:{"record":{"collectionId":"pbc_3395098727","collectionName":"notes","id":"h6milz97ue5mem5","text":"pushed over SSE"},"action":"create"}

The event reached the open stream 197 ms after we sent the create request, about the same as one API round trip. The proxy doesn't buffer the stream.

6. Stop and resume: the stream stayed open#

We left that curl stream running and stopped the sandbox:

sbx stop $id --wait     # 2.2 s
sbx resume $id          # 1.3 s
sbx info $id            # "memoryRestored": true
sbx exec $id "pgrep -x pocketbase; systemctl show -p NRestarts pocketbase"
657
NRestarts=0

Same PID, no restarts: memory was restored, so PocketBase never noticed. Both notes were still there, the hosted URL and cookie still worked, and the curl stream we opened before the stop was still connected. A note created after the resume arrived on it. That was one test, and the pause was short. Write clients to reconnect and resubscribe anyway.

A resume boots from disk instead when you resize or no machine with the same CPU type is free. Then processes are gone but files stay, and the enabled unit starts PocketBase again on the same pb_data. Expect open realtime streams to drop in that case; we didn't force a boot from disk.

7. Take a backup out before you delete#

PocketBase can zip its own data with the backups API (superuser only). The zip lands in pb_data/backups, and sbx scp copies it out:

curl -s -b jar -H "Authorization: $PB" -H 'Content-Type: application/json' \
  -d '{"name":"before-delete.zip"}' $B/api/backups          # 204
sbx scp $id:/home/user/pb/pb_data/backups/before-delete.zip .
python3 -m zipfile -l before-delete.zip
File Name                                             Modified             Size
data.db                                        2026-10-27 10:06:42       167936
auxiliary.db                                   2026-10-27 10:06:42        45056
types.d.ts                                     2026-10-27 10:05:46       802641

Then delete the sandbox. The hosted URL returned 404 right after:

sbx delete $id --yes

A small sandbox costs $0.018/hour while it runs and nothing while it is stopped.

FAQ#

Do I need Docker for PocketBase?#

No. It is one 32 MB binary with SQLite built in. A systemd unit gives you restart on crash and start on boot.

Is small big enough?#

For development and demos, yes. PocketBase used 28 MB of RAM after start, and a small sandbox has 4 GB. Moving to a smaller size isn't supported yet, so starting small is the safe choice. You can resize up when you need to.

Should I make the URL public?#

Only if your API rules are ready for strangers. sbx host $id 8090 --public drops the proxy's check, so the dashboard login page and every collection rule set to "" are open to anyone with the URL. We kept ours private. Switching from public back to private mints a new token, so every client needs the new cookie.

Can my frontend use the hosted URL?#

Yes, if it sends the __sbx_auth cookie. A browser that opened the ?_token= link once has it. For a server or script, send Cookie: __sbx_auth=<token> on every request, plus PocketBase's own Authorization header where your rules need it. We tested this with curl.

Where is my data?#

In ~/pb/pb_data on the sandbox disk: data.db holds your collections and records. It survives stop, resume and auto-stop. It doesn't survive sbx delete, so take a backup first.

More guides

Top AI sandbox platforms for code execution in 2026, ranked

For running AI-written code we rank Sandboxer, Freestyle and E2B first: each gives code its own VM with an API or SDK and can pause with memory. Pick Daytona for Windows or GPUs, Modal or Northflank for GPUs at scale, E2B or Northflank to run in your own cloud, and Vercel Sandbox if your app lives on Vercel.

Read the guide
Best sandboxes for running coding agents (Claude Code, Codex, Gemini CLI) in 2026, ranked

For coding agents we rank Sandboxer first: a full Ubuntu VM with Claude Code and Codex preinstalled, Docker, and stop-and-resume that keeps the agent's session in memory. Freestyle and Amp Orbs also keep memory across pauses. Pick E2B for prebuilt agent templates on open source, Docker Sandboxes for free local runs, and Codespaces if you live in GitHub.

Read the guide
Best Daytona sandbox alternatives in 2026, ranked

The best Daytona alternatives depend on why you're switching. For a full Linux microVM on every sandbox with memory-saving stop and Docker, we rank Sandboxer first. E2B suits open source and self-hosting, Modal massive short-job fleets, Northflank bring-your-own-cloud with GPUs, and Vercel Sandbox apps on Vercel.

Read the guide
Best E2B alternatives in 2026, ranked

If you're leaving E2B for longer sessions or a fuller machine, we rank Sandboxer first, then Freestyle: both give each sandbox a full Linux VM that keeps memory across pauses, with Docker and no short session cap. Pick Daytona for Windows or GPUs, Northflank for bring-your-own-cloud, and stay on E2B if you need open source.

Read the guide