DevOps и Docker 10 мин чтения 2026-09-30

Свой push-сервер уведомлений Ntfy на VDS в Docker Compose: мгновенные алерты с серверов, бэкапов и скриптов на телефон через одну команду curl

Практическая инструкция по установке легковесного open-source сервера push-уведомлений Ntfy на VDS в Docker Compose: настройка авторизации, интеграция с Caddy и Nginx с автопродлением SSL, отправка алертов из cron и bash-скриптов одной командой curl.

Инженерная редакция SysKit Проверено инженерами SysKit.ru

Каждый системный администратор и разработчик сталкивается с задачей оперативной доставки уведомлений: завершился ли ночной бэкап баз данных, упал ли Nginx, заблокировал ли CrowdSec атакующего или заканчивается место на диске VDS. Долгие годы стандартом де-факто оставались боты в Telegram или сторонние платформы вроде Pushover.

Однако в современных реалиях публичные платформы несут серьезные риски: Telegram Bot API подвержен задержкам, сетевым сбоям, блокировкам и спам-фильтрам, а Pushover стал платным с фиксированной стоимостью за каждое устройство. Отправка писем по SMTP через локальный почтовик часто натыкается на попадание в спам-листы или доставляется с задержкой в десятки минут.

Идеальным решением в 2026 году стал Ntfy — легковесный open-source сервис push-уведомлений на языке Go (свыше 22 000 звёзд на GitHub), который поднимается на собственном сервере в Docker Compose за 10 минут, потребляет символические 15–25 МБ оперативной памяти и позволяет отправлять алерты на мобильный телефон буквально одной строчкой curl из любого скрипта.

1. Почему Telegram и Pushover проигрывают Ntfy для алертинга

В отличие от тяжеловесных мессенджеров, архитектура Ntfy спроектирована специально для системных оповещений по протоколам HTTP/HTTPS, WebSockets и Server-Sent Events (SSE). Для отправки пуша не нужно регистрировать ботов, получать chat_id, генерировать длинные токены или парсить сложные JSON-ответы.

Критерий сравнения Telegram Bot API Pushover Self-Hosted Ntfy
Сложность отправки Сложный URL, экранирование Markdown REST API с API-токенами curl -d "Текст" url/topic
Приоритеты и тревога Только звук вкл/выкл Поддерживаются (Emergency) 5 уровней (пробивает режим «Не беспокоить»)
Кнопки действий (Actions) Inline-кнопки требуют серверного бота Ограниченные ссылки Прямой запуск HTTP-запросов и ссылок
Приватность данных Логи на серверах третьих лиц Закрытый проприетарный сервис 100% контроль на вашем VDS
Стоимость Бесплатно (с лимитами запросов) $5 за каждое устройство Бесплатно, без ограничений

💡 Совет инженера:

В Ntfy каждый топик является обычной URL-строкой, например https://ntfy.example.com/alerts-prod. Если подписаться на этот топик в мобильном приложении, любое обращение методом POST или PUT мгновенно превратится во всплывающее push-уведомление на экране телефона без задержек.

2. Архитектура Ntfy и системные требования к серверу

Серверная часть Ntfy распространяется в виде единого скомпилированного бинарного файла на Go и официального контейнера binwiederhier/ntfy. Сервер не требует отдельных тяжелых СУБД вроде MySQL или PostgreSQL: для хранения очереди неотправленных сообщений и учетных записей используется встроенный легковесный SQLite (файлы cache.db и auth.db).

Для развертывания сервера подойдет любой базовый VDS на Linux (Ubuntu 22.04 / 24.04 LTS или Debian 12). Перед началом настройки убедитесь, что доменное имя (например, ntfy.yourdomain.ru) направлено на публичный IP-адрес вашего сервера через DNS А-запись. Выяснить текущий IP сервера можно встроенной утилитой SysKit Мой IP.

3. Развертывание Ntfy в Docker Compose с защитой портов

Создадим рабочую директорию проекта на сервере и выставим надежные права доступа. Согласно правилам системной безопасности, служебный порт контейнера связывается строго с локальным адресом 127.0.0.1, чтобы служба не была открыта в интернет в обход фаервола UFW:

sudo mkdir -p /opt/ntfy/{config,cache,auth}
sudo chown -R 1000:1000 /opt/ntfy
cd /opt/ntfy

Создадим конфигурационный файл сервера /opt/ntfy/config/server.yml. Обратите внимание на параметр behind-proxy: true — без него Ntfy будет видеть только локальный IP обратного прокси, что приведет к ложному срабатыванию rate limiting для всех клиентов:

# /opt/ntfy/config/server.yml
base-url: "https://ntfy.example.com"
listen-http: ":80"
behind-proxy: true

# Базы данных SQLite для кэша и пользователей
cache-file: "/var/cache/ntfy/cache.db"
# Срок хранения неотправленных сообщений (по умолчанию 12h, можно увеличить до 7d)
cache-duration: "7d"
auth-file: "/var/lib/ntfy/auth.db"

# Политика безопасности: закрываем топики от публики
auth-default-access: "deny-all"
enable-login: true

# Шлюз для пробуждения iOS-устройств через Apple APNs
upstream-base-url: "https://ntfy.sh"

Теперь подготовим манифест compose.yaml. Мы задаем лимиты памяти mem_limit: 256m и процессора cpus: 0.5 для предотвращения нештатных утечек в соответствии с рекомендациями Docker Compose Production Limits:

# /opt/ntfy/compose.yaml
services:
  ntfy:
    image: binwiederhier/ntfy:latest
    container_name: ntfy
    restart: unless-stopped
    command: serve
    mem_limit: 256m
    cpus: 0.5
    ports:
      - "127.0.0.1:8080:80"
    volumes:
      - ./config/server.yml:/etc/ntfy/server.yml:ro
      - ./cache:/var/cache/ntfy
      - ./auth:/var/lib/ntfy
    environment:
      - TZ=Europe/Moscow

Запускаем контейнер в фоновом режиме:

docker compose up -d

Проверить локальный ответ сервера можно командой: curl -I http://127.0.0.1:8080/v1/health. Сервер должен вернуть статус HTTP/1.1 200 OK.

4. Настройка Reverse Proxy: Caddy 2 и Nginx с автопродлением SSL

Для безопасной передачи push-сообщений и работы мобильных приложений сервер обязан работать по защищенному протоколу HTTPS. Проверить статус открытости портов 80 и 443 можно с помощью утилиты SysKit Проверка портов онлайн.

Рассмотрим два самых надежных варианта настройки веб-сервера:

Вариант А: Быстрая настройка через Caddy 2 (Рекомендуется)

Веб-сервер Caddy 2 из коробки поддерживает автоматический выпуск TLS-сертификатов Let's Encrypt и потоковые протоколы. Добавьте в /etc/caddy/Caddyfile блок:

ntfy.example.com {
    reverse_proxy 127.0.0.1:8080
}

Примените конфигурацию: sudo systemctl reload caddy. Caddy самостоятельно выпустит сертификат, настроит HTTP/2 и обеспечит бесперебойную передачу WebSockets без ручного вмешательства.

Вариант Б: Полная конфигурация Nginx для HTTPS и WebSockets

Если на вашем сервере используется Nginx, важно соблюдать два инженерных правила:

  1. Динамический апгрейд соединения: Нельзя жестко прописывать proxy_set_header Connection "upgrade" для всех запросов, иначе обычные POST-публикации будут некорректно заявлять об апгрейде до сокетов. Используется стандартный маппинг $connection_upgrade.
  2. Полноценный блок HTTPS: Конфигурация должна явно описывать защищенный виртуальный хост на порту 443 с SSL-сертификатами и редирект с порта 80.

Сначала убедитесь, что в файле /etc/nginx/nginx.conf внутри секции http { ... } присутствует маппинг переменной $connection_upgrade:

# /etc/nginx/nginx.conf (внутри блока http)
map $http_upgrade $connection_upgrade {
    default upgrade;
    ''      close;
}

Выпустим первичный SSL-сертификат Let's Encrypt с помощью Certbot:

sudo certbot certonly --nginx -d ntfy.example.com

Теперь создадим полноценный production-конфиг /etc/nginx/sites-available/ntfy.conf с TLS-терминацией и проксированием в контейнер:

# /etc/nginx/sites-available/ntfy.conf
server {
    listen 80;
    server_name ntfy.example.com;
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl http2;
    server_name ntfy.example.com;

    # Пути к сертификатам Let's Encrypt
    ssl_certificate /etc/letsencrypt/live/ntfy.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/ntfy.example.com/privkey.pem;
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers HIGH:!aNULL:!MD5;

    location / {
        proxy_pass http://127.0.0.1:8080;
        proxy_http_version 1.1;

        # Корректный апгрейд до WebSockets только при наличии заголовка Upgrade
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $connection_upgrade;

        proxy_set_header Host $http_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;

        # Отключение буферизации для мгновенной доставки Server-Sent Events (SSE)
        proxy_buffering off;
        proxy_read_timeout 86400s;
        proxy_send_timeout 86400s;
    }
}

Активируйте виртуальный хост и проверьте синтаксис Nginx:

sudo ln -s /etc/nginx/sites-available/ntfy.conf /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx

Проверьте корректность цепочки шифрования через SysKit Проверка SSL-сертификата.

5. Закрытие сервера от публики: пользователи, токены и права доступа (ACL)

Поскольку мы указали в server.yml директиву auth-default-access: "deny-all", сервер полностью защищен от сторонних глаз: читать и публиковать сообщения без авторизации невозможно.

Создадим учетную запись главного администратора внутри запущенного контейнера:

docker exec -it ntfy ntfy user add --role=admin alex

Команда запросит пароль для пользователя. Для создания стойких учетных данных воспользуйтесь инструментом Генератор надежных паролей SysKit.

⚠️ Важное предупреждение:

Никогда не используйте основной пароль администратора внутри bash-скриптов и cron-задач! Если скрипт случайно утечет в лог или открытый репозиторий, посторонние получат контроль над сервером. Для автоматизации выпускаются изолированные токены доступа (Access Tokens).

Сгенерируем сервисный токен для автоматических скриптов с ограничением прав на запись в конкретный топик. В сценариях с повышенными требованиями безопасности токену можно задать срок жизни через флаг --expires (например, 90 дней), либо опустить его для создания бессрочного токена:

# Создаем служебного пользователя для скриптов
docker exec -it ntfy ntfy user add backup-bot

# Выдаем права только на запись в топик backups
docker exec -it ntfy ntfy access backup-bot "backups" write-only

# Генерируем сервисный токен со сроком действия 90 дней
docker exec -it ntfy ntfy token add --expires=90d backup-bot

Команда сгенерирует токен вида tk_a1b2c3d4e5f6.... Этот токен будет безопасно передаваться в HTTP-заголовке Authorization: Bearer tk_....

6. Отправка алертов через curl: теги, приоритеты, кнопки действий и вложения

Отправка уведомлений в Ntfy невероятно проста: базовый алерт отправляется обычной передачей тела запроса методом POST. При этом сервер поддерживает расширенные HTTP-заголовки, кардинально меняющие поведение пуша на смартфоне:

HTTP-заголовок Назначение Пример значения
Title: Заголовок уведомления жирным шрифтом Резервное копирование VDS
Priority: Важность (1–5: min, low, default, high, urgent) urgent (пробивает беззвучный режим)
Tags: Эмодзи и метки классификации через запятую warning,rotating_light,fire
Click: URL для перехода при нажатии на уведомление https://grafana.example.com
Actions: Интерактивные кнопки прямо в уведомлении view, Открыть панель, https://...

Рассмотрим пример отправки тревожного оповещения о критическом сбое базы данных с интерактивной кнопкой перехода:

curl -H "Authorization: Bearer tk_xxxxxxxxxx" \
     -H "Title: Сбой подключения к PostgreSQL" \
     -H "Priority: urgent" \
     -H "Tags: rotating_light,database,skull" \
     -H "Click: https://syskit.ru/port-checker" \
     -H "Actions: view, Проверить доступность портов, https://syskit.ru/port-checker, clear=true" \
     -d "Пул соединений переполнен! Служба PostgreSQL не отвечает более 60 секунд на VDS-01." \
     https://ntfy.example.com/backups

Такое уведомление сразу зазвучит тревожной сиреной на телефоне, отобразит иконку черепа и мигалки, а по клику на кнопку откроет диагностический инструмент.

7. Готовые скрипты: мониторинг сбоев VDS и уведомления о бэкапах

Встроим Ntfy в реальные рабочие процессы системного администратора. Ниже представлен надежный bash-скрипт резервного копирования баз данных, отправляющий отчеты об успехе или немедленный алерт при возникновении любой ошибки.

Создадим файл /usr/local/bin/backup-and-notify.sh:

#!/usr/bin/env bash
set -Eeuo pipefail

# Конфигурация Ntfy
NTFY_SERVER="https://ntfy.example.com"
NTFY_TOPIC="backups"
NTFY_TOKEN="tk_your_secret_token_here"

# Универсальная функция отправки алертов
send_push() {
    local title="$1"
    local message="$2"
    local priority="$3"
    local tags="$4"

    curl -fsSL -H "Authorization: Bearer ${NTFY_TOKEN}" \
         -H "Title: ${title}" \
         -H "Priority: ${priority}" \
         -H "Tags: ${tags}" \
         -d "${message}" \
         "${NTFY_SERVER}/${NTFY_TOPIC}" > /dev/null || true
}

# Перехват непредвиденных сбоев скрипта
trap 'send_push "❌ Авария скрипта бэкапа" "Скрипт аварийно прерван на строке $LINENO!" "urgent" "fire,warning"' ERR

echo "Запуск создания резервной копии..."
START_TIME=$(date +%s)

# Пример операции бэкапа: архивация рабочей директории
BACKUP_FILE="/tmp/backup-$(date +%Y%m%d).tar.gz"
tar -czf "${BACKUP_FILE}" /opt/ntfy/config

DURATION=$(( $(date +%s) - START_TIME ))
FILE_SIZE=$(du -h "${BACKUP_FILE}" | cut -f1)

# Уведомление об успешном завершении
send_push "✅ Бэкап VDS успешно создан" "Архив: ${FILE_SIZE}, время: ${DURATION} сек. Файл сохранен в хранилище." "low" "white_check_mark,floppy_disk"

# Удаление временного архива
rm -f "${BACKUP_FILE}"

Выдаем права на исполнение только суперпользователю: sudo chmod 750 /usr/local/bin/backup-and-notify.sh. Теперь при добавлении задачи в cron любые инциденты будут немедленно приходить на ваш смартфон.

Аналогичным образом Ntfy легко связывается с утилитами умного шифрованного бэкапа Restic и Autorestic на VDS или мониторингом серверов в панели Beszel.

8. Настройка мобильных приложений для Android и iOS

Для приема оповещений установите официальный клиент Ntfy на смартфон. Архитектура доставки уведомлений на мобильных платформах имеет важные технические различия:

  • Android: Клиент доступен в Google Play и каталоге F-Droid. На Android приложение может постоянно удерживать прямое фоновое WebSocket-соединение с вашим VDS либо использовать открытый стандарт UnifiedPush. Это позволяет получать уведомления моментально и автономно, вообще не обращаясь к серверам Google (Google Play Services).
  • iOS (iPhone / iPad): Операционная система Apple жестко запрещает сторонним приложениям держать постоянные фоновые сетевые соединения для экономии батареи. Доставка пушей на iOS реализована следующим образом:
    1. При публикации сообщения ваш VDS отправляет служебный запрос poll_request на шлюз ntfy.sh (параметр upstream-base-url: "https://ntfy.sh"). Запрос содержит только хэш топика и системный ID сообщения.
    2. Шлюз ntfy.sh через Firebase (FCM) и Apple Push Notification service (APNs) передает сигнал «пробуждения» на ваш iPhone.
    3. Приложение Ntfy просыпается в фоне и напрямую с вашего VDS скачивает тело сообщения, заголовок и вложения.
    Нюанс приватности: Текст и вложения уведомлений никогда не передаются на серверы ntfy.sh или Apple, но метаданные (факт отправки и ID сообщения) проходят через внешнюю инфраструктуру APNs. Если вам требуется абсолютная изоляция без внешних шлюзов, в приложении для iOS можно включить режим Background Polling (периодический опрос сервера раз в 15–30 минут), однако уведомления перестанут быть мгновенными.

Шаги подключения мобильного клиента к собственному серверу:

  1. Откройте приложение Ntfy и перейдите в Настройки (Settings) → Сервер по умолчанию (Default server).
  2. Укажите адрес вашего сервера: https://ntfy.example.com.
  3. В разделе Управление аккаунтами (Manage accounts) нажмите «Добавить пользователя» и укажите ваш логин и пароль администратора.
  4. Нажмите кнопку + на главном экране и введите имя топика (например, backups).

После этого проверьте работу из терминала VDS — push-уведомление появится на экране смартфона за долю секунды!

9. Резюме и чек-лист готовности

Развертывание собственного сервера Ntfy решает ключевую проблему системного администрирования: получение надежного, мгновенного и независимого от сторонних мессенджеров канала критических оповещений.

Готовы развернуть собственный сервер уведомлений?

Для надежного мониторинга и круглосуточной доставки алертов Ntfy требуется стабильный виртуальный сервер с быстрым NVMe-накопителем, статическим IP-адресом и аптаймом 99.98%.

Рекомендуемые VDS-провайдеры

Отказоустойчивые сервера с быстрыми NVMe и каналом до 1 Гбит/с

Выбор редакции
⚡

Timeweb Cloud — Быстрые NVMe VDS

Идеальная платформа для self-hosted сервисов и легковесных контейнеров: порты 80/443 открыты, мгновенный аптайм и канал до 1 Гбит/с.

Конфигурация
1 vCPU 3.3 ГГц • 1 ГБ RAM • 15 ГБ NVMe • Трафик без лимита
Премиум Tier III
🔷

Selectel — Надежные облачные серверы

Корпоративная инфраструктура уровня Tier III с гарантированной доступностью 99.98% для критически важных серверов мониторинга и алертов.

Конфигурация
1 vCPU • 2 ГБ RAM • 30 ГБ NVMe • Дата-центры в РФ
Быстрый старт
🟠

Beget — Простота и стабильность

Удобная панель управления с готовым Docker в один клик, автоматическая сетевая маршрутизация и круглосуточная поддержка.

Конфигурация
1 vCPU 3.0 ГГц • 1 ГБ RAM • 15 ГБ NVMe • Быстрый запуск

Инструменты SysKit по теме статьи

Бесплатные утилиты для проверки и диагностики вашего сервера

Вопросы и ответы (FAQ)

Популярные вопросы вебмастеров и сисадминов по данной теме

Как устроен механизм push-уведомлений Ntfy на iOS и сохраняется ли приватность?
Из-за ограничений фоновых процессов в iOS доставка выполняется через шлюз ntfy.sh и сервис Apple APNs. Сервер VDS отправляет на шлюз poll_request, содержащий только хэш топика и системный ID сообщения. APNs будит приложение на iPhone, после чего клиент скачивает сам текст и вложения напрямую с вашего VDS. Текст и файлы через Apple не проходят. Для 100% изоляции можно включить Background Polling (опрос VDS без сторонних шлюзов).
Сколько времени хранятся сообщения в кэше и сколько памяти потребляет контейнер?
Благодаря монолитной компиляции на Go сервис Ntfy потребляет всего 15–30 МБ оперативной памяти. Дисковое пространство расходуется на SQLite-базу кэша (cache.db), где срок хранения регулируется директивой cache-duration (по умолчанию 12 часов, рекомендуется увеличить до 7 дней для надежности сохранения алертов).
Как защитить свой сервер Ntfy от спама и несанкционированного доступа?
В файле server.yml обязательно выставите директиву auth-default-access: "deny-all" и создайте учетную запись администратора через команду ntfy user add. Для скриптов бэкапов и мониторинга выпускаются отдельные токены с ограничением срока действия (--expires=90d) и правами write-only на конкретные топики.

Читайте также в блоге SysKit

Смежные руководства и полезные технические статьи