Пошаговая инструкция · Самостоятельная установка
Как установить ntfy на Ubuntu: закрытые уведомления для команды
Пошаговая инструкция для самостоятельной установки. Установим закрытый ntfy, создадим разных пользователей для скрипта и телефона и проверим уведомление. Каждый небольшой шаг объясняет действие и показывает, какой результат ждать.
Что подготовить #
Установим ntfy 2.28.0 для уведомлений из скриптов. Сообщения попадают в тему — канал с именем, например backups. Скрипту разрешим отправку, а телефону — чтение. ntfy не требует SMTP для таких уведомлений.
Нужны Ubuntu 26.04 LTS, Docker Engine, Compose v2 и Nginx из подготовки сервера. Команды выполняйте в SSH-терминале сервера от пользователя с sudo. Для этой схемы используются SQLite и файлы: отдельная PostgreSQL не нужна. При ошибке остановитесь на текущем шаге.
Создайте A-запись своего домена на IP сервера. Во всех примерах замените notify.example.com своим доменом. Порты 80/443 доступны Nginx, 8085 наружу не открывайте. Для теста подготовьте приложение ntfy на телефоне; начать можно с браузера без приложения.
Настройка внешнего доступа использует обратный прокси Nginx и Certbot для Nginx. Приложение остаётся доступным на локальном порту; снаружи открыт его HTTPS-домен.
Шаг 1. Создайте постоянные каталоги #
config хранит настройки, state — пользователей и права, cache — сообщения и вложения. Следующие команды выполняйте из ~/services/ntfy.
mkdir -p ~/services/ntfy/{config,state,cache}
cd ~/services/ntfy
chmod 700 state configКаталоги созданы. При новом входе по SSH сначала выполните cd ~/services/ntfy.
Шаг 2. Настройте закрытый сервер #
Откройте nano config/server.yml и вставьте файл ниже, заменив домен. deny-all запрещает доступ без выданных прав. cache-duration: 24h хранит сообщения сутки; телефон, отсутствующий дольше, не получит удалённую историю.
config/server.ymlbase-url: https://notify.example.com
listen-http: ':80'
behind-proxy: true
cache-file: /var/cache/ntfy/cache.db
cache-duration: 24h
auth-file: /var/lib/ntfy/user.db
auth-default-access: deny-all
attachment-cache-dir: /var/cache/ntfy/attachmentsСохраните Ctrl+O, Enter, Ctrl+X. В файле указан ваш HTTPS-домен и политика deny-all; название темы само по себе не считается паролем.
Шаг 3. Сохраните Compose #
Откройте nano compose.yaml и вставьте файл ниже. Он связывает каталоги с контейнером; удаление контейнера не удаляет эти файлы.
compose.yamlservices:
ntfy:
image: binwiederhier/ntfy:v2.28.0
command: serve
restart: unless-stopped
ports:
- '127.0.0.1:8085:80'
volumes:
- ./config:/etc/ntfy:ro
- ./state:/var/lib/ntfy
- ./cache:/var/cache/ntfyСохраните Ctrl+O, Enter, Ctrl+X. Порт 8085 опубликован только на 127.0.0.1 — доступен серверу и Nginx.
Шаг 4. Проверьте Compose #
Перед запуском проверим структуру YAML.
docker compose config --quietКоманда завершилась без вывода. При ошибке исправьте указанную строку и повторите проверку.
Шаг 5. Загрузите ntfy #
Docker скачает официальный образ выбранной версии.
docker compose pullСкачивание завершилось без ошибки сети или неизвестного тега.
Шаг 6. Запустите сервер уведомлений #
Контейнер начнёт работать в фоне и будет перезапускаться после перезагрузки сервера.
docker compose up -dСервис создан; подождите несколько секунд перед проверкой.
Шаг 7. Проверьте локальный ответ #
Этот адрес проверяет приложение без DNS и HTTPS.
curl --fail http://127.0.0.1:8085/v1/healthПолучен успешный ответ. При отказе соединения посмотрите docker compose logs --tail=80 ntfy и не переходите дальше.
Шаг 8. Сохраните сайт Nginx #
Установите Nginx и Certbot по подготовке Ubuntu, если ещё не установили. Откройте sudo nano /etc/nginx/sites-available/ntfy и вставьте файл ниже. Замените все домены своими. Nginx передаёт приложению домен, адрес клиента и протокол; Upgrade и Connection сохраняют WebSocket. До получения сертификата условие $scheme блокирует обычные HTTP-запросы кодом 403, чтобы вход не происходил без шифрования. Буферизация запросов и ответов отключена: подписчик получает уведомления сразу, а загрузки не копятся целиком на прокси.
/etc/nginx/sites-available/ntfymap $http_upgrade $ntfy_connection_upgrade {
default upgrade;
"" close;
}
server {
listen 80;
server_name notify.example.com;
client_max_body_size 100m;
location / {
if ($scheme = http) { return 403; }
proxy_pass http://127.0.0.1:8085;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $ntfy_connection_upgrade;
proxy_read_timeout 3600s;
proxy_send_timeout 3600s;
proxy_buffering off;
proxy_request_buffering off;
}
}
Сохраните Ctrl+O, Enter, Ctrl+X. В файле пока нет путей к сертификатам: Nginx сможет проверить его до выпуска HTTPS.
Шаг 9. Подключите сайт и проверьте HTTP #
Ссылка подключает сохранённый сайт. nginx -t проверяет все настройки сервера; && разрешает перечитать их только при успешной проверке. curl проверяет ответ для вашего домена. При ошибке остановитесь, исправьте указанную строку и повторите проверку.
sudo ln -sfn /etc/nginx/sites-available/ntfy /etc/nginx/sites-enabled/ntfy
sudo nginx -t && sudo systemctl reload nginx
curl -I http://notify.example.com/Ожидается successful от nginx -t и HTTP 403 для обычного запроса: до HTTPS это намеренная блокировка. DNS должен указывать на сервер, TCP 80/443 — быть доступен у провайдера. Ничего не вводите в форму входа по HTTP.
Шаг 10. Получите сертификат и включите HTTPS #
Каждое имя из команды должно указывать на этот сервер. Certbot проверит домены, сам добавит сертификат в сайт Nginx и включит перенаправление HTTP → HTTPS. Укажите свою почту при запросе и подтвердите условия Let’s Encrypt. Это отдельное действие после успешной проверки HTTP.
sudo /snap/bin/certbot --nginx -d notify.example.com --redirectCertbot сообщил об успешной установке. Откройте HTTPS-адрес со своего компьютера: браузер не должен предупреждать о сертификате. Для закрытой панели используйте разрешённый IP. При ошибке исправьте DNS, в том числе AAAA, либо доступ к TCP 80; не повторяйте запросы выпуска подряд.
Шаг 11. Проверьте продление сертификата #
Certbot из snap создаёт службу автоматического продления. Dry-run выполняет пробное продление: настоящие сертификаты не заменяются. Затем проверьте, что таймер планирует следующий запуск. Сохраняйте доступ к TCP 80 для следующих проверок домена.
sudo /snap/bin/certbot renew --dry-run
systemctl list-timers --all 'snap.certbot.renew*'Пробное продление успешно, у таймера есть следующий запуск. Если тест не прошёл, проверьте /var/log/letsencrypt/letsencrypt.log; сначала исправьте причину. После этого можно создавать учётную запись и приглашать пользователей.
Шаг 12. Создайте администратора #
У ntfy нет обязательного мастера первого владельца в браузере. Создадим администратора командой: пароль вводится интерактивно и не отображается.
docker compose exec ntfy ntfy user add --role=admin ownerКоманда подтвердила создание owner. Сохраните пароль в менеджере паролей; администратор может читать все темы.
Шаг 13. Создайте отдельного отправителя #
Скрипт не должен использовать пароль администратора. Создайте обычного пользователя backup-script; задайте другой длинный пароль по запросу.
docker compose exec ntfy ntfy user add backup-scriptПользователь создан, но пока не имеет доступа к теме.
Шаг 14. Разрешите отправку в одну тему #
Право write-only позволяет отправлять сообщения в backups, сохраняя чтение закрытым.
docker compose exec ntfy ntfy access backup-script backups write-onlyКоманда подтвердила правило для backups. Доступ к другим темам не выдан.
Шаг 15. Создайте читателя для телефона #
Заведите отдельного пользователя phone-reader и выдайте ему только чтение backups. Пароль снова вводится интерактивно.
docker compose exec ntfy ntfy user add phone-reader
docker compose exec ntfy ntfy access phone-reader backups read-onlyПользователь создан и получил read-only. В приложении теперь можно обойтись без административной записи.
Шаг 16. Отправьте первое сообщение #
curl вызовет HTTP-адрес темы и запросит пароль backup-script. Замените домен вашим. Сообщение тестовое: секреты и документы в уведомления не отправляйте.
curl --fail --user backup-script --data 'Пробная копия завершена' https://notify.example.com/backupsСервер вернул описание сообщения без ошибки. Это подтверждает отправку; доставку на телефон проверим отдельно.
Шаг 17. Проверьте отказ без пароля #
Повторим чтение темы без авторизации. Эта проверка покажет, что права работают и случайный посетитель не видит историю.
curl -i 'https://notify.example.com/backups/json?poll=1'Ожидается HTTP 401 или 403, а не содержимое сообщения. Если возвращается история, остановитесь и проверьте auth-default-access и права.
Шаг 18. Подпишитесь с телефона #
В приложении ntfy добавьте собственный сервер, пользователя phone-reader и тему backups. Отправьте ещё одно сообщение командой шага 14. Затем сверните приложение и повторите тест при выключенном экране и на мобильной сети.
Сообщение видно в теме, а фоновая доставка работает в нужных условиях. На Android проверьте разрешения уведомлений и энергосбережение. Для быстрой доставки iOS официальный способ использует upstream-base-url на ntfy.sh; это отдельная зависимость. Перед её добавлением сверяйте раздел мобильной доставки в источниках.
Шаг 19. Сохраните данные согласованно #
Коротко остановим ntfy и скопируем пользователей, сообщения и конфигурацию. Сначала создайте место для копий; архив с секретами храните закрытым.
umask 077
mkdir -p "$HOME/backups/ntfy"
docker compose stop ntfy
tar -czf "$HOME/backups/ntfy/ntfy-$(date -u +%Y%m%dT%H%M%SZ).tar.gz" config state cache compose.yamlАрхив создан без ошибки. Если tar сообщил ошибку, сохранение не завершено; выясните причину. ntfy пока остановлен.
Шаг 20. Верните уведомления #
Запустим ntfy после копирования, затем снова отправим сообщение обычным пользователем.
docker compose start ntfyПубликация из шага 14 и чтение на телефоне работают. Перенесите архив в отдельное защищённое хранилище.
Если шаг не работает и как обновлять #
В ~/services/ntfy выполните docker compose ps и docker compose logs --tail=80 ntfy. При 401/403 проверьте пользователя и право на конкретную тему. При 502 повторите локальный /v1/health. Если отправка успешна, а телефон молчит, проверьте подписку, длительные соединения прокси, разрешения и фоновые ограничения.
Копию испытайте на отдельной машине с тем же v2.28.0, другим доменом и без рабочих отправителей: должны сохраниться пользователи, права и история. Перед обновлением прочитайте изменения, создайте копию и проверьте новый точный тег на восстановленной установке. После обновления повторите отправку, отказ постороннему и фоновую доставку. Не удаляйте SQLite-файлы ради повторного запуска.
Настройки внешнего доступа храните вместе с копией приложения: /etc/nginx/sites-available/ntfy и /etc/letsencrypt содержат сайт и сертификаты с закрытыми ключами. Архивируйте их с sudo в закрытое хранилище. После восстановления подключите сайт через sites-enabled, выполните sudo nginx -t, затем перечитайте Nginx. Для ошибки 502 сначала проверьте локальное приложение и /var/log/nginx/error.log; для сертификата — /var/log/letsencrypt/letsencrypt.log и таймер продления.