Self-hosting¶
Docker images¶
| Registry | Image |
|---|---|
| Docker Hub | stackopshq/ghostbit |
| GHCR | ghcr.io/stackopshq/ghostbit |
Available tags: sha-<short-commit> (amd64), one per commit on main:
pin by digest for deployments. The moving tags (latest, edge, semver)
are frozen at the last multi-arch build and will move again once the
build farm regains arm64 support; do not use them for new installs.
Docker Compose (recommended)¶
SQLite¶
Redis¶
With a password:
The REDIS_PASSWORD variable is forwarded to both the Redis container (--requirepass) and the app (injected into the connection URL). If you connect to an external Redis that already has credentials in REDIS_URL, leave REDIS_PASSWORD unset.
Redis data is persisted via AOF + RDB snapshots on a named Docker volume.
HTTPS and Web Crypto¶
Ghostbit uses the Web Crypto API for all client-side encryption. Browsers only expose this API in a Secure Context, which means:
https://: always secure ✓http://localhostorhttp://127.0.0.1: granted by the browser for local development ✓http://on any other host: not a Secure Context, encryption is blocked ✗
Reverse proxy with HTTPS termination¶
The most common production setup (HTTPS on the proxy, plain HTTP internally) works perfectly:
The browser sees https://paste.example.com → Secure Context → Web Crypto works.
The internal HTTP leg between proxy and app is invisible to the browser.
Never expose the app directly over HTTP in production
If users can reach Ghostbit via http:// (bypassing the proxy), the browser will
refuse to encrypt and the app will show an error. Always ensure HTTPS is enforced
at the proxy level.
Nginx reverse proxy¶
HTTPS is required in production
crypto.subtle (Web Crypto API) requires a Secure Context.
Without HTTPS, the browser will refuse to encrypt pastes.
Caddy reverse proxy¶
Caddy handles HTTPS automatically via Let's Encrypt.
Data persistence¶
| Backend | Persistence |
|---|---|
| SQLite | Named Docker volume ghostbit_data mounted at /data |
| Redis | Named Docker volume redis_data, AOF + RDB snapshots |
Backup SQLite¶
Backup Redis¶
Updating¶
Privacy¶
- No IP addresses or User-Agent strings are logged by default: the Docker
image starts uvicorn with
--no-access-log. SettingACCESS_LOG=true(or running uvicorn yourself without--no-access-log) re-enables access lines that pair client IPs with paste IDs, see Configuration → Access logging - Paste IDs are
secrets.token_urlsafe(6): random, non-sequential - Burn-after-read fires only on API reads, not on HTML page loads
- The server never sees plaintext or passwords
- Backups (
scripts/backup.sh) are age-encrypted and pruned afterBACKUP_RETENTION_DAYS(30 by default) - The in-app
/privacynotice names your instance's controller once you setPRIVACY_OPERATOR,PRIVACY_CONTACT_URLandPRIVACY_AUTHORITY: required for a public GDPR/nLPD-compliant deployment. The full checklist lives in the README under "Compliant deployments (GDPR / nLPD)"