Pr
Security Azure

PrivateBin on Ubuntu 24.04 on Azure User Guide

| Product: PrivateBin on Ubuntu 24.04 LTS on Azure

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 URL fragment 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 disk so your encrypted pastes are captured into the image and re provisioned on every VM. 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 Azure data disk 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 (PrivateBin has no credentials to rotate)
  • A static /healthz endpoint for Azure Load Balancer health probes, with the config directory blocked from the web
  • 24/7 cloudimg support

Prerequisites

An active Azure subscription, an SSH key pair, and a VNet plus subnet in the target region. Standard_B2s (2 vCPU / 4 GiB RAM) is a good starting point. NSG inbound: allow 22/tcp from your management network and 80/tcp 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 localhost and HTTPS both provide).

Step 1 - Deploy from the Azure Marketplace

Sign in to the Azure Portal, choose Create a resource, search the Marketplace for PrivateBin by cloudimg, and select Create. On Basics pick your subscription, resource group, region and size; under Administrator account choose SSH public key and paste your key; under Inbound port rules allow SSH (22) and HTTP (80). Then Review + create and Create.

Step 2 - Deploy from the Azure CLI

az vm create \
  --resource-group <your-rg> \
  --name privatebin \
  --image <marketplace-image-urn> \
  --size Standard_B2s \
  --admin-username azureuser \
  --ssh-key-values ~/.ssh/id_ed25519.pub \
  --vnet-name <your-vnet> --subnet <your-subnet> \
  --public-ip-sku Standard

az vm open-port --resource-group <your-rg> --name privatebin --port 80 --priority 1010

Step 3 - Connect to your VM

ssh azureuser@<vm-public-ip>

Step 4 - 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 VM info note. Confirm they are healthy:

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

All three report active.

nginx, php8.3-fpm and privatebin-firstboot services active and nginx listening on port 80

Step 5 - 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.

PrivateBin 2.0.5 on PHP 8.3, healthz and app returning 200, and cfg/conf.php denied with 403

Step 6 - The encrypted paste store on a dedicated data disk

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

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

The data disk is mounted at /var/lib/privatebin-data and the PrivateBin config points its storage dir at it.

The PrivateBin data disk mounted at /var/lib/privatebin-data and the config storage dir

The info note is not a credential; PrivateBin has no login. It simply records the URL to open in a browser.

The non secret PrivateBin info note recording the app URL for this VM

Step 7 - Open PrivateBin and start a new paste

Browse to http://<vm-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 8 - 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 9 - 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 and the Copy link, Email and QR code actions

Step 10 - Read a paste back

Opening the full link in any browser fetches the ciphertext and decrypts it locally using the key from the URL fragment. The recipient sees the original content along with an expiry notice, and can Clone, download the raw text, or discuss it if you enabled comments.

A recipient opening the paste link and reading the decrypted content

Step 11 - 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 disk. Back up that directory (for example with an Azure disk 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 NSG. 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 Microsoft Azure. cloudimg is not affiliated with, endorsed by, or sponsored by the PrivateBin project.