PrivateBin в Docker Compose: готовый файл
Если вам нужно передать коллеге пароль, приватный ключ или кусок конфига так, чтобы он не осел навсегда в переписке Telegram или почте, обычный pastebin не подходит — сервис видит текст в открытом виде и хранит его сколько захочет. PrivateBin решает это иначе: шифрование происходит в браузере до отправки на сервер, а сам сервер хранит только бессмысленный шифротекст и никогда не видит ни пароль от записи, ни её содержимое. Ниже — рабочий docker-compose.yml, который поднимает такой pastebin на вашем VPS за несколько минут, плюс нюансы, на которых обычно спотыкаются: права на volume, обратный прокси, ограничение размера вставки и автоудаление записей.
Содержание
Обсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →Что такое PrivateBin и чем он отличается от обычного pastebin
PrivateBin — open-source проект (форк ныне заброшенного ZeroBin), написанный на PHP, без базы данных в классическом понимании — записи хранятся как файлы или в SQLite/Postgres/Redis, в зависимости от бэкенда хранилища. Ключевая идея — zero-knowledge: браузер генерирует ключ AES-256 (в текущих версиях — GCM-режим через WebCrypto API), шифрует текст локально, и на сервер уходит уже шифротекст плюс соль. Сам ключ никогда не покидает URL — он передаётся в fragment-части ссылки (#), которая по стандарту HTTP вообще не отправляется на сервер даже с TLS-соединением. Это значит, что владелец сервера, при всём желании — даже если его заставят по повестке отдать данные, — физически не может расшифровать содержимое конкретной записи без ссылки, которая осталась только у отправителя и получателя.
Отличие от обычных решений вроде обычного Pastebin.com или самохостед-аналогов типа Rustdesk-каталогов конфигов — в том, что PrivateBin по умолчанию:
- удаляет запись после первого прочтения (burn after reading) или по таймеру;
- не индексирует содержимое — искать по тексту записей нельзя в принципе, его сервер не видит;
- поддерживает вложения файлов (тоже зашифрованные) и markdown/код с подсветкой синтаксиса;
- не требует регистрации и не хранит IP отправителя в самой записи (хотя nginx/access-логи — отдельная история, о ней ниже).
Для команд разработки типичный сценарий — передать .env с секретами новому сотруднику, поделиться API-ключом с фрилансером, отправить приватный лог с ошибкой без риска, что он всплывёт в открытом чате через полгода.
Готовый docker-compose.yml
Официальный образ — privatebin/nginx-fpm-alpine, в котором уже связаны nginx, php-fpm и сам PrivateBin. Это самый простой вариант для продакшена — не нужно городить отдельный контейнер для fpm и отдельный для веб-сервера.
version: "3.9"
services:
privatebin:
image: privatebin/nginx-fpm-alpine:latest
container_name: privatebin
restart: unless-stopped
ports:
- "127.0.0.1:8099:8080"
volumes:
- privatebin_data:/srv/data
- ./conf.php:/srv/cfg/conf.php:ro
environment:
- PRIVATEBIN_NAME=Notes
tmpfs:
- /var/lib/nginx/tmp
- /var/tmp/nginx
volumes:
privatebin_data:
driver: local
Порт контейнера — 8080, и наружу мы его не публикуем напрямую, а слушаем только на loopback (127.0.0.1:8099), потому что TLS-терминацию и домен будет отдавать внешний реверс-прокси (Nginx или Caddy на хосте). Если вы разворачиваете PrivateBin на отдельном VPS без другого веб-сервера, порт можно пробросить как "80:8080" и поставить Caddy рядом отдельным сервисом — но для большинства случаев, когда на сервере уже крутится несколько сайтов, вариант с loopback-портом и общим реверс-прокси удобнее.
Ключевой момент — volume для данных должен быть постоянным, иначе при пересоздании контейнера (например, docker compose up -d после docker compose pull) все записи потеряются. В официальном образе данные по умолчанию пишутся в /srv/data внутри контейнера — именно этот путь и мапим на named volume.
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать серверКонфигурация conf.php: лимиты, время жизни, storage-бэкенд
По умолчанию образ генерирует conf.php сам, но для продакшена лучше передать свой — так вы контролируете лимит размера вставки, доступные сроки хранения и язык интерфейса. Создайте файл conf.php рядом с docker-compose.yml:
[main]
name = "Notes"
basepath = ""
discussion = false
opendiscussion = false
password = true
fileupload = true
burnafterreadingselected = false
defaultformatter = "plaintext"
sizelimit = 10485760
template = "bootstrap-dark"
languageselection = false
languagedefault = "ru"
urlshortener = ""
qrcode = true
icon = "identicon"
htmlpurifier_cache_dir = "/tmp"
[expire]
default = "1week"
[expire_options]
5min = 300
10min = 600
1hour = 3600
1day = 86400
1week = 604800
1month = 2592000
never = 0
[formatter_options]
0 = "Plain Text"
1 = "Source Code"
2 = "Markdown"
[traffic]
limit = 10
header = ""
exempted = "127.0.0.1"
[purge]
limit = 300
batchsize = 10
[model]
class = Filesystem
[model_options]
dir = PATH . "data"
Пояснения по важным параметрам:
sizelimit = 10485760— лимит на одну вставку в байтах, здесь 10 МБ. Если вы планируете передавать файлы (логи, дампы), увеличивайте — но помните, что весь файл шифруется и передаётся в браузере целиком, без стриминга, так что вставки на сотни мегабайт превращают страницу в тормоза на слабом клиентском устройстве.password = true— разрешает дополнительную защиту паролем поверх шифрования ссылкой. Это не обязательный слой, но полезен, если ссылку придётся отправить не по защищённому каналу (например, в рабочий чат, где её может увидеть третий человек).[traffic] limit— простая защита от спама: не более N вставок с одного IP за период (по умолчанию 10 минут).exempted = "127.0.0.1"нужен, если у вас перед PrivateBin стоит реверс-прокси и вы отдаёте реальный IP черезX-Forwarded-For— иначе счётчик будет считать все запросы как один IP прокси.class = Filesystem— самый простой бэкенд хранения, без БД. Для нагруженного инстанса с тысячами записей в день лучшеclass = Dbс SQLite или PostgreSQL — Filesystem начинает деградировать на десятках тысяч мелких файлов из-за накладных расходов файловой системы.
Смонтировать конфиг нужно именно как файл, не директорию — иначе Docker создаст пустую папку вместо файла, если conf.php ещё не существует на хосте на момент первого up. Проверьте, что файл реально лежит рядом с compose-файлом до запуска.
Реверс-прокси и HTTPS
PrivateBin работает по HTTP внутри Docker-сети, а TLS терминирует внешний прокси. Если вы используете Caddy с автоматическим SSL — конфигурация тривиальна, полный разбор установки есть в статье про Caddy с авто-SSL на Ubuntu 24.04:
notes.example.com {
reverse_proxy 127.0.0.1:8099
header {
Strict-Transport-Security "max-age=63072000"
X-Content-Type-Options "nosniff"
X-Frame-Options "DENY"
Referrer-Policy "no-referrer"
}
}
Для Nginx с сертификатом от Let's Encrypt конфиг чуть длиннее — важно правильно пробросить заголовки, иначе счётчик трафика в conf.php будет видеть только IP прокси:
server {
listen 443 ssl http2;
server_name notes.example.com;
ssl_certificate /etc/letsencrypt/live/notes.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/notes.example.com/privkey.pem;
client_max_body_size 15m;
location / {
proxy_pass http://127.0.0.1:8099;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
Обратите внимание на client_max_body_size — если её не поднять выше значения по умолчанию (обычно 1 МБ), Nginx будет резать крупные вставки до того, как запрос дойдёт до PrivateBin, и пользователь получит невнятную ошибку 413 вместо понятного сообщения о превышении лимита. Значение должно быть чуть больше sizelimit из conf.php, с запасом на служебные заголовки шифрования. Если сомневаетесь, что выбрать между Nginx и Caddy для такой задачи — сравнение подходов есть в статье Caddy или Nginx: что выбрать для сервера.
Автоочистка старых записей
Записи с истёкшим сроком не удаляются сами по себе фоновым процессом — PrivateBin чистит их лениво, при следующем визите на главную страницу (согласно [purge] limit и batchsize из конфига). На малопосещаемом инстансе это может означать, что просроченные, но ещё не прочитанные burn-after-reading записи будут физически лежать на диске неделями. Если для вас важна гигиена хранения (например, из требований к обработке персональных данных), добавьте принудительную очистку по cron — самый надёжный способ дёрнуть garbage collection PrivateBin извне:
# /etc/cron.d/privatebin-purge
0 * * * * root curl -s -o /dev/null "https://notes.example.com/?"
Это просто периодический GET на главную страницу — он триггерит внутреннюю логику purge так же, как обычный визит пользователя. Более подробно про настройку периодических задач — в статье про cron-задачи на VPS.
Если вы используете class = Db с PostgreSQL вместо Filesystem, та же логика purge применяется к строкам таблицы — отдельного механизма TTL на уровне БД в PrivateBin нет, так что cron-запрос выше обязателен независимо от выбранного бэкенда хранения.
Обновление и бэкап
Обновление образа — стандартная процедура для Docker Compose:
docker compose pull
docker compose up -d
Данные в named volume при этом не трогаются. Бэкапить стоит саму директорию с данными — если используете Filesystem-бэкенд, это privatebin_data volume; если Postgres — дамп базы. Учтите важный нюанс: бэкап шифрованных blob-файлов сам по себе бесполезен для восстановления доступа к содержимому без ссылок, которые были у пользователей (сервер физически не хранит ключи), — бэкапить нужно ради непрерывности сервиса (чтобы активные, ещё не прочитанные записи не потерялись при сбое диска), а не ради возможности что-то расшифровать постфактум.
docker run --rm -v privatebin_privatebin_data:/data -v $(pwd):/backup alpine \
tar czf /backup/privatebin-data-$(date +%F).tar.gz -C /data .
Общие практики бэкапа Docker-стека, включая ротацию архивов и восстановление, разобраны в статье про Docker Compose для продакшена.
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать серверНужны сами нейросети для контента?
Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.
Частые вопросы
Можно ли развернуть PrivateBin без реверс-прокси, только на IP сервера без домена?
Да, технически можно — пробросьте порт 80 напрямую и заходите по http://IP-адрес. Но без HTTPS шифрование теряет смысл: ключ передаётся в URL fragment, и хотя браузер его не отправляет на сервер, сам факт работы по незащищённому HTTP делает вас уязвимыми к MITM-подмене JavaScript-кода шифрования на лету. Для реальной приватности домен и TLS обязательны.
Что произойдёт, если я забуду пароль сервера или потеряю доступ к VPS — можно ли восстановить конкретную запись?
Нет. Сервер не хранит ключ шифрования — он был только в URL-ссылке у отправителя и получателя. Даже полный root-доступ к серверу и его дискам не даёт возможности прочитать содержимое конкретной записи без этой ссылки. Это архитектурная особенность zero-knowledge, а не недоработка.
Чем PrivateBin отличается от Cryptpad или Standard Notes для похожих задач?
PrivateBin — про одноразовый обмен коротким секретом (пароль, ключ, код), а не про совместное редактирование или постоянное хранение заметок. Если нужен полноценный редактор документов с историей версий, посмотрите статью про Cryptpad в Docker Compose — это другой класс задач.
Нужен ли отдельный VPS под PrivateBin или можно на общий сервер с другими сайтами?
PrivateBin — крайне лёгкое приложение (PHP + nginx на Alpine), нагрузка минимальна даже при активном использовании командой из десятков человек. Разворачивать его отдельно от других сервисов не обязательно — достаточно того же VPS, где уже крутится реверс-прокси для остальных проектов, с отдельным поддоменом.
Как ограничить доступ к инстансу только для своей команды?
Проще всего — Basic Auth на уровне реверс-прокси (Nginx auth_basic или Caddy basicauth) поверх самого PrivateBin, либо VPN/firewall-правило, разрешающее подключение только с рабочих IP. Сам PrivateBin авторизации не имеет — он рассчитан на модель "у кого есть ссылка, тот и читает".
Обсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →