"Быстрый
<br />
Nginx Proxy Manager на <a class="wpil_keyword_link" href="https://it-apteka.com/tag/mikrotik/" target="_blank" rel="noopener" title="mikrotik" data-wpil-keyword-link="linked" data-wpil-monitor-id="1660">MikroTik</a> запускается через container package — встроенный движок контейнеров в RouterOS 7.4+. Тебе нужны два контейнера: MariaDB и NPM. Они общаются через виртуальный bridge-интерфейс. NPM слушает на порту 81, принимает HTTP/HTTPS снаружи на 80/443 и через web-интерфейс ты настраиваешь proxy hosts с Let's Encrypt SSL. После перезагрузки всё поднимается само — если правильно прописал автозапуск.<br />
<h2>Диагноз: что идёт не так и почему ты здесь</h2>
<p>Поднял container на MikroTik. Вроде запустил NPM. Открываешь порт 81 — не отвечает. Или отвечает, но SSL не выдаёт. Или всё работало, потом роутер перезагрузился и контейнеры не поднялись. Знакомо?</p>
<p>Проблема почти всегда в одном из трёх мест: неправильный сетевой интерфейс для контейнеров, не настроен veth или bridge, либо контейнеры не знают друг о друге по имени хоста. Let’s Encrypt падает отдельно — там свои причины.</p>
<p>Эта статья закрывает весь путь: от <a href="https://it-apteka.com/ustanovka-joomla-poshagovyj-recept-ot-nulja-do-rabochego-sajta/" title="Установка Joomla: пошаговый рецепт от нуля до рабочего сайта" target="_blank" rel="noopener" data-wpil-monitor-id="1666">нуля до рабочего</a> HTTPS с автопродлением сертификатов и публикацией внутренних сервисов наружу.</p>
<p><strong>Что получишь на выходе:</strong> рабочий MikroTik с container package, два запущенных контейнера (MariaDB + <a href="https://it-apteka.com/nginx_proxy_manager/" title="Nginx Proxy Manager: Конфигурация reverse-proxy для популярных веб-интерфейсов" target="_blank" rel="noopener" data-wpil-monitor-id="1663">Nginx Proxy Manager</a>), SSL через Let’s Encrypt, reverse proxy для внутренних сервисов, автозапуск после перезагрузки.</p>
<p><strong>Время:</strong> 1-2 часа при первой настройке. Повторно — 20 минут.</p>
<p><strong>Что нужно:</strong></p>
<ul>
<li><a href="https://it-apteka.com/rezervnoe-kopirovanie-mikrotik-routeros-7-v-telegram-avtomatizacija-za-20-minut/" title="Резервное копирование MikroTik RouterOS 7 в Telegram: автоматизация за 20 минут" target="_blank" rel="noopener" data-wpil-monitor-id="1665">MikroTik с RouterOS</a> 7.4 или новее (лучше 7.6+)</li>
<li>Минимум 256 MB RAM свободной, рекомендую 512 MB</li>
<li>USB-накопитель или дополнительный диск — встроенной памяти роутера не хватит</li>
<li>Белый IP или доменное имя с A-записью на твой IP (для Let’s Encrypt)</li>
<li>Открытые порты 80 и 443 наружу</li>
</ul>
<p><strong>В статье:</strong></p>
<ul>
<li>Включение и настройка container package</li>
<li>Создание <a class="wpil_keyword_link" href="https://it-apteka.com/category/networks/" target="_blank" rel="noopener" title="Сети" data-wpil-keyword-link="linked" data-wpil-monitor-id="1661">сети</a> для контейнеров</li>
<li>Запуск MariaDB</li>
<li>Запуск <a href="https://it-apteka.com/zashhita-wordpress-ot-ddos-atak-mikrotik-nginx-proxy-manager-boevoj-gajd-2026/" title="Защита WordPress от DDoS атак: MikroTik + Nginx Proxy Manager — Боевой гайд 2026" target="_blank" rel="noopener" data-wpil-monitor-id="1664">Nginx Proxy Manager</a></li>
<li>Настройка firewall и NAT</li>
<li>Первый proxy host с SSL</li>
<li>Автозапуск</li>
<li>Troubleshooting</li>
</ul>
<h2>Системные требования и совместимость версий</h2>
<table border="1" cellpadding="8" cellspacing="0" style="border-collapse: collapse; width: 100%;">
<thead>
<tr style="background-color: #f0f4ff;">
<th>Компонент</th>
<th>Минимум</th>
<th>Рекомендуется</th>
<th>Примечание</th>
</tr>
</thead>
<tbody>
<tr>
<td>RouterOS</td>
<td>7.4</td>
<td>7.10+</td>
<td>Container package появился в 7.4, стабилизировался в 7.6</td>
</tr>
<tr>
<td>Архитектура</td>
<td>x86 / arm64</td>
<td>arm64 или x86</td>
<td>arm32 (hEX, RB4xx) — не поддерживается</td>
</tr>
<tr>
<td>RAM</td>
<td>256 MB свободной</td>
<td>512 MB+</td>
<td>MariaDB любит память</td>
</tr>
<tr>
<td>Хранилище</td>
<td>2 GB</td>
<td>8 GB USB/SSD</td>
<td>Образы занимают ~500 MB каждый</td>
</tr>
<tr>
<td>Nginx Proxy Manager</td>
<td>2.10.x</td>
<td>последняя</td>
<td>jc21/nginx-proxy-manager</td>
</tr>
<tr>
<td>MariaDB</td>
<td>10.6</td>
<td>10.11 LTS</td>
<td>mariadb:10.11</td>
</tr>
</tbody>
</table>
<p>На момент публикации актуальна RouterOS 7.15. Перед установкой проверь свежие релизы на mikrotik.com/download.</p>
<h2>Архитектура: как это работает</h2>
<p>Прежде чем запускать команды — пойми схему. Без этого потом не разберёшься что сломалось и где.</p>
<pre class="mermaid">
%%{init: {
'theme': 'base',
'themeVariables': {
'primaryColor': '#ffffff',
'primaryTextColor': '#1e293b',
'primaryBorderColor': '#94a3b8',
'lineColor': '#64748b',
'fontSize': '15px',
'fontFamily': 'ui-sans-serif, system-ui, sans-serif'
},
'flowchart': {'curve': 'linear', 'nodeSpacing': 50, 'rankSpacing': 50}
}}%%
flowchart TD
INET["Internet 80/443"]
FW["MikroTik Firewall + NAT"]
NPM["Nginx Proxy Manager :80/:443/:81"]
BRIDGE["veth bridge 172.18.0.0/24"]
DB["MariaDB :3306"]
SVC1["Internal Service 1 192.168.88.x"]
SVC2["Internal Service 2 192.168.88.x"]
INET -->|"DST-NAT"| FW
FW -->|"veth-npm"| NPM
NPM -->|"запрос"| BRIDGE
BRIDGE -->|"ответ"| NPM
BRIDGE -->|"172.18.0.3"| DB
DB -->|"данные"| BRIDGE
NPM -->|"proxy_pass"| SVC1
NPM -->|"proxy_pass"| SVC2
style INET fill:#f8fafc,stroke:#3b82f6,stroke-width:2px,color:#1e40af
style FW fill:#f8fafc,stroke:#f97316,stroke-width:2px,color:#c2410c
style NPM fill:#f8fafc,stroke:#22c55e,stroke-width:2px,color:#15803d
style BRIDGE fill:#f8fafc,stroke:#94a3b8,stroke-width:2px,color:#475569
style DB fill:#f8fafc,stroke:#22c55e,stroke-width:2px,color:#15803d
style SVC1 fill:#f8fafc,stroke:#94a3b8,stroke-width:2px,color:#475569
style SVC2 fill:#f8fafc,stroke:#94a3b8,stroke-width:2px,color:#475569
</pre>
<p>Вот что происходит по шагам:</p>
<ul>
<li>Интернет стучится на 80 и 443 твоего роутера</li>
<li>MikroTik через DST-NAT перебрасывает трафик на виртуальный интерфейс veth</li>
<li>NPM принимает запросы, смотрит на hostname и проксирует запрос к внутреннему сервису</li>
<li>MariaDB живёт в той же виртуальной сети — NPM обращается к ней по имени контейнера</li>
<li>Порт 81 — веб-интерфейс NPM, только для внутреннего управления</li>
</ul>
<h2>Таблица портов</h2>
<table border="1" cellpadding="8" cellspacing="0" style="border-collapse: collapse; width: 100%;">
<thead>
<tr style="background-color: #f0f4ff;">
<th>Порт</th>
<th>Протокол</th>
<th>Контейнер</th>
<th>Назначение</th>
<th>Открыть наружу?</th>
</tr>
</thead>
<tbody>
<tr>
<td>80</td>
<td>TCP</td>
<td>NPM</td>
<td>HTTP, редирект на HTTPS + Let’s Encrypt challenge</td>
<td>Да</td>
</tr>
<tr>
<td>443</td>
<td>TCP</td>
<td>NPM</td>
<td>HTTPS reverse proxy</td>
<td>Да</td>
</tr>
<tr>
<td>81</td>
<td>TCP</td>
<td>NPM</td>
<td>Веб-интерфейс управления</td>
<td>Нет, только LAN</td>
</tr>
<tr>
<td>3306</td>
<td>TCP</td>
<td>MariaDB</td>
<td>База данных NPM</td>
<td>Никогда</td>
</tr>
</tbody>
</table>
<h2>Шаг 1: Включаем container package</h2>
<p>Container — это отдельный пакет в RouterOS. По умолчанию не установлен. Сначала проверь что у тебя есть и что нужно скачать.</p>
<p>Подключись к роутеру через Winbox или SSH и проверь архитектуру:</p>
<pre><code class="language-bash">
/system resource print
</code></pre>
<p>Смотри строку <code>architecture-name</code>. Запомни — пригодится при скачивании пакета. Типичные значения: <code>arm64</code>, <code>x86</code>, <code>arm</code>. На arm (32-bit) контейнеры не работают — это RB750, hEX и им подобные.</p>
<p>Скачай extra packages для своей версии RouterOS с mikrotik.com/download. Нужен файл вида <code>container-7.x.x-arm64.npk</code>. Загрузи его через Winbox (Files) или по SCP:</p>
<pre><code class="language-bash">
scp container-7.15.0-arm64.npk admin@192.168.88.1:/
</code></pre>
<p>После загрузки перезагрузи роутер:</p>
<pre><code class="language-bash">
/system reboot
</code></pre>
<p>После перезагрузки проверь что пакет установился:</p>
<pre><code class="language-bash">
/system package print
</code></pre>
<p>В списке должен появиться <code>container</code> со статусом <code>installed</code>.</p>
<p>Теперь включи функциональность контейнеров — по умолчанию она выключена даже с установленным пакетом:</p>
<pre><code class="language-bash">
/system/device-mode/update container=yes
</code></pre>
"Внимание:
<br />
После команды device-mode роутер попросит физически нажать кнопку reset в течение 5 минут. Это <a class="wpil_keyword_link" href="https://it-apteka.com/category/security/" target="_blank" rel="noopener" title="Безопасность" data-wpil-keyword-link="linked" data-wpil-monitor-id="1657">защита</a> от удалённой активации. Нажми кнопку на корпусе роутера один раз. Если роутер в стойке — предупреди заранее или подключись физически.<br />
<pre><code class="language-bash">
/system/device-mode/print
</code></pre>
<p>Убедись что <code>container: yes</code> в выводе.</p>
<h2>Шаг 2: Готовим хранилище</h2>
<p>Встроенная память роутера — не для контейнеров. Образы весят сотни мегабайт, данные MariaDB растут. Тебе нужен USB-накопитель или дополнительный диск.</p>
<p>Подключи USB. Проверь что он определился:</p>
<pre><code class="language-bash">
/disk print
</code></pre>
<p>Должен появиться диск, обычно с именем <code>usb1</code> или <code>disk1</code>. Если диск не отформатирован — отформатируй:</p>
<pre><code class="language-bash">
/disk format-drive disk1 file-system=ext4 label=containers
</code></pre>
<p>Создай структуру директорий для контейнеров:</p>
<pre><code class="language-bash">
/file make-dir disk1/npm
/file make-dir disk1/npm/data
/file make-dir disk1/npm/letsencrypt
/file make-dir disk1/mariadb
/file make-dir disk1/mariadb/data
/file make-dir disk1/images
</code></pre>
<p>Путь к диску в RouterOS выглядит как <code>disk1/</code> — без ведущего слеша. Запомни это, будешь использовать при создании контейнеров.</p>
<h2>Шаг 3: Настраиваем сеть для контейнеров</h2>
<p>Это самое важное место. Большинство <a href="https://it-apteka.com/nastrojka-sip-telefonii-za-mikrotik-i-otkljuchenie-sip-alg/" title="SIP ALG MikroTik: отключение, настройка NAT, RTP и решение проблем с VoIP" target="_blank" rel="noopener" data-wpil-monitor-id="1949">проблем с NPM на MikroTik</a> — именно тут. Контейнерам нужна своя изолированная сеть, и они должны видеть друг друга по IP.</p>
<p>Создаём виртуальный bridge для контейнеров:</p>
<pre><code class="language-bash">
/interface bridge add name=containers-bridge comment="Bridge for docker containers"
</code></pre>
<p>Назначаем IP этому bridge — это будет шлюз для контейнеров:</p>
<pre><code class="language-bash">
/ip address add address=172.18.0.1/24 interface=containers-bridge comment="Container network gateway"
</code></pre>
<p>Создаём veth-интерфейс для MariaDB:</p>
<pre><code class="language-bash">
/interface veth add name=veth-mariadb address=172.18.0.3/24 gateway=172.18.0.1
</code></pre>
<p>Создаём veth-интерфейс для NPM:</p>
<pre><code class="language-bash">
/interface veth add name=veth-npm address=172.18.0.2/24 gateway=172.18.0.1
</code></pre>
<p>Добавляем оба veth в bridge:</p>
<pre><code class="language-bash">
/interface bridge port add bridge=containers-bridge interface=veth-mariadb
/interface bridge port add bridge=containers-bridge interface=veth-npm
</code></pre>
<p>Проверяем:</p>
<pre><code class="language-bash">
/interface bridge port print
</code></pre>
<p>Оба интерфейса должны быть в списке со статусом <code>active</code>.</p>
<p>Теперь разреши контейнерам ходить в интернет — нужен masquerade:</p>
<pre><code class="language-bash">
/ip firewall nat add chain=srcnat src-address=172.18.0.0/24 action=masquerade comment="Container network NAT"
</code></pre>
<h2>Шаг 4: Настраиваем реестр образов</h2>
<p>По умолчанию MikroTik тянет образы с <a class="wpil_keyword_link" href="https://it-apteka.com/tag/docker/" target="_blank" rel="noopener" title="Docker" data-wpil-keyword-link="linked" data-wpil-monitor-id="1659">Docker</a> Hub. Укажи это явно:</p>
<pre><code class="language-bash">
/container config set registry-url=https://registry-1.docker.io tmpdir=disk1/images
</code></pre>
<p>Параметр <code>tmpdir</code> — куда скачиваются образы во время pull. Без этого образ попытается распаковаться во встроенную память и не влезет.</p>
<h2>Шаг 5: Запускаем MariaDB</h2>
<p>NPM без базы данных не запустится. Поэтому сначала — MariaDB.</p>
<p>Создаём контейнер MariaDB. Обрати внимание: environment переменные — это пароли и имя базы, их NPM будет использовать при подключении:</p>
<pre><code class="language-bash">
/container add \
image=mariadb:10.11 \
interface=veth-mariadb \
root-dir=disk1/mariadb \
mounts=mariadb-data \
envs=mariadb-envs \
hostname=mariadb \
start-on-boot=yes \
logging=yes \
comment="MariaDB for NPM"
</code></pre>
<p>Сначала создай переменные окружения — они нужны до создания контейнера:</p>
<pre><code class="language-bash">
/container envs add name=mariadb-envs key=MYSQL_ROOT_PASSWORD value=RootPass123!
/container envs add name=mariadb-envs key=MYSQL_DATABASE value=npm
/container envs add name=mariadb-envs key=MYSQL_USER value=npm
/container envs add name=mariadb-envs key=MYSQL_PASSWORD value=NpmDbPass456!
</code></pre>
"Пароли:
<br />
RootPass123! и NpmDbPass456! — примеры. Поставь свои. Запиши их — они понадобятся при настройке NPM. Пароль базы должен совпадать в MariaDB и NPM.<br />
<p>Создай mount для данных MariaDB:</p>
<pre><code class="language-bash">
/container mounts add name=mariadb-data src=disk1/mariadb/data dst=/var/lib/mysql
</code></pre>
<p>Теперь создаём сам контейнер:</p>
<pre><code class="language-bash">
/container add \
image=mariadb:10.11 \
interface=veth-mariadb \
root-dir=disk1/mariadb \
mounts=mariadb-data \
envs=mariadb-envs \
hostname=mariadb \
start-on-boot=yes \
logging=yes \
comment="MariaDB for NPM"
</code></pre>
<p>Запускаем pull образа:</p>
<pre><code class="language-bash">
/container pull [find comment="MariaDB for NPM"]
</code></pre>
<p>Это займёт несколько минут — образ весит около 400 MB. Следи за прогрессом:</p>
<pre><code class="language-bash">
/container print
</code></pre>
<p>Пока статус <code>extracting</code> — ждёшь. Когда станет <code>stopped</code> — образ готов, запускай:</p>
<pre><code class="language-bash">
/container start [find comment="MariaDB for NPM"]
</code></pre>
<p>Подожди 20-30 секунд. MariaDB при первом запуске инициализирует базу — это дольше обычного старта.</p>
<pre><code class="language-bash">
/container print detail
</code></pre>
<p>Статус должен стать <code>running</code>.</p>
<h2>Шаг 6: Запускаем Nginx Proxy Manager</h2>
<p>Создаём переменные окружения для NPM — он должен знать как подключиться к MariaDB. IP 172.18.0.3 — это адрес veth-mariadb, который мы назначили раньше:</p>
<pre><code class="language-bash">
/container envs add name=npm-envs key=DB_MYSQL_HOST value=172.18.0.3
/container envs add name=npm-envs key=DB_MYSQL_PORT value=3306
/container envs add name=npm-envs key=DB_MYSQL_USER value=npm
/container envs add name=npm-envs key=DB_MYSQL_PASSWORD value=NpmDbPass456!
/container envs add name=npm-envs key=DB_MYSQL_NAME value=npm
</code></pre>
<p>Создаём mount-точки для NPM:</p>
<pre><code class="language-bash">
/container mounts add name=npm-data src=disk1/npm/data dst=/data
/container mounts add name=npm-letsencrypt src=disk1/npm/letsencrypt dst=/etc/letsencrypt
</code></pre>
<p>Создаём контейнер NPM:</p>
<pre><code class="language-bash">
/container add \
image=jc21/nginx-proxy-manager:latest \
interface=veth-npm \
root-dir=disk1/npm \
mounts=npm-data,npm-letsencrypt \
envs=npm-envs \
hostname=npm \
start-on-boot=yes \
logging=yes \
comment="Nginx Proxy Manager"
</code></pre>
<p>Pull образа:</p>
<pre><code class="language-bash">
/container pull [find comment="Nginx Proxy Manager"]
</code></pre>
<p>NPM весит около 500 MB — снова ждём. После завершения запускаем:</p>
<pre><code class="language-bash">
/container start [find comment="Nginx Proxy Manager"]
</code></pre>
<p>NPM при первом запуске создаёт схему базы данных — занимает 30-60 секунд. Если он не стартует с первого раза — подожди пока MariaDB полностью инициализируется и попробуй ещё раз.</p>
<h2>Шаг 7: Firewall и NAT для публикации портов</h2>
<p>Контейнеры запущены, но снаружи к ним не добраться — нужно прокинуть порты через NAT и открыть firewall.</p>
<p>DST-NAT для HTTP (порт 80) — пробрасываем на NPM:</p>
<pre><code class="language-bash">
/ip firewall nat add \
chain=dstnat \
in-interface-list=WAN \
protocol=tcp \
dst-port=80 \
action=dst-nat \
to-addresses=172.18.0.2 \
to-ports=80 \
comment="NPM HTTP"
</code></pre>
<p>DST-NAT для HTTPS (порт 443):</p>
<pre><code class="language-bash">
/ip firewall nat add \
chain=dstnat \
in-interface-list=WAN \
protocol=tcp \
dst-port=443 \
action=dst-nat \
to-addresses=172.18.0.2 \
to-ports=443 \
comment="NPM HTTPS"
</code></pre>
<p>Открываем firewall для этих портов:</p>
<pre><code class="language-bash">
/ip firewall filter add \
chain=forward \
in-interface-list=WAN \
protocol=tcp \
dst-port=80,443 \
dst-address=172.18.0.2 \
action=accept \
comment="Allow NPM from WAN" \
place-before=0
</code></pre>
"Про
<br />
Убедись что WAN interface list существует и в нём есть твой внешний интерфейс. Проверь через /interface list print и /interface list member print. Если используешь PPPoE — добавь pppoe-out1 в список WAN.<br />
<p>Для доступа к панели управления NPM изнутри сети — порт 81 на LAN:</p>
<pre><code class="language-bash">
/ip firewall nat add \
chain=dstnat \
in-interface-list=LAN \
protocol=tcp \
dst-port=81 \
action=dst-nat \
to-addresses=172.18.0.2 \
to-ports=81 \
comment="NPM Admin Panel"
</code></pre>
<p>Проверяем весь NAT:</p>
<pre><code class="language-bash">
/ip firewall nat print where comment~"NPM"
</code></pre>
<h2>Шаг 8: Первый вход в Nginx Proxy Manager</h2>
<p>Открой браузер и перейди на <code>http://192.168.88.1:81</code> — это внутренний IP твоего MikroTik.</p>
<p>Дефолтные учётные данные при первом входе:</p>
<ul>
<li>Email: <code>admin@example.com</code></li>
<li>Password: <code>changeme</code></li>
</ul>
<p>NPM сразу попросит сменить email и пароль — сделай это. Не откладывай.</p>
<h2>Шаг 9: Настройка первого proxy host с SSL</h2>
<p>Перед этим убедись что:</p>
<ul>
<li>Домен направлен A-записью на твой внешний IP</li>
<li>Порт 80 доступен снаружи — Let’s Encrypt делает HTTP challenge через него</li>
<li>Внутренний сервис поднят и доступен по IP из сети роутера</li>
</ul>
<p>В интерфейсе NPM: Hosts -> Proxy Hosts -> Add Proxy Host.</p>
<p>Заполняй так:</p>
<ul>
<li>Domain Names: твой домен, например <code>app.example.com</code></li>
<li>Scheme: <code>http</code> (если внутренний сервис без SSL)</li>
<li>Forward Hostname / IP: IP внутреннего сервиса, например <code>192.168.88.100</code></li>
<li>Forward Port: порт сервиса, например <code>8080</code></li>
<li>Websockets Support: включи если нужно</li>
<li>Block Common Exploits: включи</li>
</ul>
<p>Переходи на вкладку SSL:</p>
<ul>
<li>SSL Certificate: Request a new SSL Certificate</li>
<li>Force SSL: включи</li>
<li>HTTP/2 Support: включи</li>
<li>HSTS Enabled: включи если уверен</li>
<li>Email Address: твой email для Let’s Encrypt</li>
<li>I Agree to Terms: отметь</li>
</ul>
<p>Нажми Save. NPM уйдёт на несколько секунд — пройдёт ACME challenge и получит сертификат. Если всё сделано правильно — статус станет зелёным.</p>
<h2>Шаг 10: Проверка работоспособности</h2>
<p>Проверяем что контейнеры живы:</p>
<pre><code class="language-bash">
/container print
</code></pre>
<p>Оба должны быть в статусе <code>running</code>.</p>
<p>Смотрим логи NPM:</p>
<pre><code class="language-bash">
/container log print [find comment="Nginx Proxy Manager"]
</code></pre>
<p>Смотрим логи MariaDB:</p>
<pre><code class="language-bash">
/container log print [find comment="MariaDB for NPM"]
</code></pre>
<p>Проверяем доступность портов изнутри роутера:</p>
<pre><code class="language-bash">
/tool tcp-ping 172.18.0.2 port=80
/tool tcp-ping 172.18.0.2 port=443
/tool tcp-ping 172.18.0.2 port=81
/tool tcp-ping 172.18.0.3 port=3306
</code></pre>
<p>Проверка сети между контейнерами:</p>
<pre><code class="language-bash">
/ping 172.18.0.2 count=3
/ping 172.18.0.3 count=3
</code></pre>
<p>Тест внешней доступности — с другого устройства или через онлайн-сервис:</p>
<pre><code class="language-bash">
curl -I https://app.example.com
</code></pre>
<p>Должен вернуть HTTP/2 200 или редирект от внутреннего сервиса.</p>
<h2>Безопасность</h2>
<p>Не пропускай этот раздел. Роутер с открытым 80/443 — это цель.</p>
<p>Закрой порт 81 (панель NPM) от интернета — оставь только для LAN:</p>
<pre><code class="language-bash">
/ip firewall filter add \
chain=forward \
in-interface-list=WAN \
protocol=tcp \
dst-port=81 \
action=drop \
comment="Block NPM admin from WAN" \
place-before=0
</code></pre>
<p>Закрой MariaDB от всех кроме контейнерной сети:</p>
<pre><code class="language-bash">
/ip firewall filter add \
chain=forward \
protocol=tcp \
dst-port=3306 \
src-address=!172.18.0.0/24 \
action=drop \
comment="Block MariaDB from outside container net"
</code></pre>
<p>Ограничь количество соединений на 80/443 — базовая защита от DDoS:</p>
<pre><code class="language-bash">
/ip firewall filter add \
chain=forward \
in-interface-list=WAN \
protocol=tcp \
dst-port=80,443 \
connection-limit=50,32 \
action=drop \
comment="Limit connections per IP to NPM"
</code></pre>
<p>Включи fail2ban или rate limiting в самом NPM — там есть встроенные настройки в Advanced разделе каждого хоста.</p>
<p>Убедись что SSH на роутер не доступен с WAN без явного правила, или ограничь по IP:</p>
<pre><code class="language-bash">
/ip firewall filter add \
chain=input \
in-interface-list=WAN \
protocol=tcp \
dst-port=22 \
src-address-list=allowed-ssh \
action=accept \
comment="SSH from trusted IPs only"
</code></pre>
<h2>Резервное копирование</h2>
<p><strong>Что бэкапить:</strong></p>
<ul>
<li>Директория <code>disk1/npm/data</code> — конфигурация NPM, все proxy hosts</li>
<li>Директория <code>disk1/npm/letsencrypt</code> — SSL сертификаты и ключи</li>
<li>Директория <code>disk1/mariadb/data</code> — данные базы</li>
<li>RouterOS конфиг: <code>/export</code> или бэкап через Winbox</li>
</ul>
<p><strong>Как часто:</strong> еженедельно для конфигов, перед каждым обновлением NPM или RouterOS.</p>
<p><strong>Скрипт бэкапа конфига RouterOS:</strong></p>
<pre><code class="language-bash">
/system backup save name=("mikrotik-backup-" . [:tostr [/system clock get date]])
/export file=("mikrotik-config-" . [:tostr [/system clock get date]])
</code></pre>
<p>Для бэкапа данных контейнеров — останови их, скопируй директории на внешнее хранилище, запусти обратно. Горячий бэкап MariaDB без остановки — через mysqldump внутри контейнера, но это отдельная история.</p>
<p><strong>Как восстановить:</strong> развернуть структуру директорий, скопировать бэкап данных, создать контейнеры заново по той же схеме. Сертификаты Let’s Encrypt восстановятся из <code>letsencrypt</code> директории.</p>
<h2>Автообновление NPM</h2>
<p>Обновление NPM — это pull нового образа и пересоздание контейнера. Данные сохранятся в mount-точках.</p>
"Перед
<br />
Сделай резервную копию disk1/npm/data и disk1/npm/letsencrypt. Бэкап RouterOS конфига. Только потом обновляй.<br />
<p>Останови NPM:</p>
<pre><code class="language-bash">
/container stop [find comment="Nginx Proxy Manager"]
</code></pre>
<p>Удали старый контейнер (данные в mount-точках сохранятся):</p>
<pre><code class="language-bash">
/container remove [find comment="Nginx Proxy Manager"]
</code></pre>
<p>Создай контейнер заново с теми же параметрами (из шага 6) — при pull подтянется новый образ. Запусти:</p>
<pre><code class="language-bash">
/container start [find comment="Nginx Proxy Manager"]
</code></pre>
<h2>Troubleshooting: что ломается и как чинить</h2>
<h3>Контейнер не стартует — статус stopped сразу после запуска</h3>
<p><strong>Причина:</strong> ошибка в конфигурации — неверные env переменные, недоступный mount, нехватка памяти.</p>
<p><strong>Решение:</strong> смотри логи сразу после попытки запуска.</p>
<pre><code class="language-bash">
/container log print [find comment="Nginx Proxy Manager"]
</code></pre>
<p>Частая причина для NPM — MariaDB ещё не готова. Подожди 30 секунд после старта MariaDB, потом запускай NPM.</p>
<h3>NPM запустился, но порт 81 не отвечает</h3>
<p><strong>Причина:</strong> проблема с veth или NAT правилом.</p>
<p><strong>Решение:</strong> проверь что veth-npm в bridge и имеет правильный IP.</p>
<pre><code class="language-bash">
/interface veth print
/interface bridge port print
/ip address print where interface=containers-bridge
</code></pre>
<p>Проверь NAT правило для порта 81:</p>
<pre><code class="language-bash">
/ip firewall nat print where dst-port=81
</code></pre>
<h3>Let’s Encrypt не выдаёт сертификат — ошибка «Invalid response from domain»</h3>
<p><strong>Причина:</strong> порт 80 снаружи недоступен или A-запись домена не указывает на твой IP.</p>
<p><strong>Решение:</strong> проверь что port 80 NAT-правило активно и firewall его пропускает. Проверь A-запись:</p>
<pre><code class="language-bash">
/tool dns-lookup app.example.com
</code></pre>
<p>Проверь порт 80 снаружи через онлайн-сервис типа portchecker.co. Let’s Encrypt должен добраться до <code>http://app.example.com/.well-known/acme-challenge/</code>.</p>
<h3>Контейнеры не видят интернет — pull образа зависает</h3>
<p><strong>Причина:</strong> нет masquerade для контейнерной сети или неверный gateway в veth.</p>
<p><strong>Решение:</strong> проверь NAT масквераде и маршрутизацию.</p>
<pre><code class="language-bash">
/ip firewall nat print where comment~"Container"
/ip route print
</code></pre>
<p>Убедись что <code>gateway=172.18.0.1</code> в обоих veth-интерфейсах и что <code>172.18.0.1</code> назначен на <code>containers-bridge</code>.</p>
<h3>После перезагрузки контейнеры не запускаются</h3>
<p><strong>Причина:</strong> start-on-boot не установлен или USB-диск не успел подключиться до старта контейнеров.</p>
<p><strong>Решение:</strong> проверь параметр start-on-boot.</p>
<pre><code class="language-bash">
/container print detail
</code></pre>
<p>Если start-on-boot=no — исправь:</p>
<pre><code class="language-bash">
/container set [find comment="MariaDB for NPM"] start-on-boot=yes
/container set [find comment="Nginx Proxy Manager"] start-on-boot=yes
</code></pre>
<p>Если USB не успевает монтироваться — добавь задержку через scheduler. Инженер с опытом скажет: задержка в 15 секунд решает 90% проблем с USB на RouterOS.</p>
<pre><code class="language-bash">
/system scheduler add \
name=start-containers \
on-event="/container start [find start-on-boot=yes]" \
start-time=startup \
interval=0 \
comment="Start containers after boot"
</code></pre>
<h3>NPM не подключается к MariaDB — ошибка «Access denied for user»</h3>
<p><strong>Причина:</strong> пароли в env переменных NPM и MariaDB не совпадают.</p>
<p><strong>Решение:</strong> проверь что <code>MYSQL_PASSWORD</code> в MariaDB совпадает с <code>DB_MYSQL_PASSWORD</code> в NPM. Оба должны быть одинаковыми. Если менял — пересоздай контейнеры.</p>
<pre><code class="language-bash">
/container envs print where name=mariadb-envs
/container envs print where name=npm-envs
</code></pre>
<h3>Proxy host показывает 502 Bad Gateway</h3>
<p><strong>Причина:</strong> NPM не может добраться до внутреннего сервиса по указанному IP и порту.</p>
<p><strong>Решение:</strong> проверь что внутренний сервис поднят и доступен с роутера.</p>
<pre><code class="language-bash">
/tool tcp-ping 192.168.88.100 port=8080
</code></pre>
<p>Убедись что firewall не блокирует трафик из контейнерной сети к LAN. NPM ходит к внутренним сервисам с адреса 172.18.0.2:</p>
<pre><code class="language-bash">
/ip firewall filter print where chain=forward
</code></pre>
<p>Если есть правило drop для трафика между VLAN — добавь исключение для контейнерной сети.</p>
<h3>Образ не скачивается — ошибка «no space left»</h3>
<p><strong>Причина:</strong> tmpdir не указан или указан на встроенную память.</p>
<p><strong>Решение:</strong></p>
<pre><code class="language-bash">
/container config print
</code></pre>
<p>Убедись что <code>tmpdir=disk1/images</code>. Если нет — установи и попробуй pull снова.</p>
<pre><code class="language-bash">
/container config set tmpdir=disk1/images
</code></pre>
<h2>Альтернативные решения</h2>
<p>NPM — не единственный вариант. Смотри что ещё есть:</p>
<p><strong>Traefik</strong> — более гибкий, поддерживает автодискавери Docker. Сложнее в настройке, требует больше ресурсов. Оправдан если планируешь много <a href="https://it-apteka.com/avtomaticheskoe-obnovlenie-docker-kontejnerov-polnoe-rukovodstvo-i-primery/" title="Автоматическое обновление Docker контейнеров: полное руководство и примеры" target="_blank" rel="noopener" data-wpil-monitor-id="1667">контейнеров с автоматическим</a> роутингом.</p>
<p><strong>Caddy</strong> — минималистичный, автоматически управляет SSL. Конфиг в одном файле. Минус — нет веб-интерфейса, всё через текстовый конфиг. Потребляет меньше памяти чем NPM.</p>
<p><strong>HAProxy</strong> — если нужен L4/L7 балансировщик с серьёзной нагрузкой. На роутере избыточен если ты не обрабатываешь тысячи соединений в секунду.</p>
<p><strong>Нативный MikroTik proxy</strong> — /ip proxy. Очень ограниченный, без SSL termination. Не замена NPM.</p>
<p>Выбрал NPM потому что: веб-интерфейс, простая настройка Let’s Encrypt, активное сообщество, хорошо документированные Docker-образы. Для домашней лабы или небольшого офиса — оптимальный выбор.</p>
<h2>Профилактика: как не сломать снова</h2>
<p>Контейнер который работает — это ещё не конец истории. Вот что делать чтобы оно работало долго.</p>
<p><strong>Мониторинг:</strong> добавь проверку состояния контейнеров через Netwatch или <a class="wpil_keyword_link" href="https://it-apteka.com/category/scripts/" target="_blank" rel="noopener" title="Скрипты" data-wpil-keyword-link="linked" data-wpil-monitor-id="1658">скрипт</a> с уведомлением. Минимум — проверяй раз в час что оба контейнера в статусе running.</p>
<pre><code class="language-bash">
/system scheduler add \
name=check-containers \
on-event=":foreach c in=[/container find status!=running] do={/log warning (\"Container down: \" . [/container get \$c comment])}" \
start-time=00:00:00 \
interval=1h \
comment="Check containers health"
</code></pre>
<p><strong>Перед обновлением RouterOS:</strong> останови контейнеры, сделай бэкап, обнови, проверь что container package совместим с новой версией OS, запусти.</p>
<p><strong>Следи за диском:</strong> MariaDB растёт. Раз в месяц смотри свободное место.</p>
<pre><code class="language-bash">
/disk print
</code></pre>
<p><strong>Ротация логов:</strong> в NPM зайди в Settings и настрой log rotation чтобы логи не съели весь диск.</p>
<p><strong>Проверяй сертификаты:</strong> Let’s Encrypt выдаёт на 90 дней, NPM обновляет автоматически за 30 дней до истечения. Но только если порт 80 открыт. После любого изменения firewall — проверяй что 80 работает снаружи.</p>
<h2>FAQ</h2>
<h3>Почему NPM не запускается если MariaDB ещё не готова?</h3>
<p>NPM при старте сразу пытается подключиться к базе данных. Если MariaDB ещё инициализируется — NPM получает ошибку подключения и падает. Решение: запускай MariaDB первой, жди 30-60 секунд и только потом запускай NPM. В production это решается через healthcheck — NPM ждёт пока база не ответит на тестовый запрос. В RouterOS container такого механизма нет, поэтому порядок запуска и scheduler с задержкой — твои друзья.</p>
<h3>Как проверить что Let’s Encrypt сертификат получен и когда он истекает?</h3>
<p>В веб-интерфейсе NPM: SSL Certificates -> список сертификатов с датами истечения. Через командную строку роутера:</p>
<pre><code class="language-bash">
/file print where name~"letsencrypt"
</code></pre>
<p>Или подключись к NPM и смотри файлы в <code>/etc/letsencrypt/live/yourdomain.com/</code>. Дату истечения покажет:</p>
<pre><code class="language-bash">
openssl x509 -noout -enddate -in cert.pem
</code></pre>
<h3>Что если роутер не поддерживает arm64 и container package не ставится?</h3>
<p>Проверь архитектуру командой <code>/system resource print</code>. Если видишь <code>arm</code> (32-bit) — официально контейнеры не поддерживаются. Варианты: поставить отдельный одноплатник (Raspberry Pi 4 с arm64) рядом с роутером и запустить NPM там, а с MikroTik делать только NAT и роутинг. Это даже лучше для производительности — роутер занимается роутингом, не крутит контейнеры.</p>
<h3>Как подключить несколько доменов к одному внутреннему сервису?</h3>
<p>В настройках proxy host в поле Domain Names добавь несколько доменов через Enter. NPM создаст один сертификат на все домены через SAN (Subject Alternative Names). Все домены должны иметь A-запись на твой IP.</p>
<h3>Можно ли запустить NPM без MariaDB — только на SQLite?</h3>
<p>Да. Убери все DB_MYSQL_* переменные из env NPM — тогда он автоматически использует встроенный SQLite. Плюс: проще, меньше ресурсов, нет зависимости от MariaDB. Минус: SQLite медленнее при большом количестве операций, не рекомендован для production с высокой нагрузкой. Для домашней лабы — вполне.</p>
<h3>Почему после перезагрузки роутера сертификаты пропадают?</h3>
<p>Они не пропадают — mount-точка <code>disk1/npm/letsencrypt</code> сохраняет их между перезагрузками. Если сертификаты действительно исчезают — проверь что mount создан правильно и NPM реально пишет в <code>disk1/npm/letsencrypt</code>, а не куда-то внутрь контейнера.</p>
<h2>Итог</h2>
<p>У тебя теперь рабочий MikroTik с двумя контейнерами, <a href="https://it-apteka.com/acme-nginx-nastrojka-let-s-encrypt-vruchnuju-i-cherez-nginx-proxy-manager/" title="ACME + Nginx: настройка Let’s Encrypt вручную и через Nginx Proxy Manager" target="_blank" rel="noopener" data-wpil-monitor-id="1662">SSL через Let’s Encrypt и reverse proxy</a> для внутренних сервисов. Роутер стал не просто маршрутизатором — он терминирует TLS и публикует сервисы наружу. После перезагрузки всё поднимается само.</p>
<p>Главное что нужно помнить: порядок имеет значение. MariaDB первой, NPM вторым. Сеть настрой до контейнеров. tmpdir — на USB. Порт 80 должен быть открыт снаружи иначе Let’s Encrypt молчаливо упадёт.</p>
<p>Если что-то не работает — смотри логи контейнера сразу после попытки запуска. Логи в RouterOS хранятся недолго, не откладывай.</p>
"Не
<br />
Опиши что именно сломалось: вывод /container print, последние строки лога, версия RouterOS и модель роутера. Разберёмся.<br />
Быстрый ответ
Nginx Proxy Manager на
MikroTik запускается через container package — встроенный движок контейнеров в RouterOS 7.4+. Тебе нужны два контейнера: MariaDB и NPM. Они общаются через виртуальный bridge-интерфейс. NPM слушает на порту 81, принимает HTTP/HTTPS снаружи на 80/443 и через web-интерфейс ты настраиваешь proxy hosts с Let’s Encrypt SSL. После перезагрузки всё поднимается само — если правильно прописал автозапуск.
Диагноз: что идёт не так и почему ты здесь
Поднял container на MikroTik. Вроде запустил NPM. Открываешь порт 81 — не отвечает. Или отвечает, но SSL не выдаёт. Или всё работало, потом роутер перезагрузился и контейнеры не поднялись. Знакомо?
Проблема почти всегда в одном из трёх мест: неправильный сетевой интерфейс для контейнеров, не настроен veth или bridge, либо контейнеры не знают друг о друге по имени хоста. Let’s Encrypt падает отдельно — там свои причины.
Эта статья закрывает весь путь: от нуля до рабочего HTTPS с автопродлением сертификатов и публикацией внутренних сервисов наружу.
Что получишь на выходе: рабочий MikroTik с container package, два запущенных контейнера (MariaDB + Nginx Proxy Manager), SSL через Let’s Encrypt, reverse proxy для внутренних сервисов, автозапуск после перезагрузки.
Время: 1-2 часа при первой настройке. Повторно — 20 минут.
Что нужно:
- MikroTik с RouterOS 7.4 или новее (лучше 7.6+)
- Минимум 256 MB RAM свободной, рекомендую 512 MB
- USB-накопитель или дополнительный диск — встроенной памяти роутера не хватит
- Белый IP или доменное имя с A-записью на твой IP (для Let’s Encrypt)
- Открытые порты 80 и 443 наружу
В статье:
- Включение и настройка container package
- Создание сети для контейнеров
- Запуск MariaDB
- Запуск Nginx Proxy Manager
- Настройка firewall и NAT
- Первый proxy host с SSL
- Автозапуск
- Troubleshooting
Системные требования и совместимость версий
| Компонент |
Минимум |
Рекомендуется |
Примечание |
| RouterOS |
7.4 |
7.10+ |
Container package появился в 7.4, стабилизировался в 7.6 |
| Архитектура |
x86 / arm64 |
arm64 или x86 |
arm32 (hEX, RB4xx) — не поддерживается |
| RAM |
256 MB свободной |
512 MB+ |
MariaDB любит память |
| Хранилище |
2 GB |
8 GB USB/SSD |
Образы занимают ~500 MB каждый |
| Nginx Proxy Manager |
2.10.x |
последняя |
jc21/nginx-proxy-manager |
| MariaDB |
10.6 |
10.11 LTS |
mariadb:10.11 |
На момент публикации актуальна RouterOS 7.15. Перед установкой проверь свежие релизы на mikrotik.com/download.
Архитектура: как это работает
Прежде чем запускать команды — пойми схему. Без этого потом не разберёшься что сломалось и где.
%%{init: {
'theme': 'base',
'themeVariables': {
'primaryColor': '#ffffff',
'primaryTextColor': '#1e293b',
'primaryBorderColor': '#94a3b8',
'lineColor': '#64748b',
'fontSize': '15px',
'fontFamily': 'ui-sans-serif, system-ui, sans-serif'
},
'flowchart': {'curve': 'linear', 'nodeSpacing': 50, 'rankSpacing': 50}
}}%%
flowchart TD
INET["Internet 80/443"]
FW["MikroTik Firewall + NAT"]
NPM["Nginx Proxy Manager :80/:443/:81"]
BRIDGE["veth bridge 172.18.0.0/24"]
DB["MariaDB :3306"]
SVC1["Internal Service 1 192.168.88.x"]
SVC2["Internal Service 2 192.168.88.x"]
INET -->|"DST-NAT"| FW
FW -->|"veth-npm"| NPM
NPM -->|"запрос"| BRIDGE
BRIDGE -->|"ответ"| NPM
BRIDGE -->|"172.18.0.3"| DB
DB -->|"данные"| BRIDGE
NPM -->|"proxy_pass"| SVC1
NPM -->|"proxy_pass"| SVC2
style INET fill:#f8fafc,stroke:#3b82f6,stroke-width:2px,color:#1e40af
style FW fill:#f8fafc,stroke:#f97316,stroke-width:2px,color:#c2410c
style NPM fill:#f8fafc,stroke:#22c55e,stroke-width:2px,color:#15803d
style BRIDGE fill:#f8fafc,stroke:#94a3b8,stroke-width:2px,color:#475569
style DB fill:#f8fafc,stroke:#22c55e,stroke-width:2px,color:#15803d
style SVC1 fill:#f8fafc,stroke:#94a3b8,stroke-width:2px,color:#475569
style SVC2 fill:#f8fafc,stroke:#94a3b8,stroke-width:2px,color:#475569
Вот что происходит по шагам:
- Интернет стучится на 80 и 443 твоего роутера
- MikroTik через DST-NAT перебрасывает трафик на виртуальный интерфейс veth
- NPM принимает запросы, смотрит на hostname и проксирует запрос к внутреннему сервису
- MariaDB живёт в той же виртуальной сети — NPM обращается к ней по имени контейнера
- Порт 81 — веб-интерфейс NPM, только для внутреннего управления
Таблица портов
| Порт |
Протокол |
Контейнер |
Назначение |
Открыть наружу? |
| 80 |
TCP |
NPM |
HTTP, редирект на HTTPS + Let’s Encrypt challenge |
Да |
| 443 |
TCP |
NPM |
HTTPS reverse proxy |
Да |
| 81 |
TCP |
NPM |
Веб-интерфейс управления |
Нет, только LAN |
| 3306 |
TCP |
MariaDB |
База данных NPM |
Никогда |
Шаг 1: Включаем container package
Container — это отдельный пакет в RouterOS. По умолчанию не установлен. Сначала проверь что у тебя есть и что нужно скачать.
Подключись к роутеру через Winbox или SSH и проверь архитектуру:
/system resource print
Смотри строку architecture-name. Запомни — пригодится при скачивании пакета. Типичные значения: arm64, x86, arm. На arm (32-bit) контейнеры не работают — это RB750, hEX и им подобные.
Скачай extra packages для своей версии RouterOS с mikrotik.com/download. Нужен файл вида container-7.x.x-arm64.npk. Загрузи его через Winbox (Files) или по SCP:
scp container-7.15.0-arm64.npk admin@192.168.88.1:/
После загрузки перезагрузи роутер:
/system reboot
После перезагрузки проверь что пакет установился:
/system package print
В списке должен появиться container со статусом installed.
Теперь включи функциональность контейнеров — по умолчанию она выключена даже с установленным пакетом:
/system/device-mode/update container=yes
Внимание: device-mode требует физического подтверждения
После команды device-mode роутер попросит физически нажать кнопку reset в течение 5 минут. Это
защита от удалённой активации. Нажми кнопку на корпусе роутера один раз. Если роутер в стойке — предупреди заранее или подключись физически.
/system/device-mode/print
Убедись что container: yes в выводе.
Шаг 2: Готовим хранилище
Встроенная память роутера — не для контейнеров. Образы весят сотни мегабайт, данные MariaDB растут. Тебе нужен USB-накопитель или дополнительный диск.
Подключи USB. Проверь что он определился:
/disk print
Должен появиться диск, обычно с именем usb1 или disk1. Если диск не отформатирован — отформатируй:
/disk format-drive disk1 file-system=ext4 label=containers
Создай структуру директорий для контейнеров:
/file make-dir disk1/npm
/file make-dir disk1/npm/data
/file make-dir disk1/npm/letsencrypt
/file make-dir disk1/mariadb
/file make-dir disk1/mariadb/data
/file make-dir disk1/images
Путь к диску в RouterOS выглядит как disk1/ — без ведущего слеша. Запомни это, будешь использовать при создании контейнеров.
Шаг 3: Настраиваем сеть для контейнеров
Это самое важное место. Большинство проблем с NPM на MikroTik — именно тут. Контейнерам нужна своя изолированная сеть, и они должны видеть друг друга по IP.
Создаём виртуальный bridge для контейнеров:
/interface bridge add name=containers-bridge comment="Bridge for docker containers"
Назначаем IP этому bridge — это будет шлюз для контейнеров:
/ip address add address=172.18.0.1/24 interface=containers-bridge comment="Container network gateway"
Создаём veth-интерфейс для MariaDB:
/interface veth add name=veth-mariadb address=172.18.0.3/24 gateway=172.18.0.1
Создаём veth-интерфейс для NPM:
/interface veth add name=veth-npm address=172.18.0.2/24 gateway=172.18.0.1
Добавляем оба veth в bridge:
/interface bridge port add bridge=containers-bridge interface=veth-mariadb
/interface bridge port add bridge=containers-bridge interface=veth-npm
Проверяем:
/interface bridge port print
Оба интерфейса должны быть в списке со статусом active.
Теперь разреши контейнерам ходить в интернет — нужен masquerade:
/ip firewall nat add chain=srcnat src-address=172.18.0.0/24 action=masquerade comment="Container network NAT"
Шаг 4: Настраиваем реестр образов
По умолчанию MikroTik тянет образы с Docker Hub. Укажи это явно:
/container config set registry-url=https://registry-1.docker.io tmpdir=disk1/images
Параметр tmpdir — куда скачиваются образы во время pull. Без этого образ попытается распаковаться во встроенную память и не влезет.
Шаг 5: Запускаем MariaDB
NPM без базы данных не запустится. Поэтому сначала — MariaDB.
Создаём контейнер MariaDB. Обрати внимание: environment переменные — это пароли и имя базы, их NPM будет использовать при подключении:
/container add \
image=mariadb:10.11 \
interface=veth-mariadb \
root-dir=disk1/mariadb \
mounts=mariadb-data \
envs=mariadb-envs \
hostname=mariadb \
start-on-boot=yes \
logging=yes \
comment="MariaDB for NPM"
Сначала создай переменные окружения — они нужны до создания контейнера:
/container envs add name=mariadb-envs key=MYSQL_ROOT_PASSWORD value=RootPass123!
/container envs add name=mariadb-envs key=MYSQL_DATABASE value=npm
/container envs add name=mariadb-envs key=MYSQL_USER value=npm
/container envs add name=mariadb-envs key=MYSQL_PASSWORD value=NpmDbPass456!
Пароли: смени на свои
RootPass123! и NpmDbPass456! — примеры. Поставь свои. Запиши их — они понадобятся при настройке NPM. Пароль базы должен совпадать в MariaDB и NPM.
Создай mount для данных MariaDB:
/container mounts add name=mariadb-data src=disk1/mariadb/data dst=/var/lib/mysql
Теперь создаём сам контейнер:
/container add \
image=mariadb:10.11 \
interface=veth-mariadb \
root-dir=disk1/mariadb \
mounts=mariadb-data \
envs=mariadb-envs \
hostname=mariadb \
start-on-boot=yes \
logging=yes \
comment="MariaDB for NPM"
Запускаем pull образа:
/container pull [find comment="MariaDB for NPM"]
Это займёт несколько минут — образ весит около 400 MB. Следи за прогрессом:
/container print
Пока статус extracting — ждёшь. Когда станет stopped — образ готов, запускай:
/container start [find comment="MariaDB for NPM"]
Подожди 20-30 секунд. MariaDB при первом запуске инициализирует базу — это дольше обычного старта.
/container print detail
Статус должен стать running.
Шаг 6: Запускаем Nginx Proxy Manager
Создаём переменные окружения для NPM — он должен знать как подключиться к MariaDB. IP 172.18.0.3 — это адрес veth-mariadb, который мы назначили раньше:
/container envs add name=npm-envs key=DB_MYSQL_HOST value=172.18.0.3
/container envs add name=npm-envs key=DB_MYSQL_PORT value=3306
/container envs add name=npm-envs key=DB_MYSQL_USER value=npm
/container envs add name=npm-envs key=DB_MYSQL_PASSWORD value=NpmDbPass456!
/container envs add name=npm-envs key=DB_MYSQL_NAME value=npm
Создаём mount-точки для NPM:
/container mounts add name=npm-data src=disk1/npm/data dst=/data
/container mounts add name=npm-letsencrypt src=disk1/npm/letsencrypt dst=/etc/letsencrypt
Создаём контейнер NPM:
/container add \
image=jc21/nginx-proxy-manager:latest \
interface=veth-npm \
root-dir=disk1/npm \
mounts=npm-data,npm-letsencrypt \
envs=npm-envs \
hostname=npm \
start-on-boot=yes \
logging=yes \
comment="Nginx Proxy Manager"
Pull образа:
/container pull [find comment="Nginx Proxy Manager"]
NPM весит около 500 MB — снова ждём. После завершения запускаем:
/container start [find comment="Nginx Proxy Manager"]
NPM при первом запуске создаёт схему базы данных — занимает 30-60 секунд. Если он не стартует с первого раза — подожди пока MariaDB полностью инициализируется и попробуй ещё раз.
Шаг 7: Firewall и NAT для публикации портов
Контейнеры запущены, но снаружи к ним не добраться — нужно прокинуть порты через NAT и открыть firewall.
DST-NAT для HTTP (порт 80) — пробрасываем на NPM:
/ip firewall nat add \
chain=dstnat \
in-interface-list=WAN \
protocol=tcp \
dst-port=80 \
action=dst-nat \
to-addresses=172.18.0.2 \
to-ports=80 \
comment="NPM HTTP"
DST-NAT для HTTPS (порт 443):
/ip firewall nat add \
chain=dstnat \
in-interface-list=WAN \
protocol=tcp \
dst-port=443 \
action=dst-nat \
to-addresses=172.18.0.2 \
to-ports=443 \
comment="NPM HTTPS"
Открываем firewall для этих портов:
/ip firewall filter add \
chain=forward \
in-interface-list=WAN \
protocol=tcp \
dst-port=80,443 \
dst-address=172.18.0.2 \
action=accept \
comment="Allow NPM from WAN" \
place-before=0
Про in-interface-list=WAN
Убедись что WAN interface list существует и в нём есть твой внешний интерфейс. Проверь через /interface list print и /interface list member print. Если используешь PPPoE — добавь pppoe-out1 в список WAN.
Для доступа к панели управления NPM изнутри сети — порт 81 на LAN:
/ip firewall nat add \
chain=dstnat \
in-interface-list=LAN \
protocol=tcp \
dst-port=81 \
action=dst-nat \
to-addresses=172.18.0.2 \
to-ports=81 \
comment="NPM Admin Panel"
Проверяем весь NAT:
/ip firewall nat print where comment~"NPM"
Шаг 8: Первый вход в Nginx Proxy Manager
Открой браузер и перейди на http://192.168.88.1:81 — это внутренний IP твоего MikroTik.
Дефолтные учётные данные при первом входе:
- Email:
admin@example.com
- Password:
changeme
NPM сразу попросит сменить email и пароль — сделай это. Не откладывай.
Шаг 9: Настройка первого proxy host с SSL
Перед этим убедись что:
- Домен направлен A-записью на твой внешний IP
- Порт 80 доступен снаружи — Let’s Encrypt делает HTTP challenge через него
- Внутренний сервис поднят и доступен по IP из сети роутера
В интерфейсе NPM: Hosts -> Proxy Hosts -> Add Proxy Host.
Заполняй так:
- Domain Names: твой домен, например
app.example.com
- Scheme:
http (если внутренний сервис без SSL)
- Forward Hostname / IP: IP внутреннего сервиса, например
192.168.88.100
- Forward Port: порт сервиса, например
8080
- Websockets Support: включи если нужно
- Block Common Exploits: включи
Переходи на вкладку SSL:
- SSL Certificate: Request a new SSL Certificate
- Force SSL: включи
- HTTP/2 Support: включи
- HSTS Enabled: включи если уверен
- Email Address: твой email для Let’s Encrypt
- I Agree to Terms: отметь
Нажми Save. NPM уйдёт на несколько секунд — пройдёт ACME challenge и получит сертификат. Если всё сделано правильно — статус станет зелёным.
Шаг 10: Проверка работоспособности
Проверяем что контейнеры живы:
/container print
Оба должны быть в статусе running.
Смотрим логи NPM:
/container log print [find comment="Nginx Proxy Manager"]
Смотрим логи MariaDB:
/container log print [find comment="MariaDB for NPM"]
Проверяем доступность портов изнутри роутера:
/tool tcp-ping 172.18.0.2 port=80
/tool tcp-ping 172.18.0.2 port=443
/tool tcp-ping 172.18.0.2 port=81
/tool tcp-ping 172.18.0.3 port=3306
Проверка сети между контейнерами:
/ping 172.18.0.2 count=3
/ping 172.18.0.3 count=3
Тест внешней доступности — с другого устройства или через онлайн-сервис:
curl -I https://app.example.com
Должен вернуть HTTP/2 200 или редирект от внутреннего сервиса.
Безопасность
Не пропускай этот раздел. Роутер с открытым 80/443 — это цель.
Закрой порт 81 (панель NPM) от интернета — оставь только для LAN:
/ip firewall filter add \
chain=forward \
in-interface-list=WAN \
protocol=tcp \
dst-port=81 \
action=drop \
comment="Block NPM admin from WAN" \
place-before=0
Закрой MariaDB от всех кроме контейнерной сети:
/ip firewall filter add \
chain=forward \
protocol=tcp \
dst-port=3306 \
src-address=!172.18.0.0/24 \
action=drop \
comment="Block MariaDB from outside container net"
Ограничь количество соединений на 80/443 — базовая защита от DDoS:
/ip firewall filter add \
chain=forward \
in-interface-list=WAN \
protocol=tcp \
dst-port=80,443 \
connection-limit=50,32 \
action=drop \
comment="Limit connections per IP to NPM"
Включи fail2ban или rate limiting в самом NPM — там есть встроенные настройки в Advanced разделе каждого хоста.
Убедись что SSH на роутер не доступен с WAN без явного правила, или ограничь по IP:
/ip firewall filter add \
chain=input \
in-interface-list=WAN \
protocol=tcp \
dst-port=22 \
src-address-list=allowed-ssh \
action=accept \
comment="SSH from trusted IPs only"
Резервное копирование
Что бэкапить:
- Директория
disk1/npm/data — конфигурация NPM, все proxy hosts
- Директория
disk1/npm/letsencrypt — SSL сертификаты и ключи
- Директория
disk1/mariadb/data — данные базы
- RouterOS конфиг:
/export или бэкап через Winbox
Как часто: еженедельно для конфигов, перед каждым обновлением NPM или RouterOS.
Скрипт бэкапа конфига RouterOS:
/system backup save name=("mikrotik-backup-" . [:tostr [/system clock get date]])
/export file=("mikrotik-config-" . [:tostr [/system clock get date]])
Для бэкапа данных контейнеров — останови их, скопируй директории на внешнее хранилище, запусти обратно. Горячий бэкап MariaDB без остановки — через mysqldump внутри контейнера, но это отдельная история.
Как восстановить: развернуть структуру директорий, скопировать бэкап данных, создать контейнеры заново по той же схеме. Сертификаты Let’s Encrypt восстановятся из letsencrypt директории.
Автообновление NPM
Обновление NPM — это pull нового образа и пересоздание контейнера. Данные сохранятся в mount-точках.
Перед обновлением - бэкап
Сделай резервную копию disk1/npm/data и disk1/npm/letsencrypt. Бэкап RouterOS конфига. Только потом обновляй.
Останови NPM:
/container stop [find comment="Nginx Proxy Manager"]
Удали старый контейнер (данные в mount-точках сохранятся):
/container remove [find comment="Nginx Proxy Manager"]
Создай контейнер заново с теми же параметрами (из шага 6) — при pull подтянется новый образ. Запусти:
/container start [find comment="Nginx Proxy Manager"]
Troubleshooting: что ломается и как чинить
Контейнер не стартует — статус stopped сразу после запуска
Причина: ошибка в конфигурации — неверные env переменные, недоступный mount, нехватка памяти.
Решение: смотри логи сразу после попытки запуска.
/container log print [find comment="Nginx Proxy Manager"]
Частая причина для NPM — MariaDB ещё не готова. Подожди 30 секунд после старта MariaDB, потом запускай NPM.
NPM запустился, но порт 81 не отвечает
Причина: проблема с veth или NAT правилом.
Решение: проверь что veth-npm в bridge и имеет правильный IP.
/interface veth print
/interface bridge port print
/ip address print where interface=containers-bridge
Проверь NAT правило для порта 81:
/ip firewall nat print where dst-port=81
Let’s Encrypt не выдаёт сертификат — ошибка «Invalid response from domain»
Причина: порт 80 снаружи недоступен или A-запись домена не указывает на твой IP.
Решение: проверь что port 80 NAT-правило активно и firewall его пропускает. Проверь A-запись:
/tool dns-lookup app.example.com
Проверь порт 80 снаружи через онлайн-сервис типа portchecker.co. Let’s Encrypt должен добраться до http://app.example.com/.well-known/acme-challenge/.
Контейнеры не видят интернет — pull образа зависает
Причина: нет masquerade для контейнерной сети или неверный gateway в veth.
Решение: проверь NAT масквераде и маршрутизацию.
/ip firewall nat print where comment~"Container"
/ip route print
Убедись что gateway=172.18.0.1 в обоих veth-интерфейсах и что 172.18.0.1 назначен на containers-bridge.
После перезагрузки контейнеры не запускаются
Причина: start-on-boot не установлен или USB-диск не успел подключиться до старта контейнеров.
Решение: проверь параметр start-on-boot.
/container print detail
Если start-on-boot=no — исправь:
/container set [find comment="MariaDB for NPM"] start-on-boot=yes
/container set [find comment="Nginx Proxy Manager"] start-on-boot=yes
Если USB не успевает монтироваться — добавь задержку через scheduler. Инженер с опытом скажет: задержка в 15 секунд решает 90% проблем с USB на RouterOS.
/system scheduler add \
name=start-containers \
on-event="/container start [find start-on-boot=yes]" \
start-time=startup \
interval=0 \
comment="Start containers after boot"
NPM не подключается к MariaDB — ошибка «Access denied for user»
Причина: пароли в env переменных NPM и MariaDB не совпадают.
Решение: проверь что MYSQL_PASSWORD в MariaDB совпадает с DB_MYSQL_PASSWORD в NPM. Оба должны быть одинаковыми. Если менял — пересоздай контейнеры.
/container envs print where name=mariadb-envs
/container envs print where name=npm-envs
Proxy host показывает 502 Bad Gateway
Причина: NPM не может добраться до внутреннего сервиса по указанному IP и порту.
Решение: проверь что внутренний сервис поднят и доступен с роутера.
/tool tcp-ping 192.168.88.100 port=8080
Убедись что firewall не блокирует трафик из контейнерной сети к LAN. NPM ходит к внутренним сервисам с адреса 172.18.0.2:
/ip firewall filter print where chain=forward
Если есть правило drop для трафика между VLAN — добавь исключение для контейнерной сети.
Образ не скачивается — ошибка «no space left»
Причина: tmpdir не указан или указан на встроенную память.
Решение:
/container config print
Убедись что tmpdir=disk1/images. Если нет — установи и попробуй pull снова.
/container config set tmpdir=disk1/images
Альтернативные решения
NPM — не единственный вариант. Смотри что ещё есть:
Traefik — более гибкий, поддерживает автодискавери Docker. Сложнее в настройке, требует больше ресурсов. Оправдан если планируешь много контейнеров с автоматическим роутингом.
Caddy — минималистичный, автоматически управляет SSL. Конфиг в одном файле. Минус — нет веб-интерфейса, всё через текстовый конфиг. Потребляет меньше памяти чем NPM.
HAProxy — если нужен L4/L7 балансировщик с серьёзной нагрузкой. На роутере избыточен если ты не обрабатываешь тысячи соединений в секунду.
Нативный MikroTik proxy — /ip proxy. Очень ограниченный, без SSL termination. Не замена NPM.
Выбрал NPM потому что: веб-интерфейс, простая настройка Let’s Encrypt, активное сообщество, хорошо документированные Docker-образы. Для домашней лабы или небольшого офиса — оптимальный выбор.
Профилактика: как не сломать снова
Контейнер который работает — это ещё не конец истории. Вот что делать чтобы оно работало долго.
Мониторинг: добавь проверку состояния контейнеров через Netwatch или скрипт с уведомлением. Минимум — проверяй раз в час что оба контейнера в статусе running.
/system scheduler add \
name=check-containers \
on-event=":foreach c in=[/container find status!=running] do={/log warning (\"Container down: \" . [/container get \$c comment])}" \
start-time=00:00:00 \
interval=1h \
comment="Check containers health"
Перед обновлением RouterOS: останови контейнеры, сделай бэкап, обнови, проверь что container package совместим с новой версией OS, запусти.
Следи за диском: MariaDB растёт. Раз в месяц смотри свободное место.
/disk print
Ротация логов: в NPM зайди в Settings и настрой log rotation чтобы логи не съели весь диск.
Проверяй сертификаты: Let’s Encrypt выдаёт на 90 дней, NPM обновляет автоматически за 30 дней до истечения. Но только если порт 80 открыт. После любого изменения firewall — проверяй что 80 работает снаружи.
FAQ
Почему NPM не запускается если MariaDB ещё не готова?
NPM при старте сразу пытается подключиться к базе данных. Если MariaDB ещё инициализируется — NPM получает ошибку подключения и падает. Решение: запускай MariaDB первой, жди 30-60 секунд и только потом запускай NPM. В production это решается через healthcheck — NPM ждёт пока база не ответит на тестовый запрос. В RouterOS container такого механизма нет, поэтому порядок запуска и scheduler с задержкой — твои друзья.
Как проверить что Let’s Encrypt сертификат получен и когда он истекает?
В веб-интерфейсе NPM: SSL Certificates -> список сертификатов с датами истечения. Через командную строку роутера:
/file print where name~"letsencrypt"
Или подключись к NPM и смотри файлы в /etc/letsencrypt/live/yourdomain.com/. Дату истечения покажет:
openssl x509 -noout -enddate -in cert.pem
Что если роутер не поддерживает arm64 и container package не ставится?
Проверь архитектуру командой /system resource print. Если видишь arm (32-bit) — официально контейнеры не поддерживаются. Варианты: поставить отдельный одноплатник (Raspberry Pi 4 с arm64) рядом с роутером и запустить NPM там, а с MikroTik делать только NAT и роутинг. Это даже лучше для производительности — роутер занимается роутингом, не крутит контейнеры.
Как подключить несколько доменов к одному внутреннему сервису?
В настройках proxy host в поле Domain Names добавь несколько доменов через Enter. NPM создаст один сертификат на все домены через SAN (Subject Alternative Names). Все домены должны иметь A-запись на твой IP.
Можно ли запустить NPM без MariaDB — только на SQLite?
Да. Убери все DB_MYSQL_* переменные из env NPM — тогда он автоматически использует встроенный SQLite. Плюс: проще, меньше ресурсов, нет зависимости от MariaDB. Минус: SQLite медленнее при большом количестве операций, не рекомендован для production с высокой нагрузкой. Для домашней лабы — вполне.
Почему после перезагрузки роутера сертификаты пропадают?
Они не пропадают — mount-точка disk1/npm/letsencrypt сохраняет их между перезагрузками. Если сертификаты действительно исчезают — проверь что mount создан правильно и NPM реально пишет в disk1/npm/letsencrypt, а не куда-то внутрь контейнера.
Итог
У тебя теперь рабочий MikroTik с двумя контейнерами, SSL через Let’s Encrypt и reverse proxy для внутренних сервисов. Роутер стал не просто маршрутизатором — он терминирует TLS и публикует сервисы наружу. После перезагрузки всё поднимается само.
Главное что нужно помнить: порядок имеет значение. MariaDB первой, NPM вторым. Сеть настрой до контейнеров. tmpdir — на USB. Порт 80 должен быть открыт снаружи иначе Let’s Encrypt молчаливо упадёт.
Если что-то не работает — смотри логи контейнера сразу после попытки запуска. Логи в RouterOS хранятся недолго, не откладывай.
Не заработало? Пиши в комментарии
Опиши что именно сломалось: вывод /container print, последние строки лога, версия RouterOS и модель роутера. Разберёмся.