Всем добра.
Меня зовут Нариман, я full-stack разработчик по совместительству и 1С-разработчик по основному роду деятельности, и сегодня хочу рассказать короткую историю про инструмент под названием Nginx Proxy Manager. Признаюсь был удивлен, не найдя на него обзора на хабре, а потому тема своей первой статьи на хабре была предрешена.

Цель статьи простая — познакомить читателя с удобным инструментом для быстрого развёртывания проектов через понятный и наглядный web-интерфейс.
Материал ориентирован в первую очередь на новичков, которые только начинают делать первые шаги в DevOps (примерно как я сам в своё время).

Немного личного опыта

На заре своей бесславной карьеры в разработке ПО, когда я написал свой первый более-менее серьёзный проект на популярном стеке FastAPI + Vue, встал вполне логичный вопрос:

А как все задеплоить?

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

Я довольно долго ковырялся с конфигами, читал статьи, смотрел примеры, пытался понять, почему здесь proxy_pass работает, а там — нет.
В итоге, да, я его настроил. Всё заработало. Но:

  • было потрачено несколько вечеров,

  • было страшно от мысли, что все это придется повторить еще раз

  • и главное — появилось ощущение, что я занимаюсь чем угодно, но не разработкой

Разумеется, в этот момент я переосмыслил ситуацию и пошел гуглить, а какие еще есть варианты. В итоге я вышел на героя рассказа нашей повести - Nginx Proxy Manager.

Но прежде чем мы начнем обзор Nginx Proxy Manager, ответим на один вопрос..

Зачем вообще нужен reverse proxy?

Как правило в большинстве проектов, приложение разбито на несколько блоков. Отдельно Backend для api, отдельно frontend для визуала сайта, часто еще несколько сопутствующих сервисов - minio, celery, RabbitMq.

И все это хозяйство надо как то между собой связать, причем в идеальном мире у нас есть отдельно develop и отдельно production окружение.

Если в dev-окружении мы просто запускаем команды локального запуска проекта, например:

npm run dev

то в production на нужно сначала сделать build проекта, а потом все это пробросить во внешний мир через reverse_proxy:

Сначала билдим проект:

npm run build

После этого появится папка build/ со статическими файлами:

build/
├── index.html
├── static/
│ ├── css/
│ └── js/
└── ...

Эти файлы — готовы к раздаче через веб-сервер (например, Nginx).

Пример минимальной конфигурации Nginx

Ниже пример, просто для понимания того, как будет происходить процесс в случае чистой установки Nginx и правки конфигов руками.
Создайте файл конфигурации, например:
/etc/nginx/sites‑available/my‑app

server {
    listen 80;
    server_name your-domain.com;  # Или IP-адрес, или localhost для теста

    root /path/to/your/my-app/build;  # ← УКАЖИТЕ ПОЛНЫЙ ПУТЬ!
    index index.html;

    location / {
        try_files $uri $uri/ /index.html;
    }

    # Опционально: кэширование статики
    location /static {
        expires 1y;
        add_header Cache-Control "public, immutable";
    }
}

Шаги для активации:

  1. Убедитесь, что путь в root правильный (например: /home/user/my-app/build)

  2. Активируйте сайт (на Ubuntu/Debian): sudo ln -s /etc/nginx/sites-available/my-app /etc/nginx/sites-enabled/

  3. Проверьте конфигурацию: sudo nginx -t

  4. Перезапустите Nginx: sudo systemctl reload nginx

Итак каждый раз при любом добавлении нового хоста, или внесении правок к текущему. А еще нам нужно будет сгенерировать SSL сертификаты от Let’s Encrypt, следить за их актуальностью, и также их прописать в конфигурацию Nignx.

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

Что такое Nginx Proxy Manager

Nginx Proxy Manager (NPM) — это бесплатный open-source инструмент с веб-интерфейсом, который упрощает настройку обратного прокси на базе Nginx. Вместо ручного редактирования десятков конфигурационных файлов вы управляете прокси-правилами, доменами и SSL-сертификатами через удобную панель в браузере.

Под капотом — тот же надёжный Nginx, но без необходимости помнить синтаксис server, location и proxy_pass. NPM автоматически выпускает и обновляет бесплатные сертификаты от Let’s Encrypt, поддерживает пользовательские домены, базовую аутентификацию и отлично ложится в Docker-экосистему.

Именно поэтому он стал де-факто стандартом для домашних лабораторий, разработчиков и тех, кто хочет быстро и безопасно выставить локальный сервис в интернет — без единой строчки конфигурации.

Интерфейс админки Nginx Proxy Manager
Интерфейс админки Nginx Proxy Manager

Быстрый старт: поднимаем NPM через Docker Compose

В отдельной папке нашего сервера, создадим папку для запуска NPM через docker-compose. В отдельной папке, потому что у нас, могут быть несколько docker compose проектов, а обслуживать его будет один NPM.

Я создал каталог nginx-pr-manager в каталоге /home и уже в нем создал файл docker-compose.yml. А также два каталога nginx-pr-manager/data и nginx-pr-manager/letsencrypt, для хранения конфигов и сертификатов.

services:
  app:
    image: 'jc21/nginx-proxy-manager:latest'
    container_name: nginx-proxy-manager
    restart: unless-stopped
    ports:
      - '80:80'   # HTTP
      - '81:81'   # Админ-панель NPM
      - '443:443' # HTTPS
    volumes:
      - ./data:/data
      - ./letsencrypt:/etc/letsencrypt

Запускаем контейнер простой командой:

docker-compose up -d

Первичная настройка

После успешного запуска переходим в браузер по адресу вашего сервера с указанием порта 81 (например, http://localhost:81 или http://<ip-вашего-сервера>:81).

Вас встретит окно авторизации. Используйте стандартные учетные данные, которые заданы разработчиком по умолчанию:

Сразу после входа система попросит вас сменить почту и пароль на свои. Не пропускайте этот шаг ради безопасности.

Окно авторизации Nginx Proxy Manager
Окно авторизации Nginx Proxy Manager

Связываем NPM с нашими сервисами

Тут есть важный нюанс. Так как мы запустили Nginx Proxy Manager отдельным изолированным контейнером, он ничего не знает о ваших других проектах. Если вы попытаетесь указать ему http://my-super-app:3000, он просто не найдет этот хост.

Чтобы решить эту проблему и «подружить» контейнеры из разных docker-compose файлов, есть самый правильный и элегантный способ — создать внешнюю Docker-сеть.

Каюсь, я часто опускаю этот шаг, и вместо создания отдельной сети для контейнеров, использую просто IP адрес сервера и порт на котором работает docker контейнер.

Решение: Создаем общую сеть

  1. Создаем сеть вручную в терминале:
    docker network create public_net

  2. Добавляем эту сеть в docker-compose.yml нашего Nginx Proxy Manager:
    Открываем конфиг NPM и добавляем секцию networks внизу и указываем её для сервиса:
    services:
    app:
    image: 'jc21/nginx-proxy-manager:latest'
    # остальные настройки (ports, volumes, environment) ...
    networks:
    - public_net

    networks:
    public_net:
    external: true

    Не забудьте перезапустить контейнер NPM: 
    docker-compose down && docker-compose up -d

  3. Добавляем эту же сеть в docker-compose.yml вашего приложения:
    Допустим, у вас есть ваш проект. Делаем то же самое:

version: "3.9"

services:
  db:
    image: postgres:15-alpine
    container_name: postgres_db
    restart: always
    environment:
      POSTGRES_USER: user
      POSTGRES_PASSWORD: password
      POSTGRES_DB: my_database
    volumes:
      - db_data:/var/lib/postgresql/data
    networks:
      - internal_net

  pgadmin:
    image: dpage/pgadmin4
    container_name: pgadmin_panel
    restart: unless-stopped
    environment:
      PGADMIN_DEFAULT_EMAIL: admin@admin.com
      PGADMIN_DEFAULT_PASSWORD: admin
    depends_on:
      - db
    networks:
      - internal_net
      - public_net

  backend:
    build:
      context: ./backend
    container_name: fastapi_app
    restart: unless-stopped
    environment:
      DATABASE_URL: postgresql://user:password@db:5432/my_database
    depends_on:
      - db
    networks:
      - internal_net
      - public_net

  frontend:
    build:
      context: ./frontend
    container_name: vue_app
    restart: unless-stopped
    networks:
      - public_net

networks:
  internal_net:
    driver: bridge
  public_net:
    external: true

volumes:
  db_data:

Теперь, когда оба контейнера находятся в одной сети public_net, NPM сможет обращаться к вашему приложению просто по имени контейнера: http://backend:8000.

Настройка проксирования в интерфейсе

Теперь самое приятное. Никаких конфигов и консоли.

  1. Заходим в админку NPM.

  2. Идем во вкладку Hosts -> Proxy Hosts.

  3. Жмем кнопку Add Proxy Host.    открывшемся окне заполняем основные поля:

    Форма добавления нового хоста в Nginx Proxy Manager
    Форма добавления нового хоста в Nginx Proxy Manager
  • Domain Names: Ваш домен (например, api.mysite.com). > Важно: У регистратора домена A-запись уже должна указывать на IP вашего сервера.

  • Scheme: http (обычно связь между контейнерами идет по http).

  • Forward Hostname / IP: Имя контейнера вашего приложения (в нашем примере my-backend-app) или IP адрес сервера, если не настраивали сеть.

  • Forward Port: Порт, на котором работает ваше приложение внутри контейнера (например, 8000).

Пример проброса внутреннего сервиса с порта 3001 наружу
Пример проброса внутреннего сервиса с порта 3001 наружу

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

  • Block Common Exploits — базовая защита от простых атак.

  • Websockets Support — если ваше приложение использует сокеты.

Настройки хоста
Настройки хоста

Магия SSL в два клика

Вспомните, как нужно было ставить certbot, запускать скрипты, прописывать пути к ключам в конфиге Nginx... Забудьте.

В этом же окне настройки хоста переходим на вкладку SSL:

  1. В выпадающем списке SSL Certificate выбираем: Request a new SSL Certificate.

  2. Включаем переключатель Force SSL (чтобы был автоматический редирект с http на https).

  3. Вводим email для Let's Encrypt и соглашаемся с условиями.

  4. Жмем Save.

NPM сам сходит к Let's Encrypt, подтвердит владение доменом (через 80 порт), скачает сертификаты, положит их в нужное место и сам перепишет конфиг Nginx. Через 10-15 секунд у вас будет рабочий сайт с зеленым замочком HTTPS.

Более того, он сам будет следить за сроком действия сертификатов и автоматически их продлевать.

Важный нюанс, вы должны для всех доменов для которых хотите получить SSL- сертификат, настроить А-запись указав имя вашего сервера, на котором развернут Nginx Proxy Manager.

Процесс получения  SSL сертификата Let`s Encrypt
Процесс получения SSL сертификата Let`s Encrypt
Так выглядят все опубликованные хосты
Так выглядят все опубликованные хосты

Итоги

Что мы получили в итоге?

  1. Экономия времени. Развертывание нового поддомена занимает ровно 1 минуту.

  2. Наглядность. Весь список ваших ресурсов перед глазами, вы видите, куда и что проксируется.

  3. Безопасность. SSL-сертификаты ставятся и обновляются автоматически, исключая человеческий фактор («ой, забыл продлить»).

  4. Никакой боли с конфигами. Вы больше не боитесь пропустить точку с запятой в /etc/nginx/sites-available/default.

Конечно, для масштабных Enterprise-проектов с тысячами RPS (requests per second) и сложной балансировкой, возможно, потребуется чистый и оптимизированный вручную Nginx или ingress-ко��троллеры в Kubernetes. Но для стартапов, пет-проектов, домашнего сервера или фриланс-заказов Nginx Proxy Manager — это тот самый «швейцарский нож», который должен быть в арсенале каждого разработчика.

Пробуйте, экспериментируйте и не пишите конфиги руками, если это можно не делать.
Всем удачного деплоя

Комментарии (2)


  1. OFrol
    16.01.2026 06:35

    не пишите конфиги руками, если это можно не делать

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

    По существу вопроса: если у вас уже и так есть docker, то логично его использовать в качестве service discovery. Например, можно поставить Traefik или Caddy, и конфигурировать эндпоинты и https для сервисов в самих сервисах.

    Всё что вам нужно, это поднять один контейнер с Traefik или Caddy и настроить их на получение конфигурации из соседних docker-контейнеров.

    Например, вот docker-compose.yml для какого-то сервиса MY_SUPER_SERVICE, обратите внимание на labels:

    services:
      MY_SUPER_SERVICE:
        image: ...
    
        labels:
          - traefik.enable=true
          - traefik.http.routers.MY_SUPER_SERVICE.rule=Host(`SERVICE.example.com`)
          - traefik.http.routers.MY_SUPER_SERVICE.entrypoints=https
          - traefik.http.routers.MY_SUPER_SERVICE.tls.certresolver=letsencrypt
          - traefik.http.services.MY_SUPER_SERVICE.loadbalancer.server.port=8080
    
        networks:
          - traefik
    
    networks:
      traefik:
        external:
          name: traefik
    

    Это единственная конфигурация, которую вам нужно будет сделать для каждого нового сервиса. Traefik сам обнаружит появление нового контейнера, сам получит для него LE-сертификат для домена SERVICE.example.com и сам будет этот сертификат обновлять когда требуется. И так же сам всё подчистит, когда вы этот контейнер удалите.

    Никаких веб-интерфейсов и ручного труда - вы просто делаете docker compose up -d для своего сервиса и через несколько десятков секунд получаете работающий эндпоинт с https


    1. baldr
      16.01.2026 06:35

      Кроме того, конфигурация, накликанная мышкой на страничке довольно сложно бэкапится, копируется и переносится.

      Это ок если вы один - можно всё запомнить. А вот как только появяется в команде хотя бы ещё один человек - то сразу возникают вопросы - кто, что, когда и как поменял?

      Он, конечно, все настройки сохраняет в стандартный конфиг nginx, и можно его оттуда в git закачивать. Но где вы это будете делать - на продакшен-сервере? (на всех?).

      Вызывает некоторое уважение список из 851 открытых issue на Github, а также 88 висящих pull-requests. Первая страница багов - только в течение последнего месяца. Встречаются совсем глупые, которые бы должны тестироваться вовремя. Есть серьёзные уязвимости безопасности, которые не чинятся годами. В общем, я бы не спешил это ставить себе.