Files
shiftcraft/.claude/skills/pocketbase-best-practices/rules/deploy-scaling.md
2026-04-17 23:26:01 +00:00

3.4 KiB

title, impact, impactDescription, tags
title impact impactDescription tags
Tune OS and Runtime for PocketBase Scale MEDIUM Prevents file descriptor exhaustion, OOM kills, and exposes secure config for production deployments production, scaling, ulimit, gomemlimit, docker, encryption, deployment

Tune OS and Runtime for PocketBase Scale

Three low-effort OS/runtime knobs have outsized impact on production stability: open-file limits for realtime connections, Go memory limits for constrained hosts, and settings encryption for shared or externally-backed infrastructure. None of these are set automatically.

Incorrect (default OS limits, no memory governor, plain-text settings):

# Start without raising the file descriptor limit
/root/pb/pocketbase serve yourdomain.com
# → "Too many open files" once concurrent realtime connections exceed ~1024

# Start in a container that has a 512 MB RAM cap without GOMEMLIMIT
docker run -m 512m pocketbase serve ...
# → OOM kill during large file upload because Go GC doesn't respect cgroup limits

# Store SMTP password and S3 secret as plain JSON in pb_data/data.db
pocketbase serve  # no --encryptionEnv
# → Anyone who obtains the database backup can read all credentials

Correct:

# 1. Raise the open-file limit before starting (Linux/macOS)
#    Check current limit first:
ulimit -a | grep "open files"
#    Temporarily raise to 4096 for the current session:
ulimit -n 4096
/root/pb/pocketbase serve yourdomain.com

#    Or persist it via systemd (recommended for production):
# /lib/systemd/system/pocketbase.service
# [Service]
# LimitNOFILE = 4096
# ...

# 2. Cap Go's soft memory target on memory-constrained hosts
#    (instructs the GC to be more aggressive before the kernel OOM-kills the process)
GOMEMLIMIT=512MiB /root/pb/pocketbase serve yourdomain.com

# 3. Encrypt application settings at rest
#    Generate a random 32-character key once:
export PB_ENCRYPTION_KEY="z76NX9WWiB05UmQGxw367B6zM39T11fF"
#    Start with the env-var name (not the value) as the flag argument:
pocketbase serve --encryptionEnv=PB_ENCRYPTION_KEY

Docker deployment pattern (v0.36.8):

FROM alpine:latest
ARG PB_VERSION=0.36.8

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

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 bundle pre-written migrations or hooks:
# COPY ./pb_migrations /pb/pb_migrations
# COPY ./pb_hooks      /pb/pb_hooks

EXPOSE 8080

# Mount a volume at /pb/pb_data to persist data across container restarts
CMD ["/pb/pocketbase", "serve", "--http=0.0.0.0:8080"]
# docker-compose.yml
services:
  pocketbase:
    build: .
    ports:
      - "8080:8080"
    volumes:
      - pb_data:/pb/pb_data
    environment:
      GOMEMLIMIT: "512MiB"
      PB_ENCRYPTION_KEY: "${PB_ENCRYPTION_KEY}"
    command: ["/pb/pocketbase", "serve", "--http=0.0.0.0:8080", "--encryptionEnv=PB_ENCRYPTION_KEY"]
volumes:
  pb_data:

Quick-reference checklist:

Concern Fix
Too many open files errors ulimit -n 4096 (or LimitNOFILE=4096 in systemd)
OOM kill on constrained host GOMEMLIMIT=512MiB env var
Credentials visible in DB backup --encryptionEnv=YOUR_VAR with a 32-char random key
Persistent data in Docker Mount volume at /pb/pb_data

Reference: Going to production