Deploying a containerized PocketBase instance to Fly.io
Requirements
I’d like to deploy a database for content and other data for this blog and various small projects. PocketBase is an ideal solution, being lightweight, quick to set up, and featuring an intuitive GUI that non-technical collaborators can comfortably use to contribute and update data.
Hosting
I’ll deploy to fly.io, though the steps to containerize will make this a viable process for other VPS setups in the future.
1. Initialize directory
First:
pnpm init
…in the directory of your choosing. Then make the necessary files and folders for PocketBase and Docker:
mkdir pb_migrations pb_hooks pb_data
touch Dockerfile pb_migrations/.gitkeep pb_hooks/.gitkeep pb_data/.gitignore
In pb_data/.gitignore we can tell git to ignore all the local data besides the .gitignore itself, so our folder is available but we don’t version control the data:
*
!.gitignore
2. Set up the Dockerfile
Create our Dockerfile. These are essentially instructions to be followed by Docker as it builds the image:
FROM alpine:latest
# a small linux distro that will run in this container
ARG PB_VERSION=0.22.22
# check the latest version at the pocketbase website
RUN apk add --no-cache \
# add the following packages without caching the package index (to reduce size)
unzip \
# add support for extracting zip archives
ca-certificates \
# Installs trusted certificates, enabling SSL/TLS verification for HTTPS connections
openssh
# Installs the OpenSSH client and server, allowing secure remote access if we want to download our data
ADD https://github.com/pocketbase/pocketbase/releases/download/v${PB_VERSION}/pocketbase_${PB_VERSION}_linux_amd64.zip /tmp/pb.zip
# download and unzip PocketBase based on the PB_VERSION
RUN unzip /tmp/pb.zip -d /pb/
# unzip it to the /pb directory
COPY ./pb_migrations /pb/pb_migrations
# copy the local pb_migrations dir into the container
COPY ./pb_hooks /pb/pb_hooks
# copy the local pb_hooks dir into the container
EXPOSE 8090
# expose 8090 for HTTP, usually this is 8080 but pocketbase examples use 8090
CMD ["/pb/pocketbase", "serve", "--http=0.0.0.0:8090"]
# start PocketBase
3. Create the package scripts
Our dev script with run docker:start if the container isn’t built yet, or fallback to docker:run if it’s already there.
docker run creates and starts a new container from a Docker image.
--name pb-starter specifies the name of the container. In this case, it’s named “pb-starter”.
*-d runs the container in detached mode (background). The container will continue running even after the terminal session ends.
-p 8090:8090 maps a container port to a host port.
8090(left): The host port (your machine’s port).8090(right): The container port (the port exposed by the container).
All the -v flags in docker:run are creating volumes for the directories in our PocketBase project.
{
"name": "pb-starter",
"private": true,
"version": "0.0.1",
"description": "Demo repo for deploying Pocketbase to Fly!",
"scripts": {
"dev": "pnpm docker:start || pnpm docker:run",
"docker:build": "docker build -t pb-starter .",
"docker:run": "docker run --name pb-starter -d -p 8090:8090 -v ./pb_data:/pb/pb_data -v ./pb_migrations:/pb/pb_migrations -v ./pb_hooks:/pb/pb_hooks pb-starter",
"docker:start": "docker start pb-starter",
"docker:stop": "docker stop pb-starter",
"docker:rm": "docker rm pb-starter"
}
}
We can run:
pnpm docker:build
Then:
pnpm dev
Access the PocketBase UI locally at localhost:8090/_!
We can log in and update the database to our liking. Migrations will be created and visible in our /pb_migrations folder which we can version control in our repo. The data we create in /pb_data will only exist locally.
4. Deployment - Fly.io CLI & login
We can deploy wherever Docker containers are supported. For fly.io we can follow these steps:
curl -L https://fly.io/install.sh | sh
This installs the fly CLI.
fly auth login
This will authenticate our account through the browser.
5. Deployment - Configure project on Fly.io
fly launch --build-only
This will allow us to set some preferences like location (PocketBase only scales vertically in a single location), and choose CPU and RAM amounts (1 CPU w 512mb will work for us to start). It will then attempt to build the container and should succeed.
6. Deployment - persisting data and getting online
fly volumes create pb_data --size=1
This creates a 1gb pb_data volume for our data. Adjust the http_sevice.concurrency and [mounts] portions of out fly.toml file.
# fly.toml app configuration file generated for YOUR_APP on 2024-10-24T13:31:47-07:00
#
# See https://fly.io/docs/reference/configuration/ for information about how to use this file.
#
app = YOUR_APP
primary_region = REGION_CLOSE_TO_YOUR_USERS
[build]
[http_service]
internal_port = 8090
force_https = true
auto_stop_machines = 'stop'
auto_start_machines = true
min_machines_running = 0
processes = ['app']
# help fly proxy reuse connections for requests
[http_service.concurrency]
type = "requests"
soft_limit = 500
hard_limit = 550
[[vm]]
memory = '512mb'
cpu_kind = 'shared'
cpus = 1
# Add this:
[mounts]
destination = "/pb/pb_data"
source = "pb_data"
Finally…
fly deploy
And we’re up and running .
7. CI/CD with Github Actions
mkdir .github .github/workflows
touch .github/workflows/deploy-pocketbase.yaml
Let’s add a github action to deploy when we push changes to the repo. We need to add out FLY_API_TOKEN to our actions repository secrets. We can generate the token:
fly tokens deploy
name: Deploy PocketBase to Fly
on:
push:
branches: [main]
jobs:
deploy:
name: Deploy proxy
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: superfly/flyctl-actions/setup-flyctl@master
- run: flyctl deploy --remote-only
env:
FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }}
Now, any migrations we generate by updating tables in PocketBase can be deployed when we push the main branch.
Helpful Sources
This workflow is credit to Johnny Magrippis, check out his tutorial here: Your own CMS + Backend in 15’: How to deploy PocketBase to Fly.io 🚀 DevEx, version control, CI/CD! - YouTube