Run PocketBase on fly.io

By

Learn how to run PocketBase on fly.io with a Dockerfile, then attach a persistent volume with fly volumes create so your data survives deploys.

~~~

To run PocketBase on fly.io, you build a small Docker image that downloads the PocketBase binary, then deploy it with a persistent volume attached, so your data survives deploys.

PocketBase is an open source backend packed in a single executable. It gives you a database (SQLite), authentication, file storage, and an admin UI, all from one binary. That makes it a great fit for a tiny Fly machine.

First set up fly.io. See run an app on fly.io.

Also see my Docker tutorials.

The Dockerfile

Create an empty folder, add this Dockerfile (update the PB_VERSION value to the latest version of PocketBase available):

FROM alpine:latest

ARG PB_VERSION=0.40.5

RUN apk add --no-cache \
    unzip \
    ca-certificates

# download and unzip PocketBase
ADD https://github.com/pocketbase/pocketbase/releases/download/v${PB_VERSION}/pocketbase_${PB_VERSION}_linux_amd64.zip /tmp/pb.zip

RUN unzip /tmp/pb.zip -d /pb/

# uncomment to copy the local pb_migrations dir into the image
# COPY ./pb_migrations /pb/pb_migrations

# uncomment to copy the local pb_hooks dir into the image
# COPY ./pb_hooks /pb/pb_hooks

EXPOSE 8080

# start PocketBase
CMD ["/pb/pocketbase", "serve", "--http=[::]:8080"]

The image downloads the release zip from GitHub, unzips the binary into /pb/, and starts the server on port 8080. Note the base image is Alpine, which is what the apk package manager belongs to.

--http is the listen address. PocketBase’s own Docker example uses 0.0.0.0:8080, which is IPv4 only. Fly’s public proxy talks to the machine over IPv4, so that works for https://<yourapp>.fly.dev. Fly’s private network is IPv6 only. .internal names resolve to IPv6 addresses, and those connections never reach an IPv4-only listener.

[::]:8080 is the IPv6 wildcard. PocketBase opens it with Go’s net.Listen("tcp", ...), which on Linux also accepts IPv4. Public Fly traffic and .internal URLs then hit the same process. After deploy, from another app in the same org you can run curl -I http://<yourapp>.internal:8080/_/.

Create the app and the volume

Run fly launch --build-only to create and build the app but not deploy it, as we need to attach a volume to persist data across deploys (but first we need to initialize it and the fly.toml file).

fly launch writes fly.toml. Check that the HTTP service internal_port is 8080, the same port as --http.

Why the volume? PocketBase writes everything to the pb_data folder: the SQLite database, uploaded files, all of it. The machine’s filesystem is rebuilt on every deploy, so without a volume you’d lose your data each time you ship a change.

Run fly volumes create pb_data --size=1 to create the volume. Create it in the same region of the app.

Now in fly.toml add:

[mounts]
  destination = "/pb/pb_data"
  source = "pb_data"

This mounts the volume at the exact path where PocketBase keeps its data.

Deploy

Now run fly deploy --ha=false.

The --ha=false flag matters. By default Fly creates two machines for redundancy, but a volume attaches to a single machine, and SQLite can’t be shared between two of them. One machine is what we want here.

PocketBase will be available at https://<yourapp>.fly.dev/_/.

When I wrote this post, PocketBase 0.20 let you create the admin account on the first visit to that page. Since PocketBase 0.23 the dashboard is locked until a superuser exists. On the first start PocketBase prints an installer link in the server output, so run fly logs and open that link. Or create the account from the command line:

fly ssh console -C "/pb/pocketbase superuser create [email protected] yourpassword"

Then you land in the dashboard where you define collections.

Upgrade PocketBase

The binary lives in the image, the data lives on the volume. To upgrade, change PB_VERSION in the Dockerfile, then deploy again with fly deploy --ha=false. pb_data stays on the volume.

You do not run pocketbase update in this setup. That command downloads a new GitHub release and replaces a binary on disk. It is for a VPS, and PocketBase warns that it may not behave in Docker. Bumping PB_VERSION is the same idea: new executable, same data directory.

System database migrations run on the first serve of the new binary. Read the GitHub release notes before you bump. PocketBase is pre-1.0, so a major release can break the API, the JS SDK, or pb_hooks / pb_migrations. Existing pb_data is usually upgraded automatically when the new binary starts. If you ship pb_hooks or pb_migrations, test those against the new version locally, and take a backup before you deploy.

Back up pb_data

Copying pb_data while PocketBase is stopped is a valid backup. With the app running, use the dashboard instead: Settings > Backups. That API shipped in PocketBase 0.16. It writes a zip of pb_data (uploaded files included, local backup zips excluded).

Store those zips on the volume (the default) or send them to S3-compatible storage. Use a separate bucket for backups, not the same one you use for user uploads. A backup can slow queries while it runs. If pb_data is several gigabytes, the dashboard zip is the wrong tool and you want a different strategy (sqlite3 .backup plus rsync, or an off-machine copy of the volume).

Fly also snapshots the volume. Daily snapshots are on by default and kept for 5 days (you can set 1 to 60). To take one yourself:

fly volumes list
fly volumes snapshots create VOLUME_ID

Restore by creating a new volume from a snapshot, same size or larger, then point [mounts] at that new volume:

fly volumes snapshots list VOLUME_ID
fly volumes create pb_data --snapshot-id SNAPSHOT_ID --size=1

Snapshots cover the disk. If the data changes all day, also use the PocketBase backups, because a host failure between snapshots still loses the writes in between.

The public URL still works if another Fly app of yours should talk to PocketBase over the internet. For app-to-app traffic on the same org, use http://<yourapp>.internal:8080 now that the process is listening on [::]:8080.

Want me to talk about your product? You can sponsor this site.

~~~

Related posts about services: