Home | Send Feedback | Share on Bluesky |

PocketBase - Deploy to Production

Published: 20. July 2025  •  angular, ionic, pocketbase

In the previous blog post, I showed you how to write a JavaScript application talking to PocketBase on the backend. In this post, I will show you how to deploy such an application on a Debian 12 server using Caddy as a reverse proxy. This guide assumes that a domain is already set up and pointing to the server IP address. I will use the domain todo.rasc.ch for this example. And it also assumes that the application has access to an SMTP server for sending emails. This could be a local SMTP server or an external one like Gmail, SendGrid, etc.

Architecture

The application we will deploy in this guide consists of two parts: an Angular application compiled to static files and a PocketBase API server. Both parts will be served by the Caddy web server, which will act as a reverse proxy.

Internet
Caddy
Angular App - Static Files /home/todo/www
PocketBase Server 127.0.0.1:9999

1. Server Preparation

Update System

To ensure the system is up to date, I always do the following first:

sudo apt update && sudo apt upgrade -y

Install Required Packages

To install Caddy, follow the instructions from the official documentation.

If you want to build the Angular application on the server, you also need to install Node.js and npm. I will compile the Angular application on my local machine and then upload the static files to the server with scp.

We also need the unzip and wget packages to download and extract the PocketBase binary. If not already installed, install them with the following command:

sudo apt install unzip wget -y

Create Application User

For security reasons, it is a good practice to run the application under a dedicated user account. Here we create a user named todo:

sudo useradd -m -s /bin/bash todo
sudo usermod -aG sudo todo

# Switch to application user
sudo su - todo

2. Deploy PocketBase

Download and Setup PocketBase

In this step, we create the home directory for our application and download the latest version of PocketBase. Make sure to run these steps as the todo user we created earlier.

The pocketbase folder will contain the PocketBase binary including the pb_data and pb_migrations directories. Into the www folder, we will later copy the static files of our Angular application.

mkdir -p /home/todo/pocketbase
mkdir -p /home/todo/pocketbase/pb_migrations

mkdir -p /home/todo/www
cd /home/todo/pocketbase

# Download PocketBase (check for latest version at https://github.com/pocketbase/pocketbase/releases)
# Make sure to download the correct version for your architecture
wget https://github.com/pocketbase/pocketbase/releases/download/v0.29.0/pocketbase_0.29.0_linux_amd64.zip

unzip pocketbase_0.29.0_linux_amd64.zip
chmod +x pocketbase
rm pocketbase_0.29.0_linux_amd64.zip
mkdir -p pb_data

Create PocketBase Systemd Service

To run PocketBase as a service, we create a systemd service file.

sudo nano /etc/systemd/system/pocketbase.service

Add the following content:

[Unit]
Description=PocketBase
After=network.target

[Service]
Type=simple
User=todo
Group=todo
LimitNOFILE=4096
WorkingDirectory=/home/todo/pocketbase
ExecStart=/home/todo/pocketbase/pocketbase serve --http=127.0.0.1:9999
Restart=always
RestartSec=5
StandardOutput=journal
StandardError=journal
SyslogIdentifier=pocketbase

# Security settings
NoNewPrivileges=yes
PrivateTmp=yes
ProtectSystem=strict
ReadWritePaths=/home/todo/pocketbase

[Install]
WantedBy=multi-user.target

Enable PocketBase Service

Next, enable the PocketBase service so it runs on server startup.

# Reload systemd and enable service
sudo systemctl daemon-reload
sudo systemctl enable pocketbase

3. Deploy Angular Application

Build Application Locally

In the Angular application, I set the production environment to point to the PocketBase API URL.

export const environment = {
  production: true,
  pocketbaseUrl: 'https://todo.rasc.ch'
};

environment.prod.ts

Then I build the application with ng build and copy the files in the dist/app/browser directory to the server.


Upload to Server

# From your local machine, upload the built files
scp -r dist/app/browser/* todo@your-server-ip:/home/todo/www/

# Or using rsync
rsync -avz --delete dist/app/browser/ todo@your-server-ip:/home/todo/www/

4. Configure Caddy

Create Caddyfile

In this step, we create the Caddy configuration file to serve the Angular application and reverse proxy requests to the PocketBase API.

sudo nano /etc/caddy/Caddyfile

Add the following configuration:

# PocketTodo Application
todo.rasc.ch {
        # Serve Angular static files
        root * /home/todo/www
        file_server

        # Enable gzip compression
        encode gzip

        # Security headers
         header {
                # HSTS
                Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
                # Prevent clickjacking
                X-Frame-Options "DENY"
                # Prevent MIME type sniffing
                X-Content-Type-Options "nosniff"
                # XSS Protection
                X-XSS-Protection "1; mode=block"
                # Content Security Policy
                Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self'; connect-src 'self' https://todo.rasc.ch/api;"
        }

        reverse_proxy /_/* 127.0.0.1:9999 {
                transport http {
                        read_timeout 360s
                }
        }

        # Handle PocketBase API requests
        reverse_proxy /api/* 127.0.0.1:9999 {
                transport http {
                        read_timeout 360s
                }
        }

        # Cache static assets
        @static {
                file
                path *.js *.css *.png *.jpg *.jpeg *.gif *.ico *.svg *.woff *.woff2
        }
        header @static Cache-Control "public, max-age=31536000"

        # Logging
        log {
                output file /var/log/caddy/todo.rasc.ch.log
        }
}

Test and Reload Caddy Configuration

# Test configuration
sudo caddy validate --config /etc/caddy/Caddyfile

# Reload Caddy
sudo systemctl stop caddy
sudo systemctl start caddy

# Check Caddy status
sudo systemctl status caddy

# View Caddy logs
sudo journalctl -u caddy -f

5. Configure PocketBase

Copy Migrations

Copy the migration files from the pb_migrations directory of your local PocketBase installation to the server.

scp -r /path/to/local/pocketbase/pb_migrations/* todo@your-server-ip:/home/todo/pocketbase/pb_migrations/

Now start PocketBase.

sudo systemctl start pocketbase

# Check status
sudo systemctl status pocketbase

# View logs
sudo journalctl -u pocketbase -f

Setup Admin Account

In this step, we create the admin account for PocketBase.

cd /home/todo/pocketbase
./pocketbase superuser create EMAIL PASS

Alternatively, you can also do this via the web interface when opening the PocketBase admin dashboard. You find the URL for this functionality in the systemd log file:

sudo journalctl -u pocketbase -f

Configure Settings

Open the PocketBase admin dashboard and log in with the admin account you just created. Under Settings > Application, set the application name and URL.

Under "User IP proxy headers", enter the header name that Caddy uses to forward the user's IP address to PocketBase.

Under Settings > Mail settings, configure the SMTP settings to enable email functionality.

I recommend enabling rate limiting. PocketBase already configured default rate limit rules, but it's not enabled by default. Check them if they fit your needs and enable them.


Cleanup

After the deployment is successful, I recommend removing access to the PocketBase admin dashboard. In the Caddyfile, comment out or remove the reverse_proxy /_/* 127.0.0.1:9999 { block.

When you comment it out, you can enable it again later if you need to access the admin dashboard for maintenance or configuration changes.

Validate the configuration and reload it after you have made the changes in the Caddyfile:

sudo caddy validate --config /etc/caddy/Caddyfile
sudo systemctl reload caddy

6. Log Rotation

It is also recommended to set up log rotation for the Caddy logs to prevent them from growing indefinitely.

sudo nano /etc/logrotate.d/todo-app

Insert the following configuration.

/var/log/caddy/todo.rasc.ch.log {
    daily
    rotate 30
    compress
    delaycompress
    missingok
    create 0644 caddy caddy
    postrotate
        systemctl reload caddy
    endscript
}

7. Backup

Backups are crucial for a production application. You should regularly back up the PocketBase database.

Open the admin dashboard. In my case, it is https://todo.rasc.ch/_/. Then open the Settings > Backups menu and enable auto backups by entering a cron expression and the number of backups to keep.

I also enabled the "Store backups in S3 storage" option, which stores the backup in an S3-compatible storage away from the server. This is a good practice to ensure that backups are not lost if the server fails. When using the S3 option, it is recommended to use a separate bucket only for backups. The generated backup represents a full snapshot of your pb_data directory, including the locally stored uploaded files as Zip archive. During the backup's Zip generation, the application will be temporarily set in read-only mode.

8. Deployment Updates

Updating the application is straightforward. Unless your client application uses features that require a new PocketBase version, you can update them separately. If there are breaking changes in the PocketBase API, you may need to stop the PocketBase service, update the client application, update the PocketBase binary, and then start PocketBase again.


Update Angular Application

# Build new version locally
npm run build

# Upload to server
rsync -avz --delete dist/ todo@your-server-ip:/home/todo/www/

Updating just the Angular application does not require any service restarts, as Caddy serves static files directly.


Update PocketBase

Run these commands as the todo user. I recommend backing up the current PocketBase binary and pb_data directory. If something goes wrong, you can immediately go back to the previous version.

sudo su - todo
cd /home/todo/pocketbase

# Stop service
sudo systemctl stop pocketbase

# Backup current version
cp pocketbase pocketbase.backup
cp -r pb_data pb_data.backup

# Update PocketBase binary
./pocketbase update

# Start service
sudo systemctl start pocketbase

# Check status
sudo systemctl status pocketbase

# If okay remove old backup files
rm pocketbase.backup
rm -r pb_data.backup

Conclusion

This concludes this two-part blog post on how to develop and deploy a JavaScript application with PocketBase as the backend. The application is online and can be accessed at https://todo.rasc.ch.

I hope you found it helpful and learned something new. If you have any questions or suggestions, feel free to send me a message.