Fixing PrivateBin “Could not create document” in Docker

Googled777 avatar   
Googled777
This doctrine block documents a stubborn PrivateBin failure mode when running the privatebin/nginx-fpm-alpine image under Docker: every attempt to create a paste fails with:


Doctrine: Fixing PrivateBin “Could not create document” on Docker

Doctrine Block: Fixing PrivateBin “Could not create document” in Docker

This doctrine block documents a stubborn PrivateBin failure mode when running the privatebin/nginx-fpm-alpine image under Docker: every attempt to create a paste fails with:

Could not create document: Error saving document. Sorry.

The UI loads, encryption warnings may appear or not, but saving never works. The root cause is filesystem permissions and container user identity—specifically, PrivateBin running as nobody inside the container while the mounted data directory on the host is owned by a different user.

Goal: Make PrivateBin reliably save encrypted pastes using a Docker volume, with correct ownership and permissions, without exposing the directory as world-writable.

1. Baseline: Minimal Docker Compose for PrivateBin

Create a dedicated directory for PrivateBin on the host:

mkdir -p /srv/privatebin
cd /srv/privatebin

Place this docker-compose.yml in that directory:

version: "3.8"

services:
  privatebin:
    image: privatebin/nginx-fpm-alpine
    container_name: privatebin
    restart: unless-stopped
    ports:
      - "8090:8080"
    volumes:
      - ./data:/srv/data
      - ./cfg:/srv/cfg

Start the container:

docker compose up -d

After first start, the host directory should look like:

/srv/privatebin/
  docker-compose.yml
  data/
  cfg/

2. Providing a real conf.php (not the empty placeholder)

The image does not auto-generate a conf.php. The upstream repository ships a conf.sample.php that must be copied to conf.php.

Ensure you own the directory so you can write into it:

sudo chown -R "$USER":"$USER" /srv/privatebin

Then download the sample config as the real config:

curl -o /srv/privatebin/cfg/conf.php \
  https://raw.githubusercontent.com/PrivateBin/PrivateBin/master/cfg/conf.sample.php

Restart the container:

docker compose down
docker compose up -d

At this point, PrivateBin has a valid configuration file mounted from the host. If you still see “Could not create document”, the problem is not the config syntax but the filesystem permissions.


3. Symptom: “Could not create document: Error saving document. Sorry.”

When trying to create a paste, PrivateBin shows:

Could not create document: Error saving document. Sorry.

This almost always means: the application cannot write to its data directory. In this setup, that directory is:

/srv/data   (inside the container)
./data      (on the host, mounted into /srv/data)

On the host, you might see something like:

ls -ld /srv/privatebin/data
drwxr-xr-x 2 user user 4096 ... /srv/privatebin/data

From the host’s perspective, this looks fine. From inside the container, it is not—because the process writing files is not running as that host user.


4. Confirming the container’s runtime user

Enter the container shell:

docker exec -it privatebin sh

List running processes:

ps aux

You will see something like:

PID   USER     TIME  COMMAND
  1   nobody   ...   s6-svscan /run/services
 11   nobody   ...   php-fpm: master process (/etc/php84/php-fpm.conf)
 15   nobody   ...   php-fpm: pool www
 17   nobody   ...   nginx: master process /usr/sbin/nginx
 19   nobody   ...   nginx: worker process
 ...

This is the key insight: both nginx and php-fpm (and thus PrivateBin) are running as nobody inside the container. On Alpine, nobody typically maps to:

uid=65534(nobody) gid=65534(nogroup)

Confirm inside the container:

id nobody

5. Verifying write access from inside the container

Still inside the container shell, test writing to the data directory:

touch /srv/data/testfile
  • If this succeeds: the container user can write; the problem lies elsewhere (e.g. config path).
  • If this fails with “Permission denied”: the container user cannot write to the mounted volume.

As a quick diagnostic, making the directory world-writable:

sudo chmod -R 777 /srv/privatebin/data

If this instantly makes PrivateBin work, the diagnosis is confirmed: it’s a pure ownership/permissions mismatch between the container’s runtime user (nobody) and the host directory.

Warning: chmod 777 is a diagnostic tool, not a final state. It proves the cause, then you tighten it back down.

6. The correct, secure fix: match ownership to nobody

Since PrivateBin runs as nobody inside the container, the host directory used as the data volume must be owned by the same UID/GID: 65534:65534.

On the host, run:

sudo chown -R 65534:65534 /srv/privatebin/data
sudo chmod -R 755 /srv/privatebin/data

This yields:

ls -ld /srv/privatebin/data
drwxr-xr-x 2 65534 65534 4096 ... /srv/privatebin/data

Now the container’s nobody user owns the directory and has write access, while group and others have read/execute only.

Restart the container:

docker compose down
docker compose up -d

Test creating a paste again. The error should be gone.

Result: PrivateBin can now create and store encrypted pastes using the mounted data volume, without resorting to world-writable permissions.

7. Sanity checks in conf.php

Open the config file on the host:

nano /srv/privatebin/cfg/conf.php

Verify at least these two settings:

'model' => 'filesystem',
'dir'   => 'data',

The dir value is relative to the container’s working directory (/srv in this image), so 'data' correctly points to /srv/data, which is where the volume is mounted.

Optionally, you can also enable file uploads and tweak defaults, for example:

'fileupload'       => true,
'upload_max_size'  => 20 * 1024 * 1024, // 20 MB
'discussion'       => false,
'opendiscussion'   => false,
'defaultformatter' => 'plaintext',
'template'         => 'bootstrap',

Restart after changes:

docker compose down
docker compose up -d

8. Doctrine summary

  • Symptom: PrivateBin UI loads, but every paste fails with “Could not create document: Error saving document. Sorry.”
  • Root cause: PrivateBin (php-fpm) runs as nobody inside the container, but the mounted data directory on the host is owned by a different user and not writable by nobody.
  • Diagnostic proof: chmod 777 on the data directory makes it work immediately.
  • Correct fix: Set ownership of the data directory to 65534:65534 and permissions to 755:
    sudo chown -R 65534:65534 /srv/privatebin/data
    sudo chmod -R 755 /srv/privatebin/data
  • Config sanity: Ensure 'model' => 'filesystem' and 'dir' => 'data' in conf.php.

This pattern applies to any Dockerized service where the container runs as nobody (or any non-root user) and writes to a host-mounted volume: always align the host directory’s ownership with the container’s runtime UID/GID, then tighten permissions to the minimum required.

0 Komentari

Nema komentara