Всем добра.
Меня зовут Нариман, я 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";
}
}
Шаги для активации:
Убедитесь, что путь в
rootправильный (например:/home/user/my-app/build)Активируйте сайт (на Ubuntu/Debian):
sudo ln -s /etc/nginx/sites-available/my-app /etc/nginx/sites-enabled/Проверьте конфигурацию:
sudo nginx -tПерезапустите 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-экосистему.
Именно поэтому он стал де-факто стандартом для домашних лабораторий, разработчиков и тех, кто хочет быстро и безопасно выставить локальный сервис в интернет — без единой строчки конфигурации.

Быстрый старт: поднимаем 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).
Вас встретит окно авторизации. Используйте стандартные учетные данные, которые заданы разработчиком по умолчанию:
Email:
admin@example.comPassword:
changeme
Сразу после входа система попросит вас сменить почту и пароль на свои. Не пропускайте этот шаг ради безопасности.

Связываем NPM с нашими сервисами
Тут есть важный нюанс. Так как мы запустили Nginx Proxy Manager отдельным изолированным контейнером, он ничего не знает о ваших других проектах. Если вы попытаетесь указать ему http://my-super-app:3000, он просто не найдет этот хост.
Чтобы решить эту проблему и «подружить» контейнеры из разных docker-compose файлов, есть самый правильный и элегантный способ — создать внешнюю Docker-сеть.
Каюсь, я часто опускаю этот шаг, и вместо создания отдельной сети для контейнеров, использую просто IP адрес сервера и порт на котором работает docker контейнер.
Решение: Создаем общую сеть
Создаем сеть вручную в терминале:
docker network create public_net-
Добавляем эту сеть в
docker-compose.ymlнашего Nginx Proxy Manager:
Открываем конфиг NPM и добавляем секцию networks внизу и указываем её для сервиса:services:
app:
image: 'jc21/nginx-proxy-manager:latest'
# остальные настройки (ports, volumes, environment) ...
networks:
- public_netnetworks:
public_net:
external: trueНе забудьте перезапустить контейнер NPM:
docker-compose down && docker-compose up -d Добавляем эту же сеть в
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.
Настройка проксирования в интерфейсе
Теперь самое приятное. Никаких конфигов и консоли.
Заходим в админку NPM.
Идем во вкладку Hosts -> Proxy Hosts.
-
Жмем кнопку Add Proxy Host. открывшемся окне заполняем основные поля:

Форма добавления нового хоста в Nginx Proxy Manager
Domain Names: Ваш домен (например,
api.mysite.com). > Важно: У регистратора домена A-запись уже должна указывать на IP вашего сервера.Scheme:
http(обычно связь между контейнерами идет по http).Forward Hostname / IP: Имя контейнера вашего приложения (в нашем примере
my-backend-app) или IP адрес сервера, если не настраивали сеть.Forward Port: Порт, на котором работает ваше приложение внутри контейнера (например,
8000).

Рекомендую также сразу проставить галочки:
Block Common Exploits — базовая защита от простых атак.
Websockets Support — если ваше приложение использует сокеты.

Магия SSL в два клика
Вспомните, как нужно было ставить certbot, запускать скрипты, прописывать пути к ключам в конфиге Nginx... Забудьте.
В этом же окне настройки хоста переходим на вкладку SSL:
В выпадающем списке SSL Certificate выбираем:
Request a new SSL Certificate.Включаем переключатель Force SSL (чтобы был автоматический редирект с http на https).
Вводим email для Let's Encrypt и соглашаемся с условиями.
Жмем Save.
NPM сам сходит к Let's Encrypt, подтвердит владение доменом (через 80 порт), скачает сертификаты, положит их в нужное место и сам перепишет конфиг Nginx. Через 10-15 секунд у вас будет рабочий сайт с зеленым замочком HTTPS.
Более того, он сам будет следить за сроком действия сертификатов и автоматически их продлевать.
Важный нюанс, вы должны для всех доменов для которых хотите получить SSL- сертификат, настроить А-запись указав имя вашего сервера, на котором развернут Nginx Proxy Manager.


Итоги
Что мы получили в итоге?
Экономия времени. Развертывание нового поддомена занимает ровно 1 минуту.
Наглядность. Весь список ваших ресурсов перед глазами, вы видите, куда и что проксируется.
Безопасность. SSL-сертификаты ставятся и обновляются автоматически, исключая человеческий фактор («ой, забыл продлить»).
Никакой боли с конфигами. Вы больше не боитесь пропустить точку с запятой в
/etc/nginx/sites-available/default.
Конечно, для масштабных Enterprise-проектов с тысячами RPS (requests per second) и сложной балансировкой, возможно, потребуется чистый и оптимизированный вручную Nginx или ingress-контроллеры в Kubernetes. Но для стартапов, пет-проектов, домашнего сервера или фриланс-заказов Nginx Proxy Manager — это тот самый «швейцарский нож», который должен быть в арсенале каждого разработчика.
Пробуйте, экспериментируйте и не пишите конфиги руками, если это можно не делать.
Всем удачного деплоя
Комментарии (16)

UnstoppableRage
16.01.2026 06:35Если так уж сильно хочется гуйни для энжа, то я бы посоветовал обратить внимание на Nginx UI. В нем есть возможность редактировать блоки конфигов ручками просто через текстовый редактор. Если по какой то причине подрубаться по ssh кажется неудобным, то это неплохой вариант. Плюс умеет acme и даже делать записи в разных провайдерах dns серверов.

mgis Автор
16.01.2026 06:35Впервые слышу про такой инструмент. По ощущениям что то очень близкое, к тому что я искал.

UnstoppableRage
16.01.2026 06:35Я какое-то время назад так же искал инструмент который будет браузерным, но при этом позволит редактировать конфиг "по старинке". И перебрав несколько штук остановился именно на этом. И в целом, в данный момент использую его в домашней лабе для управления сервисами внутри своей сети. Даже терминал встроенный есть. В целом штука достаточно удобная для человека который понимает что делает, но вот для новчика возможно будет не так удобно.

corporate-sellout
16.01.2026 06:35Подобные инструменты всегда будут отставать по функционалу от текстовых конфигов, и будут только создавать лишний головняк с сопровождением и уязвимостями. Можно один раз написать Ansible плейбук, который раскатит все конфиги и выпустит все сертификаты. А касательно "сложности" - админ не домохозяйка, ему платят в первую очередь за умение читать документацию сопровождаемых сервисов.

Funkub
16.01.2026 06:35Ну на мой взгляд NPM отлично подходит для хомлаба и например какого то быстрого тестового проекта, где надо 1-2 домена использовать, без автоматизации и вот этого вот всего, а для продакшена логично использовать более защищенные/автоматизированные/узкопрофильные инструменты.

OFrol
16.01.2026 06:35У меня абсолютно противоположный опыт: если забивавать на ansible и любую другую автоматизацию, то хомлаба очень быстро превращается неподдерживаемое непотребство, в котором уже через год невозможно ничего понять. Никто (включая автора) уже не будет знать для какого проекта что и в каком конфиге было поменяно и зачем.
У меня сейчас всё строго, особенно в хомлабе, в которой текучка проектов сильно выше чем на проде: для системных настроек всё только через ансибл, а все сервисы - только через докер композ.
По началу кажется, что это избыточно, но на самом деле это не так - лишнее время тратится только на настройку первого проекта таким способом, а уже со второго начинается экономия даже при первом деплое

Funkub
16.01.2026 06:35Ну хомлабы бывают разные, у меня несколько виртуалок и с десяток LXC контейнеров, наружу торчит не самое большое количество, ну и сам Proxmox торчит наружу и всё, конечно если в ваши требования не укладывается NPM то логичнее использовать строгую автоматизацию и шаблоны

ritorichesky_echpochmak
16.01.2026 06:35https://github.com/nginx-proxy/nginx-proxy/
services: nginx-proxy: container_name: nginx-proxy image: nginxproxy/nginx-proxy:1.8.0 ports: - "80:80" - "443:443/tcp" - "443:443/udp" volumes: - vhost.d:/etc/nginx/vhost.d - ./certs:/etc/nginx/certs:ro - html:/usr/share/nginx/html - /var/run/docker.sock:/tmp/docker.sock:ro environment: - ENABLE_HTTP3=true - TRUST_DOWNSTREAM_PROXY=false restart: always networks: - default nginx-proxy-letsencrypt: container_name: nginx-proxy-letsencrypt image: nginxproxy/acme-companion:2.5.2 volumes: - acme:/etc/acme.sh - vhost.d:/etc/nginx/vhost.d - ./certs:/etc/nginx/certs:rw - html:/usr/share/nginx/html - /var/run/docker.sock:/var/run/docker.sock:ro environment: - NGINX_PROXY_CONTAINER=nginx-proxy depends_on: - nginx-proxy restart: always networks: - default networks: default: external: true name: network volumes: vhost.d: html: acme:во всех остальных петах просто добавляется
serivices: my_service: environment: - VIRTUAL_HOST=${VIRTUAL_HOST} - LETSENCRYPT_HOST=${VIRTUAL_HOST} - LETSENCRYPT_EMAIL=admin@${VIRTUAL_HOST} networks: default: external: name: networkесли хочется чтобы это был не хост, а как бы подраздел хоста, добавить
environment: - VIRTUAL_PATH=/rss-bridge/ - VIRTUAL_DEST=/Всё. Хочешь в git суй, хочешь бэкапь, хочешь через ansible разворачивай, хочешь - переноси куда хочешь, все CLI тулы работают.

baldr
16.01.2026 06:35Ну, во-первых, NPM - это не цель, а средство. Цель - настроить nginx, верно? И именно его конфигурацию хочется бэкапить и переносить. Переезжаете вы на новый сервер - как всё перенести?
Во-вторых, доступ к
/var/run/docker.sock- это сразу красный флаг. Это, практически, вы даёте доступ приложению к хосту с root-правами. Нужно, хотя бы, какие-то прокси для ограничения доступа ставить. Если в приложении находят уязвимости с произвольным выполнением команд, то это реальная проблема. Вы же не хотите майнер на хосте найти как-нибудь?Ну и вообще, nginx - это не просто vhost/location/certificate - там много чего может быть. Начиная от банального .htaccess, до немного более сложного auth_request или просто error_page..

ritorichesky_echpochmak
16.01.2026 06:35Ага, NPM не цель, а средство, которое мордой в Интернет торчит и не умеет в auto discovery. И да, его как раз тут и продают как "поменьше nginx настраивать". Отлично конфигурация в docker-compose.yml бэкапится и переносится.
Во-вторых, как по вашему все остальные делают service discovery? Выше вы что-то на траефик не агрились, nginx то чем не угодил? Волшебства не случилось. Лучше будет один открытый понятный сервис на базе достаточно надёжного ПО смотреть в docker-sock, чем разброд и шатания настроенные левой пяткой и торчащие бесконтрольно в мир. Ну и да, очень странно тащить что попало, а потом надеяться что изоляция докера достаточно надёжна и всё это не протечёт в систему)))) Может поэтому у меня майнеров никогда не водилось...
.htaccess там или ещё что - зависит от конечного контейнера, который для внешнего брокера выглядит как какой-то ещё один сервис до которого нужно пробросить трафичек в соответствии с правилами. Брокер в душе не чает какое там на эндпоинте приложение вам error_page возвращает потому что вы не смогли CORS забороть))) Но это вот вообще не про NPM речь.

baldr
16.01.2026 06:35Как раз у меня Traefik коннектится к докеру через прокси (на базе nginx!), который дает ему только readonly доступ. Я именно про это и говорю - надо ограничивать.
Я Traefik выбирал именно из-за более лёгкой service discovery, но, по прошествии пары лет, он мне не нравится как раз тем, что настроек в нём меньше и неудобно конфигурацию сохранять и контролировать. В labels какой-то ад творится, если нужно дополнительные плагины подключать (для авторизации, например). У меня ещё много к нему претензий, ну да не об этом статья. Тем не менее, после того, как в nginx добавили dynamic resolving, а Angie научился с докером работать - я уже серьёзно думаю на них перейти. Да, это не хоумлаб.
nginx я люблю и уважаю, а вот NPM - ну, наверное, кому-то мышкой проще накликать. Я высказал свои сомнения, но запрещать никому не буду, не было такой цели.
OFrol
Вместо конфигов, написание и деплой которых отлично автоматизируется, вы предлагаете кликать мышкой в интерфейсе для каждого нового сервиса. Удобство тут так себе, на мой взгляд.
По существу вопроса: если у вас уже и так есть docker, то логично его использовать в качестве service discovery. Например, можно поставить Traefik или Caddy, и конфигурировать эндпоинты и https для сервисов в самих сервисах.
Всё что вам нужно, это поднять один контейнер с Traefik или Caddy и настроить их на получение конфигурации из соседних docker-контейнеров.
Например, вот docker-compose.yml для какого-то сервиса MY_SUPER_SERVICE, обратите внимание на labels:
Это единственная конфигурация, которую вам нужно будет сделать для каждого нового сервиса. Traefik сам обнаружит появление нового контейнера, сам получит для него LE-сертификат для домена
SERVICE.example.comи сам будет этот сертификат обновлять когда требуется. И так же сам всё подчистит, когда вы этот контейнер удалите.Никаких веб-интерфейсов и ручного труда - вы просто делаете
docker compose up -dдля своего сервиса и через несколько десятков секунд получаете работающий эндпоинт с httpsbaldr
Кроме того, конфигурация, накликанная мышкой на страничке довольно сложно бэкапится, копируется и переносится.
Это ок если вы один - можно всё запомнить. А вот как только появяется в команде хотя бы ещё один человек - то сразу возникают вопросы - кто, что, когда и как поменял?
Он, конечно, все настройки сохраняет в стандартный конфиг nginx, и можно его оттуда в git закачивать. Но где вы это будете делать - на продакшен-сервере? (на всех?).
Вызывает некоторое уважение список из 851 открытых issue на Github, а также 88 висящих pull-requests. Первая страница багов - только в течение последнего месяца. Встречаются совсем глупые, которые бы должны тестироваться вовремя. Есть серьёзные уязвимости безопасности, которые не чинятся годами. В общем, я бы не спешил это ставить себе.
mgis Автор
Полностью с вами согласен, использование Traefik/Caddy с Docker labels — это более правильный подход для автоматизации. Мой подход в статье скорее ориентирован на тех, кто только начинает знакомство с self-hosted решениями или кому психологически проще контролировать процесс через GUI на этапе прототипирования. Но ваш пример с
docker-compose.yml— отличное дополнение к материалу, спасибо!