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.
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/cfgStart 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.
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.
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
nobodyinside the container, but the mounteddatadirectory on the host is owned by a different user and not writable bynobody. - Diagnostic proof:
chmod 777on the data directory makes it work immediately. - Correct fix: Set ownership of the data directory to
65534:65534and permissions to755:sudo chown -R 65534:65534 /srv/privatebin/data sudo chmod -R 755 /srv/privatebin/data
- Config sanity: Ensure
'model' => 'filesystem'and'dir' => 'data'inconf.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.
کوئی تبصرہ نہیں ملا