Устранение неполадок
Как пользоваться страницей: найдите свой случай по заголовку и выполните
команды по порядку. Ошибка — это последние строки вывода команды;
mitdev doctor в строке каждой проблемы сам подсказывает команду для
починки. (Совсем новичок? Термины и «как читать ошибки» — в
Азбуке.)
Первое действие при любой проблеме:
mitdev doctor # общая диагностика
sudo tail -50 /var/log/mitdev/errors.log # что именно упало
sudo less +G /var/log/mitdev/install.log # полный контекст (вывод команд)
Установка идемпотентна: исправив причину, просто перезапустите
sudo ./install.sh — успешные модули предложит пропустить. Ничего не
сломается, если запустить установку повторно.
Повторный запуск сохраняет настройки. Начиная с 2.27.1 установщик читает
предыдущие ответы из /var/lib/mitdev/install.env и подставляет их как
значения по умолчанию: порт SSH, deploy-пользователь, а политика входа
(root/пароли) берётся из текущего sshd_config.d/99-mitdev.conf. Поэтому если
просто принимать значения по умолчанию, порт SSH и правила файрвола не
«переедут» на 22 и службы не отвалятся. Уже установленные компоненты в списке
предотмечены; на «переустановить?» по умолчанию — «нет» (пропуск). Если вы
намеренно меняете порт SSH — не забудьте, что файрвол обновится под новый порт
в том же запуске.
Если при переустановке модуль падает и вы соглашаетесь на откат, откат работает в защищённом режиме: он не удаляет уже работавшие службы, пакеты, пользователей и роли (только отменяет новые изменения — конфиги, репозитории, правила файрвола). На первой установке откат по-прежнему полностью очищает частичную установку.
Установка
sudo ./install.sh → «command not found»
Две причины, проверяются одной командой:
ls -la install.sh && head -3 install.sh | cat -A
Файла нет (
lsругается) — перенос на сервер был неполным.В конце строк видно
^M— файлы испорчены Windows-переводами строк (CRLF): ядро ищет интерпретатор «bash\r» и не находит. Лечение:find . -type f \( -name '*.sh' -o -name mitdev -o -name '*.conf' \ -o -name '*.template' -o -name '*.mjs' \) -exec sed -i 's/\r$//' {} + chmod +x install.sh mitdev sudo ./install.sh
Чтобы не повторялось, переносите проект так:
curl -fsSL https://ваш-домен/get | sudo bash— сервер установки (SITE.md), tar передаёт байты без изменений;git clone …—.gitattributesпроекта принудительно даёт LF;- scp/rsync — безопасно; WinSCP — только в режиме Binary (текстовый режим конвертирует переводы строк и ломает скрипты).
/get устанавливает старую версию
Скрипт ставит ровно то, что отдаёт сайт. Проверьте, что сайт отдаёт свежее:
curl -s https://ваш-домен/version.txt # версия на сайте
mitdev version # версия на сервере
Если на сайте старая версия:
- Не перезалит
site/public/— пересоберите (node site/build.mjs --url https://ваш-домен) и загрузите заново; проверьте version.txt. - Кэш CDN/прокси (Cloudflare и т.п.) — очистите кэш для
/get,/mitdev.tar.gz,/version.txtили добавьте правило «не кэшировать» (готовый nginx-блок — в SITE.md). Начиная с 2.8.1/getсам обходит кэш (query-параметр) и печатает версию скачанного дистрибутива — расхождение видно сразу.
mitdev self-update падает: tmp: unbound variable (версия не поменялась)
Симптом: sudo mitdev self-update показывает экран успеха, но завершается
строкой вида /usr/local/bin/mitdev: line NNN: tmp: unbound variable, а
mitdev version остаётся прежней.
Причина: баг в старой версии самого self-update (cleanup-trap на EXIT
с локальной переменной tmp → под set -u падает уже после подмены каталога).
Это «проблема начальной загрузки»: сломанная версия не может корректно обновить
сама себя. Исправлено в свежем дистрибутиве (trap на RETURN), но чтобы его
получить, сервер нужно один раз обновить вручную — дальше self-update
работает штатно.
Ручное обновление (в обход self-update):
# 1. Скачать свежий дистрибутив и распаковать во временный каталог
cd /tmp
curl -fsSL https://mitdev.top/mitdev.tar.gz -o mitdev.tar.gz
sudo rm -rf /opt/mitdev.new && sudo mkdir -p /opt/mitdev.new
sudo tar -xzf mitdev.tar.gz -C /opt/mitdev.new --strip-components=1
grep MITDEV_VERSION /opt/mitdev.new/lib/core.sh # проверьте новую версию
# 2. Подменить установку (прежняя сохраняется в /opt/mitdev.prev для отката)
sudo rm -rf /opt/mitdev.prev
sudo mv /opt/mitdev /opt/mitdev.prev
sudo mv /opt/mitdev.new /opt/mitdev
sudo chmod +x /opt/mitdev/install.sh /opt/mitdev/mitdev
sudo ln -sf /opt/mitdev/mitdev /usr/local/bin/mitdev
hash -r # сбросить кэш путей shell
mitdev version # должна быть новая
Если экран успеха всё-таки прошёл, а версия старая — обычно достаточно поправить симлинк и сбросить кэш shell:
readlink -f /usr/local/bin/mitdev # куда указывает
grep MITDEV_VERSION /opt/mitdev/lib/core.sh # что реально лежит в /opt/mitdev
sudo ln -sf /opt/mitdev/mitdev /usr/local/bin/mitdev; hash -r
«APT занят другим процессом»
На свежих серверах несколько минут работает unattended-upgrades.
sudo fuser -v /var/lib/dpkg/lock-frontend # кто держит
# дождитесь завершения и повторите установку
«Нет доступа в интернет»
Preflight проверяет HTTPS до api.github.com и archive.ubuntu.com.
Проверьте DNS (resolvectl status), исходящий 443 и прокси, если он есть.
gum не установился / интерфейс «некрасивый»
Не критично: TUI автоматически деградирует до whiptail/dialog/plain. Установить gum вручную: повторите установку — модуль попробует снова (репозиторий Charm, затем GitHub-релиз).
Модуль упал, я откатил, что дальше?
Откат вернул систему к состоянию до модуля. Причина сбоя — в
errors.log; чаще всего это сеть/зеркала apt. Исправьте и перезапустите
инсталлятор — он продолжит с нужного места.
Система: обновления, swap, NTP упал с кодом 100
Смотрите полный вывод команды в errors.log, а не только последние строки TUI:
sudo less +G /var/log/mitdev/errors.log
Типичный случай на Ubuntu 22.04 — конфликт переходящих пакетов, например
fwupd зависит от старой версии библиотек, а APT уже видит новую. mitdev
использует apt-get dist-upgrade, чтобы такие переходы разрешались штатно.
Если локальный APT всё равно сломан, сначала восстановите его и повторите
установку:
sudo apt-get update
sudo apt-get -f install
sudo dpkg --configure -a
sudo apt-get dist-upgrade
sudo ./install.sh
Все ошибки команд целиком пишутся в /var/log/mitdev/errors.log, полный поток
установки — в /var/log/mitdev/install.log.
Где взять SSH-ключи для Termius?
После установки:
sudo mitdev ssh-key status
sudo mitdev ssh-key show deploy
sudo mitdev ssh-key show root
В Termius импортируйте private key через Keychain → New Key → Import/Paste
private key. Рекомендуемый профиль подключения — deploy-пользователь с sudo.
Root-ключ создаётся отдельно, но прямой root-вход может быть запрещён
настройкой PermitRootLogin no.
Хочу переустановить один модуль
Запустите sudo /opt/mitdev/install.sh, отметьте только нужный компонент,
на вопрос «уже выполнен, переустановить?» ответьте «Да». Либо снимите маркер
вручную: sudo rm /var/lib/mitdev/state/<модуль>.done.
SSH
«Connection refused» на порту 22
Сервер жив (ответил отказом), но на 22 никто не слушает или соединение отклоняется. Причины по частоте:
- Fail2Ban забанил ваш IP — предыдущие неудачные попытки входа
(включая publickey!) считаются атакой; бан выглядит именно как refused.
Через веб-консоль провайдера:
fail2ban-client status sshd # ваш IP в списке банов? fail2ban-client set sshd unbanip <ваш_IP> # чтобы не повторялось — свой IP в исключения: sed -i 's/^ignoreip = .*/& <ваш_IP>/' /etc/fail2ban/jail.local && systemctl restart fail2ban - SSH на нестандартном порту (вы задали его при установке):
в клиенте укажите свой порт; на сервере он виден в
grep SSH_PORT /var/lib/mitdev/install.envиss -tlnp | grep sshd. - Сервер ещё загружается — подождите 1–2 минуты после перезагрузки.
- Служба ssh не поднялась:
systemctl status ssh→systemctl enable --now ssh(через консоль провайдера).
«Authentication failed (publickey)» — не пускает по ключу
Сервер принимает только ключи (пароли вы отключили при установке), а предложенный ключ не подходит. Причины по частоте:
- Ключ не прикреплён к хосту в Termius — Keychain-ключ есть, но в настройках Host поле Key пустое. Откройте Host → выберите ключ явно.
- Логин и ключ не совпадают: deploy-ключ работает только с Username
deploy, root-ключ — только сroot(и только если root-вход по SSH не был запрещён при установке). - Импортирован публичный ключ вместо приватного, или вставлен не весь
блок (нужны строки
-----BEGIN…-----и-----END…-----целиком).
Первое, что стоит попробовать: сменить Username на deploy — root получите
через sudo -i.
Полный локаут (не пускает никак): откройте веб-консоль в панели провайдера (VNC/KVM — это не SSH, запрет паролей на неё не действует), войдите root с паролем и:
mitdev ssh-key show deploy # приватный ключ для импорта в Termius
sudo cat /home/deploy/.ssh/authorized_keys # что реально примет сервер
sudo journalctl -u ssh -n 20 # причина отказа (отпечаток ключа)
# либо временно вернуть пароли по SSH:
sed -i 's/^PasswordAuthentication no/PasswordAuthentication yes/' /etc/ssh/sshd_config.d/99-mitdev.conf
sshd -t && systemctl restart ssh
Сменил порт и не могу подключиться
ssh -p <новый_порт> <пользователь>@<ip>
Если не помогло (со старой сессии или из консоли провайдера):
sudo sshd -t # конфиг валиден?
sudo ss -tlnp | grep sshd # какой порт реально слушается
sudo ufw status # порт открыт в файрволе?
sudo systemctl status ssh ssh.socket
Экстренный возврат к дефолту:
sudo rm /etc/ssh/sshd_config.d/99-mitdev.conf
sudo systemctl enable --now ssh.socket && sudo systemctl restart ssh
Fail2Ban забанил мой IP
sudo fail2ban-client status sshd # список банов
sudo fail2ban-client set sshd unbanip <ваш_ip>
Docker
permission denied у deploy-пользователя
Членство в группе docker применяется при новом входе:
exit && ssh deploy@server # или: newgrp docker
hello-world не прошёл при установке
sudo systemctl status docker
sudo journalctl -u docker -n 50
В LXC/OpenVZ-контейнерах Docker может требовать вложенной виртуализации — preflight предупреждает об этом типе виртуализации заранее.
Nginx
nginx -t падает после моих правок
sudo nginx -t # покажет файл и строку
ls /etc/nginx/sites-available/*.mitdev-bak.* # резервные копии mitdev
Порт 80/443 занят
sudo lsof -i :80 -i :443
Типовой конфликт — traefik из k3s (см. раздел Kubernetes).
Kubernetes (k3s)
Узел не Ready
sudo journalctl -u k3s -n 100
mitdev k8s nodes
Частая причина на малых VPS — нехватка RAM (k3s требует ~512 МБ сверх нагрузки).
Nginx не стартует после установки k3s (или наоборот)
Оба претендуют на 80/443. При установке в одном прогоне mitdev отключает traefik автоматически. Если k3s ставился отдельно и traefik уже работает:
# отключить traefik в пользу host-nginx:
sudo tee -a /etc/rancher/k3s/config.yaml <<< 'disable: ["traefik"]'
sudo systemctl restart k3s
sudo kubectl -n kube-system delete helmchart traefik traefik-crd --ignore-not-found
Под в CrashLoopBackOff после k8s deploy
mitdev k8s logs <имя>
kubectl describe pod -l app=<имя>
Проверьте, что порт в k8s deploy совпадает с портом, который слушает
приложение внутри контейнера, и что образ доступен (imagePullPolicy: Always).
Плейбук: отказал primary PostgreSQL
При watchdog v2 система полностью чинит себя сама — включая возврат узлов. Хронология без вашего участия:
| Время | Что происходит автоматически |
|---|---|
| 0 с | primary перестал отвечать |
| ~30 с | watchdog основной реплики дважды перепроверил и выполнил promote |
| +2–5 с | прокси/VIP переключились; драйверы переподключились |
| +~30 с | вторая реплика увидела нового primary через эндпоинты ролей и сама перецепилась (смена primary_conninfo, без копирования данных) |
| позже | ожившый старый primary видит primary со старшим timeline → сам понижается (pg_rewind) и возвращается репликой |
Итого: простой записи ~30–40 секунд, никаких ручных действий, split-brain исключён fencing'ом по timeline.
Ваша единственная задача — убедиться, что всё сошлось (когда удобно):
mitdev pg status # кто primary
mitdev pg replication # обе реплики streaming?
journalctl -t mitdev-pg-watchdog -n 30 # что делала автоматика
Автоматика не справилась (нет сети до пиров, pg_rewind не смог)? Журнал
watchdog скажет, что именно; ручные запасные ходы прежние: mitdev pg promote и mitdev pg follow <ip>.
Плейбук: недоступен сервер Kubernetes
HA-кластер (3 сервера, режимы init/join): ничего не делать. Поды с
упавшего сервера автоматически пересоздаются на живых (~1–5 минут, реплики
deployment'ов разнесены по серверам анти-аффинити — сервис не прерывался).
Вернувшийся сервер сам входит в кластер. Проверка: mitdev k8s nodes
(упавший — NotReady, потом снова Ready).
Развернуть такой кластер: на первом сервере install.sh → Kubernetes →
режим init; mitdev k8s token выдаст URL и токен; на остальных двух —
режим join. Домен приложений направьте на все серверы (DNS round-robin
или LB провайдера). Кворум etcd требует нечётного числа серверов (3 или 5);
при трёх серверах кластер переживает отказ любого одного.
Одиночный k3s: приложения недоступны до возвращения сервера; после старта всё поднимается само:
mitdev k8s status && mitdev k8s pods
sudo journalctl -u k3s -n 50 # если узел не Ready
sudo systemctl restart k3s
Кластер PostgreSQL
Реплика не подключается (pg_basebackup: connection refused / no pg_hba entry)
На primary проверьте по порядку:
sudo -u postgres psql -c 'SHOW listen_addresses' # должно быть *
sudo grep mitdev-replication /etc/postgresql/*/main/pg_hba.conf # есть IP реплики?
sudo ufw status | grep 5432 # правило для IP реплики?
mitdev pg add-replica <ip_реплики> # добавляет всё разом
Пароль — replication_password из /root/.mitdev-credentials на primary.
Реплика: Проверка доступности primary … no response (pg_isready exit 2)
Модуль реплики останавливается на первой же проверке и локальный PostgreSQL не трогает — до порта 5432 на primary не достучаться с этого узла. Отличать:
- раньше была ошибка
password authentication failed→ 5432 был доступен (проблема была в пароле); no response(таймаут) → 5432 недоступен — primary не слушает снаружи или блокирует файрвол.
Частая причина: на primary ранее сняли оверлей кластера
(mitdev remove pgcluster / mitdev pg cluster-remove) — это удаляет
listen_addresses='*', и primary вернулся к прослушиванию только localhost
(а --purge ещё и удалил роль репликации).
Починка на primary:
# 1. Восстановить приём реплик (add-replica теперь сам чинит listen_addresses):
sudo mitdev pg add-replica <ip_реплики>
# add-replica вернёт listen_addresses='*', при необходимости перезапустит
# PostgreSQL и в конце покажет, слушает ли 5432 на всех интерфейсах.
# 2. Если роль репликации была удалена (--purge) — add-replica об этом скажет.
# Восстановите роль primary через установщик (идемпотентно):
sudo /opt/mitdev/install.sh # выбрать PostgreSQL-кластер → роль primary
# 3. Проверить, что порт открыт наружу и файрвол пропускает:
sudo ss -tlnp | grep 5432 # ожидается 0.0.0.0:5432 / *:5432, не только 127.0.0.1
sudo ufw status | grep 5432
С самой реплики проверьте связность (файрвол провайдера тоже должен пропускать 5432 с IP реплики — это отдельный от UFW уровень):
nc -zv <ip_primary> 5432 # timeout → сеть/файрвол; refused → PostgreSQL не слушает
pg_isready -h <ip_primary> -p 5432
mitdev doctor: «неактивных слотов: N (копится WAL!)»
Реплика отвалилась, а её слот держит WAL — диск primary будет заполняться. Верните реплику в строй или уберите её из кластера штатной командой (снимет слот, доступ pg_hba и правило файрвола):
mitdev pg remove-replica <ip_реплики> # на primary — всё разом
Вручную (если нужно точечно):
sudo -u postgres psql -c "SELECT slot_name, active FROM pg_replication_slots"
sudo -u postgres psql -c "SELECT pg_drop_replication_slot('<slot_name>')"
Полностью убрать реплику: на primary — mitdev pg remove-replica <ip>, затем
на самой реплике — mitdev remove pgcluster --purge (удалит её БД) или
mitdev remove postgresql --purge (удалит PostgreSQL целиком).
Отставание реплики растёт
mitdev pg replication на primary покажет отставание в байтах WAL.
Причины по частоте: медленный диск реплики, сеть, массовые записи на primary.
Разово реплика догонит сама; хронически — усильте реплику.
Авто-failover не сработал / сработал неожиданно
Журнал watchdog:
mitdev pg watchdog status # служба, конфиг, последние события
journalctl -t mitdev-pg-watchdog -n 50 # полная история
Не сработал — проверьте, что служба активна (mitdev pg watchdog enable)
и что в /etc/default/mitdev-pg-watchdog AUTO_PROMOTE=yes.
Сработал от кратковременного сбоя — увеличьте FAIL_THRESHOLD или
CHECK_INTERVAL в том же файле и перезапустите:
sudo systemctl restart mitdev-pg-watchdog.
После promote старый primary вернулся в сеть
Не подключайте его обратно как есть — это split-brain. Пересоздайте его репликой нового primary одной командой на нём:
mitdev pg follow <ip_нового_primary>
(предварительно на новом primary: mitdev pg add-replica <ip_старого>).
Приложение с другого сервера не подключается к VIP
Проверяйте по порядку:
# 1. С сервера приложения: маршрут и порт
nc -zv <VIP> 5432 # timeout → сеть/UFW; refused → PostgreSQL/pg_hba
# 2. На DB-узлах: доступ открыт?
sudo grep mitdev-app-access /etc/postgresql/*/main/pg_hba.conf
sudo ufw status | grep 5432
# если пусто — откройте (на КАЖДОМ узле):
sudo mitdev pg allow <ip-приложения>
# 3. Ошибка "no pg_hba.conf entry" в логах приложения
# → IP приложения не совпадает с разрешённым (NAT? другой интерфейс?)
# → смотрите фактический адрес: sudo tail /var/log/postgresql/*.log
Серверы приложений должны быть в одной приватной сети с DB-узлами (или иметь маршрут до подсети VIP).
Прокси (mitdev pg proxy) не находит primary
# на сервере приложений:
sudo systemctl status haproxy
curl -s http://<ip-узла>:8008/ # primary|replica с каждого DB-узла
echo "show stat" | sudo socat stdio /run/haproxy/admin.sock 2>/dev/null | cut -d, -f1,2,18 | column -s, -t
# на DB-узлах:
sudo systemctl status mitdev-pg-role.service
sudo journalctl -u mitdev-pg-role.service -n 50 # история/ошибки socat
curl -s http://127.0.0.1:8008/ # локально отвечает?
sudo ufw status | grep 8008 # порт открыт для сервера приложений?
Эндпоинт роли — персистентный листенер на socat (юнит
mitdev-pg-role.service, зависимость socat ставится модулем автоматически).
Раньше это был socket-activated юнит (mitdev-pg-role.socket +
[email protected]), который systemd поднимал заново на каждое
подключение; при частых опросах HAProxy это упиралось в systemd
start-limit, часть коннектов рвалась по RST, HAProxy метил primary как DOWN
и сыпал каскадные 500/503 — поэтому от socket-activation отказались.
Если чек-порт закрыт (mitdev pg allow <ip-приложения> не выполнялся на
узле) — HAProxy пометит узел как DOWN, даже когда PostgreSQL жив.
VIP не переезжает / не поднимается
mitdev pg vip # держатель, служба, события
journalctl -u keepalived -n 50 # полная история VRRP
Частые причины:
- VRRP заблокирован файрволом — модуль добавляет
proto 112в/etc/ufw/before.rules; проверьтеgrep mitdev-vrrp /etc/ufw/before.rulesи что промежуточный сетевой экран провайдера пропускает proto 112 между узлами; - IP пиров указаны неверно (unicast VRRP шлёт пакеты только им) —
поправьте
unicast_peerв/etc/keepalived/keepalived.conf; - один
virtual_router_idуже используется другим кластером в этой сети — задайте другойPG_VIP_VRID; - в облаке (Hetzner Cloud, DO, AWS) «чужой» IP на интерфейсе может блокироваться anti-spoofing — используйте floating IP провайдера или разрешите IP в панели (alias IP).
mitdev doctor отдельно сигналит опасную ситуацию: VIP поднят на узле,
который является репликой.
Разное
mitdev: command not found
sudo ln -sf /opt/mitdev/mitdev /usr/local/bin/mitdev
Где пароли служб?
sudo cat /root/.mitdev-credentials
Внимание: пароль deploy-пользователя здесь НЕ хранится (в файле — только пароли служб: БД, Redis, RabbitMQ, MinIO). См. пункт ниже.
sudo: «пароль не подходит» после установки
Симптом: вход по SSH-ключу работает, но sudo отклоняет пароль
(«Sorry, try again» / «пароль не подходит»).
Причина: при установке в шаге «Пароль для deploy (пусто — не менять)» пароль
оставили пустым. Пользователь создаётся с заблокированным паролем —
вход по ключу возможен, а sudo (пользователь в группе sudo) требует пароль,
которого нет. Пароль deploy-пользователя нигде не сохраняется — его нужно
задать заново.
Проверка статуса пароля (замените deploy на своё имя — mitdev info или
grep DEPLOY_USER /var/lib/mitdev/install.env):
sudo passwd -S deploy # 2-е поле: P = пароль задан, L = заблокирован, NP = нет пароля
Если L или NP — задайте пароль и разблокируйте аккаунт от root
(зайдите по root-SSH-ключу — mitdev ssh-key show root — или через консоль
провайдера):
passwd deploy # задать пароль
passwd -u deploy # снять блокировку, если была
id deploy # убедиться, что в группе sudo (RHEL/Alma: wheel)
# если нет: usermod -aG sudo deploy # Debian/Ubuntu
# usermod -aG wheel deploy # RHEL/Alma/Rocky
su - deploy -c 'sudo -k; sudo whoami' # проверка: спросит пароль → выведет root
Профилактика: при новой установке задавайте непустой пароль для
deploy-пользователя. Нужен вход только по ключу с рабочим sudo без пароля —
это отдельная настройка (NOPASSWD), сейчас установщик её не делает.
Диск заполнен
mitdev doctor # покажет процент
mitdev clean # apt + journald + (опц.) docker prune
ncdu / # найти крупное вручную
Ничего не помогло
Откройте issue с выводом mitdev version, mitdev doctor,
фрагментами errors.log/install.log вокруг сбоя и типом виртуализации
(systemd-detect-virt). Шаблон — в CONTRIBUTING.md.