Self-Hosting
Self-hosting gives you full control over your data, no rate limits, and the ability to customize the server. The default Docker stack includes Redis (event buffer), a UI sidecar (GHCR UI assets), and the PizzaPi server (relay API + web UI).
Quickest: pizza web
Section titled “Quickest: pizza web”The fastest way to self-host is with a single command. No cloning, no config files — just Docker.
pizzapi webThis automatically:
- Clones the PizzaPi repo (if not already present) to
~/.pizzapi/web/repo - Generates required secrets (including
BETTER_AUTH_SECRETand VAPID keys) and a Docker Compose file in~/.pizzapi/web/ - Uses the GHCR UI image by default — or the local dev stack (
bun run dev/dev:ui) when you run--dev-uifrom a local checkout (UI on port 5173) - Builds and starts Redis + the relay server
- Serves the web UI at http://localhost:7492
On subsequent runs, it pulls the latest repo changes and refreshes the configured UI image tag before startup.
# Custom port (persisted for future runs)pizzapi web --port 8080
# Set extra CORS origins (persisted)pizzapi web --origins "https://example.com"
# Pull a specific GHCR UI tagpizzapi web --tag main
# Build the UI locally (fallback mode)pizzapi web --build
# Run the local dev UI stack (repo checkout only)pizzapi web --dev-ui
# Run in the foregroundpizzapi web --foreground
# View / change config without startingpizzapi web configpizzapi web config set port 9000pizzapi web config set extraOrigins "https://example.com"
# Managementpizzapi web logs # Tail container logspizzapi web status # Show running containerspizzapi web stop # Stop the hubAll settings are persisted in ~/.pizzapi/web/config.json and applied on every start. CLI flags like --port and --origins update the config automatically — you only need to pass them once.
Persistent data (SQLite database) is stored in ~/.pizzapi/web/data/.
Manual Docker Compose
Section titled “Manual Docker Compose”-
Clone the repository
Terminal window git clone https://github.com/Pizzaface/PizzaPi.gitcd PizzaPi -
Start the stack
Terminal window docker compose -f docker/compose.yml up -dThis starts:
Service Port Description redis6379 Session event buffer (internal) ui— Sidecar that syncs the GHCR UI assets into a shared volume server7492 Relay API + Web UI -
Open the web UI
Navigate to http://localhost:7492 — you’ll see the PizzaPi interface.
-
Create your account
The first time you visit, register an account. Then run
pizzapi setupon your dev machine pointing tohttp://localhost:7492.
Environment Variables
Section titled “Environment Variables”Configure the server by setting environment variables in Docker Compose or your shell:
| Variable | Default | Description |
|---|---|---|
PORT | 7492 | HTTP/WebSocket listen port |
PIZZAPI_REDIS_URL | — | Redis connection URL, e.g. redis://localhost:6379 |
PIZZAPI_BASE_URL | http://localhost:5173 | Public URL of the UI, added to CORS trusted origins |
PIZZAPI_EXTRA_ORIGINS | — | Comma-separated extra trusted origins (e.g. a Tailscale URL). Added to CORS and WebSocket auth without hardcoding hostnames. Example: https://myhost.ts.net,http://myhost.ts.net:5173 |
See the Environment Variables reference for the full list.
Custom docker/compose.yml overrides
Section titled “Custom docker/compose.yml overrides”# docker/compose.override.yml (not committed — create locally)services: server: environment: PORT: "8080" PIZZAPI_REDIS_URL: redis://redis:6379 ports: - "8080:8080"Development Stack
Section titled “Development Stack”Hot-reloading dev server for contributors:
# Start both the API server and Vite UI dev serverdocker compose -f docker/compose.yml --profile dev up| Service | Port | Description |
|---|---|---|
redis | 6379 | Redis |
server (dev) | 7492 | Bun dev server (hot-reload) |
ui (dev) | 5173 | Vite UI dev server |
Or run without Docker:
# Prerequisites: Bun, Redis running locallybun installbun run dev# API → http://localhost:7492# UI → http://localhost:5173Running Without Docker
Section titled “Running Without Docker”If you prefer not to use Docker:
-
Install Bun
Terminal window curl -fsSL https://bun.sh/install | bash -
Start Redis
Terminal window # macOS via Homebrewbrew install redis && brew services start redis# Ubuntu/Debiansudo apt install redis-server && sudo systemctl start redis -
Install dependencies and build
Terminal window bun installbun run build -
Run the server
Terminal window cd packages/serverPIZZAPI_REDIS_URL=redis://localhost:6379 bun run start
Database
Section titled “Database”The server uses SQLite (via Kysely) for user accounts and session metadata. The database file lives at packages/server/auth.db.
Running Migrations
Section titled “Running Migrations”After upgrading PizzaPi, always run migrations before starting the server:
bun run migrateBackup
Section titled “Backup”# Simple backup — safe to copy while the server is stoppedcp packages/server/auth.db packages/server/auth.db.backupReverse Proxy Configuration
Section titled “Reverse Proxy Configuration”When PizzaPi runs behind a reverse proxy (like Caddy, nginx, or Traefik in Docker), the server needs to trust proxy headers to correctly identify the real client IP for rate limiting and other security features.
Loopback Proxies (Auto-Detected)
Section titled “Loopback Proxies (Auto-Detected)”If your reverse proxy is on the same host and connects to PizzaPi via loopback (127.0.0.1) — for example, a system-installed nginx or Caddy forwarding to localhost:7492 — no configuration is needed. The server auto-detects this and safely trusts X-Forwarded-For headers.
Non-Loopback Proxies
Section titled “Non-Loopback Proxies”If your reverse proxy reaches PizzaPi via a non-loopback address — including any Docker Compose setup, a Docker overlay network, or a cloud load balancer — set PIZZAPI_TRUST_PROXY=true.
The simplest approach is to pass the variable inline — it will be forwarded to the Docker container:
PIZZAPI_TRUST_PROXY=true pizzapi webAlternatively, add it to docker/compose.override.yml and pass both compose files explicitly (Docker only auto-merges compose.override.yml when you omit -f):
services: server: environment: PIZZAPI_TRUST_PROXY: "true"docker compose -f docker/compose.yml -f docker/compose.override.yml up -dMulti-Proxy Chains
Section titled “Multi-Proxy Chains”By default, PizzaPi uses the rightmost X-Forwarded-For entry — the IP appended by the directly-connected proxy. This is safe against client spoofing for single-proxy setups.
If you have multiple trusted proxy hops (e.g. CDN → nginx → PizzaPi), set PIZZAPI_PROXY_DEPTH to the number of intermediate hops between the outermost trusted proxy and the real client:
# CDN + local reverse proxy = 1 intermediate hopPIZZAPI_TRUST_PROXY=true PIZZAPI_PROXY_DEPTH=1 pizzapi webOr with both compose files explicitly (required when using -f):
# docker/compose.override.yml — CDN + local proxy = 1 intermediate hopservices: server: environment: PIZZAPI_TRUST_PROXY: "true" PIZZAPI_PROXY_DEPTH: "1"docker compose -f docker/compose.yml -f docker/compose.override.yml up -d| Depth | Topology | XFF entry used |
|---|---|---|
0 (default) | Single proxy (Caddy, nginx, Traefik) | Rightmost |
1 | CDN + local proxy (2 total hops) | Second from right |
N | N+1 total trusted hops | N positions from the right |
HTTPS with Caddy
Section titled “HTTPS with Caddy”Caddy automatically provisions TLS certificates via Let’s Encrypt:
relay.example.com { reverse_proxy localhost:7492}sudo systemctl reload caddyThen update your CLI config to use the secure URL:
pizzapi setup# Relay server URL: https://relay.example.comUpdating
Section titled “Updating”cd PizzaPigit pulldocker compose -f docker/compose.yml builddocker compose -f docker/compose.yml up -dAlways run bun run migrate if you’re running without Docker.
Security Hardening
Section titled “Security Hardening”PizzaPi includes several built-in security measures for production deployments.
Response Headers
Section titled “Response Headers”All server responses include security headers automatically:
X-Content-Type-Options: nosniff— prevents MIME-type sniffingX-Frame-Options: DENY— prevents clickjacking (tunnel responses useSAMEORIGINto allow same-origin iframing)X-XSS-Protection: 0— disables the legacy browser XSS filter (the recommended modern practice)Referrer-Policy: strict-origin-when-cross-origin— limits referrer leakagePermissions-Policy— restricts browser feature access
No configuration needed — these are applied to every response.
Request Body Size Limits
Section titled “Request Body Size Limits”The server enforces body size limits to prevent denial-of-service:
- API routes: 1 MB maximum
- Attachment uploads: 50 MB maximum (configurable via
PIZZAPI_ATTACHMENT_MAX_FILE_SIZE_BYTES)
Requests exceeding these limits receive a 413 Payload Too Large response.
Redis Health Monitoring
Section titled “Redis Health Monitoring”The /health endpoint reports Redis and Socket.IO connectivity status. When Redis becomes unavailable, the web UI shows a dismissable degraded-mode banner with automatic retry. Sessions continue operating with reduced functionality (no cross-node relay, no event persistence) until Redis recovers.
Web Push Notifications
Section titled “Web Push Notifications”The server supports browser push notifications for session events. Push is configured automatically — no additional setup required. Users can opt in from the web UI’s notification settings.