MAATRIX / Блог / Как установить и настроить Portainer на VPS

Как установить и настроить Portainer на VPS

Как установить и настроить Portainer на VPS

MAATRIX

Десяток контейнеров ещё держится в голове, тридцать — уже нет: docker ps уезжает за край терминала, а нужный лог ищется по памяти. Portainer даёт веб-панель ко всему, что крутится на сервере, но установка «в одну команду» оставляет открытый в интернет порт, самоподписанный сертификат и окно, в котором админа может создать кто угодно. Ниже — установка Portainer на VPS так, чтобы потом ничего не переделывать: compose-файл с закреплённой версией, админ без гонки с таймаутом, нормальный HTTPS и бэкап базы панели.

Обсудить статью, задать вопрос или начать новую тему

Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.

Перейти в сообщество →

Что вы ставите: версии, теги и граница между CE и BE

Portainer CE — веб-интерфейс к Docker: контейнеры, образы, тома, сети, логи и консоль внутрь контейнера в браузере, плюс стеки — те же compose-файлы из редактора. Магии нет: панель ходит в сокет /var/run/docker.sock и дёргает тот же API, что и docker.

Каналов релизов два: LTS — стабильный, для продакшена, и STS — быстрый, с новыми функциями. На конец августа 2026:

ВеткаВышлаПоддержка доТеги образа
2.45 LTSавгуст 2026май 20272.45.0, lts, latest
2.44 STSиюль 2026до следующего STS2.44.0, sts
2.39 LTSфевраль 2026ноябрь 20262.39.7

alpine и alpine-sts — те же сборки на Alpine, образ весит около 45 МБ в сжатом виде. Указывайте точную версию (portainer/portainer-ce:2.45.0), а не latest: иначе очередной docker compose pull подтянет новую ветку не вовремя, и вы займётесь миграцией базы вместо своей задачи.

Честно про границу редакций. В CE есть пользователи, команды и владение ресурсами, но нет ролевой модели: RBAC с семью ролями, шаблоны OAuth/OIDC (Google, GitHub, Entra ID, Okta) и журнал действий — это Business Edition. Нужно «выдать разработчику три контейнера и видеть, кто что перезапускал» — планируйте BE сразу: до трёх нод лицензия бесплатная, ключ продлевается раз в год, образ portainer/portainer-ee.

Подготовка VPS перед установкой Portainer

Берём чистый VPS с Ubuntu 24.04 LTS или Debian 13 и root-доступом, ставим Docker: curl -fsSL https://get.docker.com | sh, затем systemctl enable --now docker. Проверка docker --version && docker compose version в августе 2026 даёт Docker version 29.7.2 и Docker Compose version v2.x. Portainer заведётся и на старых ветках, но всё ниже 26.x уже без обновлений безопасности.

Дальше фаервол — здесь главная грабля установки:

ufw default deny incoming
ufw allow 22/tcp
ufw allow 80,443/tcp
ufw enable

Кажется, всё, кроме SSH и веба, закрыто. Это не так: Docker при публикации порта пишет правила прямо в iptables (цепочки DOCKER и DOCKER-USER), и они обрабатываются раньше правил ufw. Контейнер с -p 9443:9443 доступен из интернета, хотя ufw status показывает deny (incoming). Проверьте с другой машины nmap -Pn -p 9443 203.0.113.10: ответ 9443/tcp open значит, что панель видна сканерам. Поэтому её порт мы не публикуем — привязываем к 127.0.0.1, а наружу смотрит только реверс-прокси на 80 и 443.

Последнее — DNS: заведите A-запись portainer.example.com на IP сервера и проверьте dig +short portainer.example.com, иначе Let's Encrypt не даст сертификат.

Развернуть за пару минут

Готовый образ на VPS MAATRIX: NVMe, AMD EPYC, root-доступ. Локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Развернуть Portainer

Установка Portainer через compose-файл

docker run одной строкой хорош ровно один раз: через месяц никто не вспомнит, с какими флагами поднимался контейнер. Конфигурацию кладём в файл: mkdir -p /opt/portainer && cd /opt/portainer.

Сначала пароль администратора: минимум 12 символов, записать без завершающего перевода строки — иначе \n уедет в базу вместе с паролем.

printf '%s' "$(openssl rand -base64 18)" > /opt/portainer/admin_password
chmod 600 /opt/portainer/admin_password && cat /opt/portainer/admin_password

Сохраните его в менеджер паролей. Теперь /opt/portainer/compose.yaml:

services:
  portainer:
    image: portainer/portainer-ce:2.45.0
    container_name: portainer
    restart: unless-stopped
    command: --http-disabled --admin-password-file /run/secrets/portainer_admin
    ports:
      - "127.0.0.1:9443:9443"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - portainer_data:/data
    secrets:
      - portainer_admin

secrets:
  portainer_admin:
    file: ./admin_password

volumes:
  portainer_data:

Что здесь важно:

  • --admin-password-file создаёт admin при старте, минуя экран первичной настройки. Пароль идёт файловым секретом в /run/secrets/, а не светится в docker inspect, как с --admin-password.
  • --http-disabled выключает HTTP-порт 9000, а привязка 127.0.0.1:9443:9443 оставляет панель видимой только с хоста — защита от граблей с ufw.
  • /var/run/docker.sock даёт панели полный контроль над Docker: это суть её работы и главный риск, доступ к сокету равносилен root на хосте.
  • Том portainer_data хранит базу настроек: без него пересоздание контейнера обнулит учётки, окружения и стеки.

Поднимаем docker compose up -d и смотрим docker compose logs --tail 20 portainer: в логе должна быть строка вида starting Portainer | version=2.45.0 и ни одной с failed. Аппетиты скромные — docker stats --no-stream portainer на узле 2 vCPU / 4 ГБ показывает 71.9MiB / 3.84GiB и 0.28% CPU. Ресурсы съест не панель, а контейнеры.

Первый вход без гонки с пятиминутным таймаутом

Типичный сценарий: запустили контейнер, отвлеклись на звонок, вернулись через десять минут — и вместо формы регистрации:

Your Portainer instance timed out for security purposes.
To re-enable your Portainer instance, you will need to restart Portainer.

Это не баг. Свежая панель без администратора — открытая дверь: кто первым дошёл до формы, тот и хозяин сервера. На создание первого пользователя даётся пять минут, дальше форма блокируется — лечится командой docker restart portainer.

В конфигурации выше этой гонки нет: администратор создан из секрета при первом запуске, экран первичной настройки не появляется. Вы входите логином admin и паролем из /opt/portainer/admin_password. Пока прокси не настроен, до панели добираются SSH-туннелем ssh -L 9443:127.0.0.1:9443 root@203.0.113.10, но спешить некуда.

После первого входа включите двухфакторную аутентификацию (My accountTwo-factor authentication) и уберите пароль с диска: удалите из compose.yaml блок secrets и флаг --admin-password-file, выполните docker compose up -d, затем shred -u /opt/portainer/admin_password. Флаг нужен только при первом запуске. Окружение local подключать не нужно, панель сама находит сокет.

HTTPS: Caddy перед панелью вместо самоподписанного сертификата

Portainer отдаёт интерфейс по HTTPS с самоподписанным сертификатом: браузер ругается каждый раз, а корпоративные политики его иногда не пропускают. Ставим перед панелью Caddy — он сам получит и продлит сертификат Let's Encrypt, без certbot и cron. Файл /opt/portainer/Caddyfile:

portainer.example.com {
    reverse_proxy https://portainer:9443 {
        transport http {
            tls
            tls_insecure_skip_verify
        }
    }
}

Сервис в compose.yaml:

  caddy:
    image: caddy:2-alpine
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile:ro
      - caddy_data:/data
      - caddy_config:/config

В блок volumes допишите caddy_data: и caddy_config:. Первый обязателен: в нём лежат сертификаты и ACME-аккаунт, иначе каждое пересоздание контейнера запрашивает новый сертификат и упирается в лимит Let's Encrypt — пять выпусков на один набор доменов в неделю.

Строка tls_insecure_skip_verify выглядит подозрительно, но здесь обязательна: без неё Caddy не примет самоподписанный сертификат Portainer, отдаст 502 и напишет в лог:

tls: failed to verify certificate: x509: certificate signed by unknown authority

Проверять сертификат бэкенда, до которого трафик не покидает хост, смысла нет. Альтернатива — проксировать на http://portainer:9000 без --http-disabled: проще, но внутри docker-сети трафик пойдёт открытым, что допустимо на одной машине и не годится между серверами.

Поднимаем docker compose up -d и проверяем curl -sI https://portainer.example.com | head -1: ответ HTTP/2 200 означает, что сертификат выпущен, панель работает по домену, а 9443 закрыт. При постоянных IP добавьте в блок домена @blocked not remote_ip 203.0.113.7 198.51.100.0/24 и respond @blocked 403. Честный минус: с динамическим IP такой список — ловушка, однажды вы получите 403 из отпуска. Тогда лучше оставить вход открытым, но с 2FA и длинным паролем, либо пускать в панель через WireGuard.

Второй хост, обновления и бэкап панели

Одна панель управляет несколькими хостами — на дополнительном сервере поднимается агент:

docker run -d --name portainer_agent --restart=always \
  -p 10.8.0.3:9001:9001 \
  -v /var/run/docker.sock:/var/run/docker.sock \
  -v /var/lib/docker/volumes:/var/lib/docker/volumes \
  portainer/agent:2.45.0

Порт опубликован на адрес 10.8.0.3 — приватная сеть или WireGuard-интерфейс. Агент не защищён паролем: отдадите 9001 на 0.0.0.0 — подарите первому сканеру root на этом сервере, а ufw deny, как мы помним, не спасает. Без приватной сети закрывайте порт правилом iptables -I DOCKER-USER -p tcp --dport 9001 ! -s 203.0.113.10 -j DROP. Хост добавляется в EnvironmentsAdd environmentDocker StandaloneAgent.

Обновление — смена тега в compose.yaml и docker compose pull && docker compose up -d. Важно: схема базы мигрирует вперёд, и откатить тег назад не выйдет — панель не стартует на «слишком новой» базе. Перед сменой ветки (2.39 → 2.45) снимайте архив тома:

docker run --rm -v portainer_data:/data:ro -v /root/backups:/backup alpine \
  tar czf /backup/portainer-$(date +%F).tar.gz -C /data .

Архив небольшой — единицы мегабайт даже на панели с десятками контейнеров: база хранит только настройки панели, пользователей, окружения и стеки. Отсюда важное: бэкап Portainer не является бэкапом ваших сервисов, тома с базами и файлы приложений резервируются отдельно.

Встроенный механизм тоже есть: SettingsBackup Portainer — архив с шифрованием паролем вручную или по расписанию, с выгрузкой в S3 либо Azure Blob; локальные копии ложатся в /data/scheduled-backups/, восстановление — вкладка Restore from backup на экране входа.

Какой сервер взять под Portainer в MAATRIX

Сама панель почти ничего не стоит: образ около 45 МБ, в простое 70 МБ памяти и доли процента CPU. Сервер выбирают не под Portainer, а под то, что под ним крутится.

ПрофильvCPURAMNVMeНа что хватит
Минимум12 ГБ25 ГБПанель, Caddy и 3–5 лёгких контейнеров: статика, бот, API
Рабочий24 ГБ50 ГБПлюс PostgreSQL, Redis, очередь, staging рядом с продом
Комфортный48 ГБ100+ ГБСборка образов, десяток сервисов, мониторинг, запас на пики

Честно про нижнюю границу: на 1 ГБ Portainer запустится, но первая же сборка через docker build или тяжёлый запрос к контейнерной СУБД упрутся в память, и OOM-killer погасит не тот процесс. Один гигабайт — стенд, а не прод.

Диск заканчивается быстрее, чем ожидают: слои старых образов и брошенные тома копятся молча. Расход смотрят через docker system df, чистят docker image prune -a (снесёт все образы, не используемые ни одним контейнером). Ограничьте и логи: в /etc/docker/daemon.json задайте "log-driver": "json-file" и "log-opts": { "max-size": "10m", "max-file": "3" }, затем systemctl restart docker — иначе разговорчивый контейнер за месяц съест десятки гигабайт. Настройка касается новых контейнеров, старые нужно пересоздать.

Локация. Если под панелью европейские сервисы, берите UK, Лондон: 5–25 мс до европейских точек обмена, 45–60 мс до Москвы (для веб-интерфейса неощутимо), плюс GDPR-соседство для клиентов из ЕС. Российская аудитория и персональные данные по 152-ФЗ — RU-площадка. Контейнеры, которые ходят к зарубежным ИИ-API, — US, Нью-Йорк: чистые IP и прямой доступ.

Оплата у MAATRIX идёт картой российского банка, через СБП, криптовалютой или токеном MAAT — иностранная карта не нужна даже для лондонской площадки. Апгрейд делается без переезда: тома и контейнеры остаются на месте, а Portainer после ресайза поднимется сам благодаря restart: unless-stopped.

Развернуть за пару минут

Готовый образ на VPS MAATRIX: NVMe, AMD EPYC, root-доступ. Локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Развернуть Portainer

Обсудить статью, задать вопрос или начать новую тему

Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.

Перейти в сообщество →

Частые вопросы

Какой тег образа ставить — latest, lts или точный номер?

Для продакшена точный номер вроде portainer/portainer-ce:2.45.0: обновление тогда происходит в запланированный момент. lts годится для домашнего стенда, latest предсказуемости не даёт.

Можно ли обойтись без монтирования /var/run/docker.sock?

Для управления локальным Docker — нет, панель работает именно через сокет. Риск снижают агентом или socket-proxy с ограниченным набором вызовов API, но часть функций тогда отвалится.

Хватит ли 1 ГБ RAM под панель и пару сервисов?

Панель займёт около 70 МБ, но запас исчезнет на первой же сборке образа или всплеске нагрузки на базу. Рабочий минимум для прода — 2 ГБ, а если собираете образы на сервере — 4 ГБ.

Нужны сами нейросети для контента?

Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.