How to Self-Host PrivateBin with Docker Compose

What Is PrivateBin?

PrivateBin is a self-hosted, zero-knowledge encrypted pastebin. Paste text, code, or Markdown, set an expiration time, and share the link. The encryption key is part of the URL fragment (after the #) — it never reaches the server. The server stores only encrypted blobs and cannot read your content. It replaces Pastebin.com, GitHub Gists (for temporary sharing), and similar services — with the guarantee that your server admin can’t read your pastes.

Prerequisites

  • A Linux server (Ubuntu 22.04+ recommended)
  • Docker and Docker Compose installed (guide)
  • 128 MB of free RAM
  • 200 MB of free disk space
  • A domain name (optional, for remote access)

Docker Compose Configuration

Create a docker-compose.yml file:

services:
  privatebin:
    image: privatebin/nginx-fpm-alpine:2.0.3
    container_name: privatebin
    restart: unless-stopped
    ports:
      - "8080:8080"
    volumes:
      - privatebin_data:/srv/data
    read_only: true
    tmpfs:
      - /tmp
      - /var/tmp
      - /run
    healthcheck:
      test: ["CMD-SHELL", "curl -f http://localhost:8080/ || exit 1"]
      interval: 30s
      timeout: 10s
      retries: 3

volumes:
  privatebin_data:

Start the stack:

docker compose up -d

Initial Setup

  1. Open http://your-server-ip:8080 in your browser
  2. PrivateBin is immediately ready — no accounts, no setup wizard
  3. Paste text, select options (expiration, format, burn-after-reading), and create
  4. Share the generated link — the encryption key is in the URL fragment

Configuration

Custom Configuration

Create a conf.php configuration file:

[main]
name = "My PrivateBin"
basepath = "https://paste.example.com/"
discussion = true
opendiscussion = false
password = true
fileupload = true
burnafterreadingselected = false
defaultformatter = "plaintext"
sizelimit = 10485760

[expire]
default = "1week"

[expire_options]
5min = 300
10min = 600
1hour = 3600
1day = 86400
1week = 604800
1month = 2592000
1year = 31536000
never = 0

[traffic]
limit = 10
header = "X-Forwarded-For"

[purge]
limit = 300
batchsize = 10

Mount it:

volumes:
  - privatebin_data:/srv/data
  - ./conf.php:/srv/cfg/conf.php:ro

Key Options

SettingDefaultDescription
discussiontrueAllow comments on pastes
passwordtrueAllow password-protected pastes
fileuploadfalseAllow encrypted file attachments
sizelimit10485760Max paste size in bytes (10 MB)
burnafterreadingselectedfalsePre-select burn after reading

File Uploads

Enable encrypted file uploads by setting fileupload = true in the config. Files are encrypted client-side just like text.

Storage Backends

PrivateBin supports multiple storage backends. In v2.x, you must use the updated class names:

; Filesystem (default required in v2.x config)
[model]
class = Filesystem

; Google Cloud Storage
[model]
class = GoogleCloudStorage

; S3-compatible storage (new in v2.x)
[model]
class = S3Storage

The default filesystem storage works well for most deployments. If upgrading from v1.x: Legacy class names (privatebin_data, privatebin_db, zerobin_db) are removed in v2.x. Update your config to use Filesystem or Database.

Reverse Proxy

Nginx Proxy Manager config:

  • Scheme: http
  • Forward Hostname: privatebin
  • Forward Port: 8080

If using the traffic limiter, set header = "X-Forwarded-For" in the config so rate limiting works on real client IPs.

See Reverse Proxy Setup for full configuration.

Backup

docker run --rm -v privatebin_data:/data -v $(pwd):/backup alpine \
  tar czf /backup/privatebin-backup-$(date +%Y%m%d).tar.gz /data

Note: Backups contain encrypted data that cannot be read without the URL fragments (which only the users have).

See Backup Strategy for a complete backup approach.

Troubleshooting

”Error saving paste” on Large Content

Symptom: Saving large pastes fails with an error. Fix: Increase sizelimit in the configuration. Also check your reverse proxy’s client_max_body_size setting.

Rate Limiting Blocking Users

Symptom: Users get “Please wait X seconds” errors too frequently. Fix: Increase limit under [traffic] in the config (default 10 seconds between pastes). Set header = "X-Forwarded-For" behind a reverse proxy to avoid rate limiting all users as one IP.

Pastes Disappearing Faster Than Expected

Symptom: Pastes expire sooner than the selected time. Fix: Check the [purge] settings. limit = 300 means purge runs every 300 seconds (5 minutes). Verify the paste expiration was set correctly — “burn after reading” destroys on first view.

Resource Requirements

  • RAM: ~30-50 MB
  • CPU: Negligible — all encryption happens client-side
  • Disk: ~50 MB for the application, paste storage varies

Frequently Asked Questions

Can the server admin read the pastes?

No. PrivateBin uses client-side encryption — the encryption key is part of the URL fragment (after #) which is never sent to the server. The server stores only encrypted data it cannot decrypt. Even a compromised server cannot read paste contents.

Does PrivateBin support file uploads?

Yes. Set fileupload = true in the configuration. Files are encrypted client-side just like text pastes. File size is limited by the sizelimit setting (default 10 MB).

What happens when a paste expires?

Expired pastes are deleted during the next purge cycle (configurable via the [purge] section in config). With “burn after reading” enabled, the paste is destroyed after the first view — regardless of expiration time.

Can I use PrivateBin with a database instead of the filesystem?

Yes. PrivateBin supports filesystem storage (default), MySQL/MariaDB, PostgreSQL, and S3-compatible storage. Set the [model] class in your config accordingly. Filesystem works fine for most deployments.

Is PrivateBin compatible with the Pastebin.com API?

No. PrivateBin has its own API that differs from Pastebin.com’s. There are command-line tools and browser extensions that work with PrivateBin’s API (e.g., pbincli for Python).

Verdict

PrivateBin is the best self-hosted pastebin. The zero-knowledge encryption means your server genuinely cannot read the pasted content — even if compromised. Setup is dead simple, resource usage is minimal, and the read-only container with tmpfs is security-hardened out of the box. For sharing code snippets or text securely, nothing else comes close.

Comments