Security AWS

PrivateBin Zero-Knowledge Pastebin on AWS User Guide

| Product: PrivateBin

Overview

PrivateBin is a minimalist, open source pastebin where the server has zero knowledge of the data it stores. Every paste is encrypted and decrypted entirely in your browser using 256-bit AES-GCM; the decryption key lives only in the fragment of the share link and is never sent to the server, so the server only ever holds ciphertext. The cloudimg image serves PrivateBin over nginx and PHP-FPM on port 80, with the paste store on a dedicated data volume so your encrypted pastes are captured into the AMI and re-provisioned on every instance. There is no database and, by design, no login or admin account. Built from PrivateBin 2.0.5, backed by 24/7 cloudimg support.

What is included:

  • PrivateBin 2.0.5 served from /var/www/privatebin by nginx on :80 in front of PHP 8.3 FPM
  • The Filesystem storage model pointed at a dedicated EBS data volume mounted at /var/lib/privatebin-data
  • nginx.service + php8.3-fpm.service as systemd units, enabled and active on every boot
  • A first-boot service that writes an MOTD banner and a non-secret info note recording the access URL for this instance (PrivateBin has no credentials to rotate)
  • A static /healthz endpoint for load-balancer health checks, with the config directory blocked from the web
  • 24/7 cloudimg support

Prerequisites

An AWS account, an EC2 key pair in the target region, and a VPC with a public subnet. m5.large (2 vCPU / 8 GiB RAM) is the recommended instance type. Security group inbound: allow 22/tcp from your management network and 80/tcp (and 443/tcp if you terminate TLS) for the web interface. PrivateBin serves plain HTTP on port 80; for production, terminate TLS in front of it with your own domain so the site runs over HTTPS (the browser Web Crypto that PrivateBin relies on requires a secure context, which both localhost and HTTPS provide).

Step 1 - Launch the AMI

Subscribe to the listing in AWS Marketplace, choose Continue to Launch, and launch through the EC2 console or the AWS CLI. Select the m5.large instance type, your key pair, and a security group that opens ports 22 and 80 (and 443 for TLS).

aws ec2 run-instances \
  --image-id <ami-id> \
  --instance-type m5.large \
  --key-name <your-key-pair> \
  --security-group-ids <security-group-id> \
  --subnet-id <subnet-id> \
  --tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=privatebin}]'

Step 2 - Connect to your instance

Connect over SSH as the default login user for the OS variant you launched. This listing may ship more than one OS variant; use the matching login user below.

OS variant SSH login user Connect
Ubuntu 24.04 LTS ubuntu ssh ubuntu@<instance-public-ip>

Step 3 - Confirm the services are running

PrivateBin is served by nginx in front of PHP-FPM, with a one-shot first-boot service that writes the per-instance info note. Confirm they are healthy:

systemctl is-active nginx.service php8.3-fpm.service privatebin-firstboot.service

All three report active.

Step 4 - Verify the stack and endpoints

Confirm the PrivateBin version, the PHP runtime, the health endpoint, the app itself, and that the configuration file is never web-reachable:

grep -m1 'const VERSION' /var/www/privatebin/lib/Controller.php
php -r 'echo "PHP " . PHP_VERSION . "\n";'
curl -s -o /dev/null -w 'healthz: HTTP %{http_code}\n' http://127.0.0.1/healthz
curl -s -o /dev/null -w 'PrivateBin app: HTTP %{http_code}\n' http://127.0.0.1/
curl -s -o /dev/null -w 'cfg/conf.php: HTTP %{http_code} (denied)\n' http://127.0.0.1/cfg/conf.php

The app and /healthz return 200, while cfg/conf.php returns 403 because nginx blocks the config directory.

Step 5 - The encrypted paste store on a dedicated data volume

Every paste is stored as ciphertext under /var/lib/privatebin-data, a dedicated EBS data volume mounted separately from the OS disk and outside the web root. A non-secret info note records the app URL for this instance:

findmnt -no SOURCE,TARGET,FSTYPE /var/lib/privatebin-data
sudo grep -m1 'dir =' /var/www/privatebin/cfg/conf.php
sudo cat /root/privatebin-credentials.txt

The data volume is mounted at /var/lib/privatebin-data and the PrivateBin config points its storage dir at it. The info note is not a credential; PrivateBin has no login. It simply records the URL to open in a browser.

Step 6 - Open PrivateBin and start a new paste

Browse to http://<instance-public-ip>/ in any modern browser (or use your own HTTPS domain in production). The New Paste editor loads with the toolbar across the top: an expiry selector, Burn after reading, Open discussion, an optional password, and a format selector. There is no sign-in because the server never holds a decryption key.

The PrivateBin New Paste editor with the toolbar and options

Step 7 - Compose a paste

Type or paste your content into the editor. Choose how long it should live from Expires (from 5 minutes to never), pick a Format (Plain Text, Source Code with syntax highlighting, or Markdown), and optionally tick Burn after reading so the paste is destroyed the moment it is first opened, or set a Password for a second decryption factor.

Composing a paste with the expiry set to one week and the format set to Markdown

Step 8 - Create and share the encrypted link

Click Create. PrivateBin encrypts the content in your browser, sends only the ciphertext to the server, and shows you a shareable link. The part of the link after the # is the decryption key: it stays in your browser and is never transmitted, so anyone with the full link can read the paste but the server never can. Use Copy link, Email, or QR code to share it.

A created encrypted paste showing the shareable link with the decryption key and the Copy link, Email and QR code actions

Step 9 - Share by QR code

The QR code action renders the full share link, decryption key included, as a QR code so a recipient can open the encrypted paste on a phone without retyping the link.

The QR code dialog rendering the full share link for the encrypted paste

Step 10 - Create a paste from the command line

PrivateBin also has a JSON API, so you can create pastes from scripts. The following creates a paste and reads it back, proving the round trip end to end (the payload is opaque ciphertext, exactly what a browser would send):

IV="$(openssl rand -base64 16 | tr -d '\n')"; SALT="$(openssl rand -base64 8 | tr -d '\n')"; CT="$(openssl rand -base64 48 | tr -d '\n')"
REQ="$(printf '{"v":2,"adata":[["%s","%s",100000,256,128,"aes","gcm","none"],"plaintext",0,0],"ct":"%s","meta":{"expire":"5min"}}' "$IV" "$SALT" "$CT")"
CREATE="$(curl -s -H 'X-Requested-With: JSONHttpRequest' -H 'Content-Type: application/json' --data-binary "$REQ" http://127.0.0.1/)"
PID="$(printf '%s' "$CREATE" | sed -n 's/.*"id":"\([0-9a-f]*\)".*/\1/p')"
READ="$(curl -s -H 'X-Requested-With: JSONHttpRequest' "http://127.0.0.1/?$PID")"
case "$CREATE$READ" in *'"status":0'*'"ct":'*) echo "OK - created and read paste $PID" ;; *) echo "FAILED"; exit 1 ;; esac

It prints OK - created and read paste <id>. Note the server stores only the ciphertext you send; a real client encrypts the content and keeps the key in the URL fragment.

Maintenance

  • Data: encrypted pastes live under /var/lib/privatebin-data on the dedicated data volume. Back up that directory (for example with an EBS snapshot) to preserve pastes. Expired and burn-after-reading pastes are purged automatically.
  • Traffic limiter: this appliance ships with PrivateBin's IP-based traffic limiter disabled (limit = 0 in /var/www/privatebin/cfg/conf.php) because it is single-tenant behind your security group. To rate-limit posting again, set limit = 10 and reload nginx.
  • File upload: file attachments are off by default. To enable them, set fileupload = true under [main] in /var/www/privatebin/cfg/conf.php.
  • TLS: PrivateBin serves plain HTTP on port 80. Front it with TLS (for example certbot) and your own domain before production use, so the browser runs in a secure context over HTTPS.
  • Restart: sudo systemctl restart nginx.service php8.3-fpm.service if you need to bounce the web stack.
  • Security patches: unattended-upgrades remains enabled so the OS continues to receive security updates automatically.

Support

cloudimg provides 24/7 expert support for this image. Contact support@cloudimg.co.uk.

This image packages the open source PrivateBin software (Zlib/libpng License) for convenient deployment on AWS. cloudimg is not affiliated with, endorsed by, or sponsored by the PrivateBin project.