PrivateBin – Your Self-Hosted Pastebin Instance

Raspberry Pi - PrivateBin

A Pastebin is a quick and handy way to share logs, debug output, etc. They are usually used in support situations. There are quite several public Pastebin services out there, with the most known being But they all have one issue, privacy. You have to trust the hoster with your data. And logs can reveal a lot of information.

Wouldn’t it be nice to host such a service yourself? PrivateBin to the rescue!

This article will showcase how easy it is to host your own PrivateBin instance. Some assumptions are being made here of course, such as that you have a server to run your PrivateBin instance, your own domain to go with and are confident to get SSL certificates to work because it’s 2022 and there is no reason not to have your website not running with HTTPS.

In this article, I walk you through both, bare-metal installation and docker-compose.

Bare-Metal Installation


To run PrivateBin you need PHP installed. According to the official documentation of PrivateBin, PHP 7.0 at least, but again, it’s 2022, so best go with a more recent version here. I recommend using PHP 8.1, you can have a quick read-up here on how to install PHP 8.1 on Debian-based systems such as your Raspberry Pi for example.

Furthermore, you need some PHP extensions, such as:

  • GD extension
  • zlib extension

You also need some disk space or a database supported by PDO.

Install PrivateBin

The installation process is pretty straightforward.

Download the latest release from the release archive (It’s the link labelled “Source Code (…)”) and extract it in the folder you want to install your PrivateBin instance. For example /var/www/privatebin/. Ensure the files and folders are owned by the `www-data` user so your webserver can read and write there.

Configure PrivateBin

An example configuration is provided in cfg/conf.sample.php which you can copy to cfg/conf.php. The file’s content is well documented and explained in the file itself, change it to your needs. Important to know is that basepath is mandatory to configure with the URL of your PrivateBin instance, including the full protocol (`https://`) and it needs to have a trailing slash.

Configure the Webserver

I use Nginx for this, which I will cover here. For Apache2 users, I’m pretty sure you know how to write a vhost file pointing at a directory. There is no rocket science involved in this one, just have it pointing to the right directory.

server {
    listen 80;
    listen [::]:80;


    # Redirect all HTTP requests to HTTPS with a 301 Moved Permanently response.
    return 301 https://$host$request_uri;

server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;


    ssl_certificate         /certificates/;
    ssl_certificate_key     /certificates/;

    include /etc/nginx/options-ssl-nginx.conf;

    gzip on;
    access_log  /var/log/nginx/;
    error_log   /var/log/nginx/  warn;

    root /var/www/pastebin;
    index index.php index.html index.htm index.nginx-debian.html;

    location / {
        try_files $uri $uri/ =404;

    # In case you are running PHP-FPM, uncomment the following lines
#    location ~ .php$ {
#        include snippets/fastcgi-php.conf;
#        fastcgi_pass unix:/var/run/php/php-fpm.sock;
#    }

Docker-Compose Installation


Same as for the bare-metal installation, we need to install some software packages, this time to run docker images. Let’s dive right into it and install docker and docker-compose.

sudo apt install docker-compose

That’s all the software you need to install. The next step is to prepare the docker-compose file so Docker knows what to do.

The Docker-Compose File

This file is pretty much a description file for Docker and holds information about what to download, how to name the container and so on. I’m not going into too much detail here, when you chose a Docker install you probably know what this is all about.

So, pick a nice spot on your favourite drive, I like to have my docker stuff in ~/docker/ for example, so it’s nice and easy to find, and create a file called docker-compose.yml and paste the following in.

version: "3.7"

    image: privatebin/nginx-fpm-alpine:latest
    container_name: privatebin
    read_only: true
      - PUID=1000
      - PGID=1000
      - TZ=Etc/UTC
      - './privatebin-data:/srv/data' # data volume for pastes
      - './conf.php:/srv/cfg/conf.php:ro' # second volume for custom configuration file
      - 3007:8080
    restart: always

Of course, you can set port 3007 to whatever you like, just make sure you remember what you set it to and that no other service is already listening in this port.

Before we start up the Docker volume, one additional file needs to be created, conf.php. This file holds the configuration for your PrivateBin instance. To do so, simply download the configuration example from the official PrivateBin GitHub repository.

wget -O conf.php

This is your configuration file, you definitely should have a look through it right about now and set things up accordingly.

Nginx as Reverse Proxy

Next on the list of things to do is to set up the Nginx reverse proxy. Create a new vhost file and paste in the following.

server {
    listen 80;
    listen [::]:80;


    # Redirect all HTTP requests to HTTPS with a 301 Moved Permanently response.
    return 301 https://$host$request_uri;

server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;

    ssl_certificate         /certificates/;
    ssl_certificate_key     /certificates/;

    include /etc/nginx/options-ssl-nginx.conf;

    add_header          X-Xss-Protection            "1; mode=block" always;
    add_header          X-Content-Type-Options      "nosniff" always;
    add_header          Strict-Transport-Security   "max-age=15552000; preload" always;
    add_header          X-Frame-Options             "SAMEORIGIN" always;
    add_header          'Referrer-Policy'           'origin-when-cross-origin';
    add_header          Content-Security-Policy     "frame-ancestors;";
    add_header          X-XSS-Protection            "1; mode=block" always;

    proxy_hide_header   X-Powered-By;
    proxy_buffering     off;                        ## Sends data as fast as it can not buffering large chunks.

    gzip on;
    access_log  /var/log/nginx/;
    error_log   /var/log/nginx/  warn;

    location / {
        proxy_set_header   X-Real-IP $remote_addr;
        proxy_set_header   X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header   Host $host;
        proxy_pass         http://localhost:3007/;
        proxy_http_version 1.1;
        proxy_set_header   Upgrade $http_upgrade;
        proxy_set_header   Connection "upgrade";

As you can see in line 41, this is where you need the port from your docker-compose.yml file.

One thing you have to keep in mind, PasteBin will only run with SSL, so make sure you have that Let’s Encrypt certificate configured.

Start the Show

When you have saved your vhost file and activated it, go back to your docker directory and run the following command.

docker-compose up -d

If everything is correct, you should now be able to access your new PrivateBin instance in your browser.

Fix Permissions

(This only applies when you have selected file storage for your pastes in your conf.php.)

Unfortunately, you cannot yet save pastes, there are still some permissions that need to be fixed. It’s a bit fiddling around, but not that much.

First, make sure the privatebin-data directory is globally writeable. Don’t worry, we change that again, we just need the Docker instance to be able to write into it, so we can see what user and group it’s using.

sudo chmod 777 privatebin-data/

Now open your PrivateBin website and create a paste. This will now write some data into the directory and you can check for the user and group.

ls -rtlh privatebin-data/

For me, this was the result:

» sudo ll privatebin-data/
total 32K
drwx------ 3 nobody 82 4.0K May 13 21:12 3c
drwx------ 3 nobody 82 4.0K May 13 21:08 4f
drwx------ 3 nobody 82 4.0K May 13 21:02 66
drwx------ 3 nobody 82 4.0K May 13 20:59 86
drwx------ 3 nobody 82 4.0K May 13 21:00 ae
-rw-r----- 1 nobody 82   45 May 13 21:12 purge_limiter.php
-rw-r----- 1 nobody 82  522 May 13 20:59 salt.php
-rw-r----- 1 nobody 82  132 May 13 21:12 traffic_limiter.php

As you can see, the user is nobody and the group is 82 in my case. So the next step will be to fix the permissions for the privatebin-data directory.

sudo chown -R nobody:82 privatebin-data/
sudo chmod 700 privatebin-data/

This should take care of the permission issue and PrivateBin should now be able to write into the data directory without issues and without it being globally writeable.

That’s it, have fun with your own Pastebin service!


Leave a Reply

Your email address will not be published. Required fields are marked *