Pocketbase As A Framework: Deploy With Front- and Backend
You build your application using Pocketbase as a Framework, and now you want to bring it to production and host it on your server. In this guide, we will look at 2 ways to deploy pocketbase as the backend and 3 ways how to deploy it together with a frontend. As a little bonus, I also added simple docker-compose files and GitHub Actions.
Backend
Fullstack
- Deploy Pocketbase with a Frontend using two Containers
- Deploy Pocketbase with a Frontend using one Docker image
Deploy Pocketbase as a Framework using Docker
This method is suitable for you if you only want to host a pocketbase backend and use Docker on your server.
FROM golang:1.18 AS build-backend
RUN mkdir /app
ADD . /app
WORKDIR /app
RUN CGO_ENABLED=0 GOOS=linux go build -o <binary-name> .
FROM alpine:latest AS production
COPY --from=build-backend /app .
EXPOSE 8080
CMD ["./<binary-name>", "serve", "--http=0.0.0.0:8080"]
This is a simple docker-compose file to run the container on your server. After running it, you can access the pocketbase instance under IP:8080
.
You can also make it accessible through a domain by pointing an A record to the IP of your server and using a reverse proxy to direct the traffic to the correct container. In this post, I teach you how to use an automatic reverse proxy with SSL support for your new applications: here.
Need help or want to share feedback? Join my discord community!
services:
backend:
container_name: backend
image: <registry>/<image-name>
ports:
- "8080:8080"
volumes:
- ./data:/pb_data
The GitHub Action connects to your Server using SSH and then pulls and builds the new version of the image using docker-compose. You might need to adapt the GitHub Action to your environment, but the Dockerfile will help you with every environment using Docker.
name: publish
on:
push:
branches: [ "main" ]
env:
# Use docker.io for Docker Hub if empty
REGISTRY: ghcr.io
# github.repository as <account>/<repo>
IMAGE_NAME: <image-name>
jobs:
publish:
name: publish image
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Login
run: |
echo ${{ secrets.PAT }} | docker login ghcr.io -u ${{ github.actor }} --password-stdin
- name: Build and Publish Backend
run: |
docker build . --tag ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest
docker push ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest
deploy:
needs: publish
name: deploy image
runs-on: ubuntu-latest
steps:
- name: install ssh keys
# check this thread to understand why its needed:
# <https://stackoverflow.com/a/70447517>
run: |
install -m 600 -D /dev/null ~/.ssh/id_rsa
echo "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/id_rsa
ssh-keyscan -H ${{ secrets.SSH_HOST }} > ~/.ssh/known_hosts
- name: connect and pull
run: ssh ${{ secrets.SSH_USER }}@${{ secrets.SSH_HOST }} "cd ${{ secrets.WORK_DIR }} && docker compose pull && docker compose up -d && exit"
- name: cleanup
run: rm -rf ~/.ssh
Deploy Pocketbase with a Frontend using two Containers
This method is suitable for you if you only want to host both a pocketbase backend and a frontend, for example, using vue. Additionally, you need to use Docker on your server. Splitting the frontend and backend into different images is useful if you want to run multiple frontend instances on your server or use a solution like Kubernetes with auto-scaling.
If this guide is helpful to you and you like what I do, please support me with a coffee!
# build stage
FROM node:lts-alpine as build
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build
# production stage
FROM nginx:stable-alpine as production
COPY --from=build /app/dist /usr/share/nginx/html
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
FROM golang:1.18 AS build-backend
RUN mkdir /app
ADD . /app
WORKDIR /app
RUN CGO_ENABLED=0 GOOS=linux go build -o <binary-name> .
FROM alpine:latest AS production
COPY --from=build-backend /app .
EXPOSE 8080
CMD ["./<binary-name>", "serve", "--http=0.0.0.0:8080"]
This is a simple docker-compose file to run the containers on your server. After running it, you can access the pocketbase instance under IP:8080
, and the frontend instance under IP:3000
.
You can also make them accessible through a domain by pointing an A record to the IP of your server and using a reverse proxy to direct the traffic to the correct container. In this post, I teach you how to use an automatic reverse proxy with SSL support for your new applications: here.
services:
frontend:
container_name: frontend
image: <registry>/<image-name>-frontend:latest
ports:
- "3000:3000"
backend:
container_name: backend
image: <registry>/<image-name>-backend:latest
ports:
- "8080:8080"
volumes:
- ./data:/pb_data
The GitHub Action connects to your Server using SSH and then pulls and builds the new versions of the images using docker-compose. You might need to adapt the GitHub Action to your environment, but the Dockerfile will help you with every environment using Docker.
name: publish
on:
push:
branches: [ "main" ]
env:
# Use docker.io for Docker Hub if empty
REGISTRY: ghcr.io
# github.repository as <account>/<repo>
IMAGE_NAME: <image-name>
jobs:
publish:
name: publish image
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Login
run: |
echo ${{ secrets.PAT }} | docker login ghcr.io -u ${{ github.actor }} --password-stdin
- name: Build and Publish Backend
run: |
docker build ./backend --tag ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-backend:latest
docker push ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-backend:latest
- name: Build and Publish Frontend
run: |
docker build ./frontend --tag ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-frontend:latest
docker push ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-frontend:latest
deploy:
needs: publish
name: deploy image
runs-on: ubuntu-latest
steps:
- name: install ssh keys
# check this thread to understand why its needed:
# <https://stackoverflow.com/a/70447517>
run: |
install -m 600 -D /dev/null ~/.ssh/id_rsa
echo "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/id_rsa
ssh-keyscan -H ${{ secrets.SSH_HOST }} > ~/.ssh/known_hosts
- name: connect and pull
run: ssh ${{ secrets.SSH_USER }}@${{ secrets.SSH_HOST }} "cd ${{ secrets.WORK_DIR }} && docker compose pull && docker compose up -d && exit"
- name: cleanup
run: rm -rf ~/.ssh
Deploy Pocketbase with a Frontend using one Docker image (⭐ Docker)
This method suits you if you want to host both the backend and frontend using one docker image. In the example, before, we used NGINX to serve the frontend, and in this case, we use pocketbase itself to do so. For this to work, we must add a simple route to the main.go
file:
app.OnBeforeServe().Add(func(e *core.ServeEvent) error {
// serves static files from the provided public dir (if exists)
e.Router.GET("/*", apis.StaticDirectoryHandler(os.DirFS("./pb_public"), true))
return nil
})
Here it’s important to note that the true add the end sets index.html as the fallback for all routes. If you get 404 errors when moving to different routes while having this option on false, you can fix that by setting it to true.
FROM node:lts-alpine as build-frontend
WORKDIR /app
COPY ./frontend/package*.json ./
RUN npm install
COPY ./frontend/ .
RUN npm run build
FROM golang:1.18 AS build-backend
RUN mkdir /app
ADD ./backend /app
COPY --from=build-frontend /app/dist /app/pb_public
WORKDIR /app
RUN CGO_ENABLED=0 GOOS=linux go build -o <binary-name> .
FROM alpine:latest AS production
COPY --from=build-backend /app .
EXPOSE 8080
CMD ["./<binary-name>", "serve", "--http=0.0.0.0:8080"]
This is a simple docker-compose file to run the container on your server. After running it, you can access the pocketbase instance under IP:8080/_
and the frontend under IP:8080/
. You can also make it accessible through a domain by pointing an A record to the IP of your server and using a reverse proxy to direct the traffic to the correct container. In this post, I teach you how to use an automatic reverse proxy with SSL support for your new applications: here.
services:
backend:
container_name: backend
image: <registry>/<image-name>
ports:
- "8080:8080"
volumes:
- ./data:/pb_data
The GitHub Action connects to your Server using SSH and then pulls and builds the new version of the image using docker-compose. You might need to adapt the GitHub Action to your environment, but the Dockerfile will help you with every environment using Docker.
name: publish
on:
push:
branches: [ "main" ]
env:
# Use docker.io for Docker Hub if empty
REGISTRY: ghcr.io
# github.repository as <account>/<repo>
IMAGE_NAME: <image-name>
jobs:
publish:
name: publish image
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Login
run: |
echo ${{ secrets.PAT }} | docker login ghcr.io -u ${{ github.actor }} --password-stdin
- name: Build and Publish Backend
run: |
docker build . --tag ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest
docker push ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest
deploy:
needs: publish
name: deploy image
runs-on: ubuntu-latest
steps:
- name: install ssh keys
# check this thread to understand why its needed:
# <https://stackoverflow.com/a/70447517>
run: |
install -m 600 -D /dev/null ~/.ssh/id_rsa
echo "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/id_rsa
ssh-keyscan -H ${{ secrets.SSH_HOST }} > ~/.ssh/known_hosts
- name: connect and pull
run: ssh ${{ secrets.SSH_USER }}@${{ secrets.SSH_HOST }} "cd ${{ secrets.WORK_DIR }} && docker compose pull && docker compose up -d && exit"
- name: cleanup
run: rm -rf ~/.ssh
Conclusion
There are multiple different methods to bring your Pocketbase Application to production. In this post, we learned how to do so by using Docker and rsync. I hope this post was helpful to you, and if there are any questions, feel free to contact me!
And also, in case you liked this post, consider subscribing to my newsletter.
[convertkit form=2303042]