Сброс пароля root в MySQL и MariaDB
Забыли пароль root? Скорее всего, он вам вообще не нужен, а если всё-таки нужен, вот как открыть базу данных ровно на одну минуту, не отдав её при этом половине интернета.
Пароль root у базы данных относится к тем паролям, которые задают один раз при установке и больше никогда не вспоминают, потому что все приложения работают под собственными учётными записями. Так продолжается до того дня, когда доступ всё-таки понадобится. И тогда на экране появляется вот это:
ERROR 1045 (28000): Access denied for user 'root'@'localhost' (using password: YES)
Стандартный ответ в интернете звучит так: остановить службу, запустить её с --skip-grant-tables, задать пароль, готово. Это верно, но только наполовину. В этом руководстве есть ещё и объяснение, почему растиражированная всюду команда на Ubuntu с MySQL 8 не даёт никакого эффекта, почему во многих случаях пароль вам вообще не нужен и что делать, если после всех действий служба перестала запускаться.
Сначала проверьте: скорее всего, пароль вам не нужен
На Debian и Ubuntu учётная запись root в базе данных уже много лет защищена не паролем, а личностью системного пользователя. В MariaDB этот механизм называется unix_socket, в MySQL он называется auth_socket. Смысл один и тот же: кто подключается через локальный сокет и на уровне операционной системы уже является root, тот попадает внутрь без пароля. Обоснование простое: системный пользователь root и без того имеет доступ ко всем файлам данных и к оперативной памяти процесса, поэтому дополнительный пароль никакого препятствия не создаёт.
Поэтому первая попытка всегда должна выглядеть так:
sudo mariadb
sudo mysql
Обратите внимание: sudo mysql -u root -p не сработает, потому что -p переключает вход на проверку пароля. Именно на этом спотыкается большинство. Без -p и с sudo вы, как правило, сразу оказываетесь в приглашении клиента. Оттуда новый пароль задаётся за две секунды, причём саму базу данных трогать вообще не придётся.
Второй запасной путь: сервисная учётная запись дистрибутива. На Ubuntu с пакетом mysql-server по-прежнему существует файл /etc/mysql/debian.cnf, в котором лежат данные доступа для учётной записи с полными правами, читать его может только root:
sudo ls -l /etc/mysql/debian.cnf
sudo mysql --defaults-file=/etc/mysql/debian.cnf -e "SELECT current_user();"
Если в ответ пришла строка, доступ есть и можно сразу работать дальше. Только не ждите здесь root@localhost: запрос отвечает debian-sys-maint@localhost. Так и должно быть, у этой сервисной учётной записи есть ALL PRIVILEGES, и для сброса пароля её вполне достаточно, но root вы при этом не являетесь. Кроме того, путь этот чисто дебиановский и убунтовский. На AlmaLinux, Rocky Linux и Oracle Linux нет ни каталога /etc/mysql/, ни самой учётной записи, там работает вход через сокет под root из предыдущего раздела. В MariaDB начиная с 10.4 такая учётная запись больше не создаётся заново (файл там обычно просто ссылается на root через сокет), но в уже существующих установках она часто ещё есть. Заглянуть туда ничего не стоит, а в случае удачи вы сэкономите себе всё оставшееся руководство.
Какая база данных здесь вообще работает?
Это не формальность, от ответа зависит каждая следующая команда. В официальном архиве Debian пакета mysql-server нет уже много лет, там практически всегда работает MariaDB, даже если команда mysql существует. Она представляет собой всего лишь символическую ссылку на клиент MariaDB. Сейчас в дистрибутивах картина такая:
| Система | mysql-server | mariadb-server |
| Debian 13 | отсутствует | 11.8 |
| Debian 12 | отсутствует | 10.11 |
| Ubuntu 24.04 | 8.0 | 10.11 |
| Ubuntu 22.04 | 8.0 | 10.6 |
Спрашивайте сам сервер, а не клиент:
systemctl list-units --type=service --all | grep -Ei 'mysql|mariadb'
mysqladmin --version
mariadbd --version
Ключ --all здесь обязателен, иначе list-units покажет только загруженные, активные юниты. Как раз в той ситуации, ради которой команда и нужна, то есть когда служба не работает, вывод остался бы пустым. mysqladmin --version есть в обоих мирах и называет движок прямым текстом, системы с MariaDB отвечают строкой вида ... Distrib 11.8.6-MariaDB .... Для mariadbd --version ответ command not found на чистой системе с MySQL это ожидаемый результат, а не ошибка; зеркально то же самое верно для специфичных для mysqld команд на чистой системе с MariaDB.
Если юнит называется mariadb.service, вы работаете с MariaDB. Если mysql.service, то с MySQL. На системах с MariaDB дополнительно существует псевдоним mysql.service, который указывает на mariadb.service, поэтому вывод systemctl list-units говорит больше, чем простое наличие имени.
Сами имена юнитов различаются между семействами дистрибутивов, а все дальнейшие вызовы systemctl в этом руководстве написаны под Debian и Ubuntu. В семействе Red Hat юнита с именем mysql.service нет вообще, там systemctl status mysql отвечает Unit mysql.service could not be found. В этом случае подставляйте везде имена из правого столбца, в том числе и в путь к каталогу drop-in:
| Что | Debian и Ubuntu | AlmaLinux, Rocky, RHEL |
|---|---|---|
| Юнит MariaDB | mariadb.service | mariadb.service |
| Юнит MySQL | mysql.service | mysqld.service |
| Каталог drop-in для MySQL | /etc/systemd/system/mysql.service.d/ | /etc/systemd/system/mysqld.service.d/ |
| Исполняемый файл сервера MySQL | /usr/sbin/mysqld | /usr/libexec/mysqld --basedir=/usr |
| Конфигурация | /etc/mysql/ | /etc/my.cnf и /etc/my.cnf.d/ |
Сначала изолируйте сервер: почему этот шаг не является необязательным
С ключом --skip-grant-tables сервер не считывает таблицы прав. Это означает не «root пускают без пароля», а «любой может всё, без пароля и под любым пользователем». В этом состоянии нет ни аутентификации, ни проверки прав, в том числе и для ваших клиентских баз данных.
MySQL 8 в этом случае автоматически включает ещё и skip_networking, то есть TCP-соединения больше не принимаются. Полагаться на это всё же не стоит, дописывайте опцию всегда сами. Для MariaDB это в любом случае задокументированная рекомендация, а тому, кто обслуживает обе системы, ни к чему держать в голове, какая из них додумывает за администратора.
--skip-grant-tables --skip-networking
Два момента, которые здесь охотно упускают. Первое: skip_networking закрывает только сетевой порт. Unix-сокет по пути /run/mysqld/mysqld.sock остаётся открытым, а на многих системах он доступен всем локальным пользователям. Скомпрометированный процесс PHP под www-data может в это окно вычитать любую базу данных на сервере. Поэтому держите окно как можно короче и не выполняйте операцию, пока работает веб-сервер с неизвестным кодом. Второе: заранее остановите всё, что подключается автоматически, то есть веб-сервер и прикладные службы. Их попытки подключения не просто мешают, они в этом состоянии идут с полными правами.
Если сервер доступен извне, дополнительно затяните файрвол. Как настроить его начисто и надолго, описано в статье Настройка файрвола UFW.
sudo apt-get install -y ufw
sudo ufw deny 3306/tcp
sudo ss -ltnp | grep 3306
Первая строка не лишняя: в минимальной установке Debian ufw не предустановлен, и команда иначе завершится сообщением sudo: ufw: command not found. На AlmaLinux, Rocky Linux и RHEL пакета ufw нет вовсе, там фильтрация пакетов идёт через firewalld:
sudo firewall-cmd --permanent --remove-service=mysql
sudo firewall-cmd --reload
Во всех проверенных установках Debian и Ubuntu параметр bind-address и так стоит на 127.0.0.1, это подтверждает ss -ltnp. В таком случае сервер снаружи соединений всё равно не принимает, и правило файрвола служит вторым рубежом, а не собственно защитой.
MariaDB: сброс пароля
Юнит MariaDB запускает сервер строкой ExecStart=/usr/sbin/mariadbd $MYSQLD_OPTS. Эта переменная предусмотрена ровно для таких случаев, поэтому править какой-либо файл не нужно. Проверьте это коротко, и вы будете знать, что описанный ниже путь на вашей системе сработает:
systemctl cat mariadb | grep ExecStart
Дальше в таком порядке:
sudo systemctl stop mariadb
sudo systemctl set-environment MYSQLD_OPTS="--skip-grant-tables --skip-networking"
sudo systemctl start mariadb
sudo mariadb -u root
В приглашении клиента первой идёт команда FLUSH PRIVILEGES. Без этого шага таблицы прав ещё вообще не загружены в память сервера, и он отклоняет любое управление учётными записями. После этого задавайте пароль:
FLUSH PRIVILEGES;
ALTER USER 'root'@'localhost' IDENTIFIED VIA unix_socket OR mysql_native_password USING PASSWORD('ВашНовыйПароль');
Эта громоздкая на вид запись сделана намеренно и представляет собой самый важный специфичный для MariaDB момент во всём руководстве. Начиная с 10.4 MariaDB умеет держать несколько методов аутентификации на одну учётную запись, и root после установки пакета настроен именно так: сначала сокет, в качестве замены пароль. Если вместо этого написать простое ALTER USER 'root'@'localhost' IDENTIFIED BY '...', вы замените всю цепочку чистой парольной аутентификацией. Работать это будет, но sudo mariadb без пароля после такого уже не пройдёт, а внутренние обслуживающие скрипты дистрибутива, рассчитанные на сокет, уйдут в пустоту. Проблему вы тем самым просто отложили на год.
Напоследок уберите особый режим:
sudo systemctl stop mariadb
sudo systemctl unset-environment MYSQLD_OPTS
sudo systemctl start mariadb
Не забудьте про unset-environment. Переменная привязана к менеджеру systemd, а не к службе, и переживает любой перезапуск службы. Если ночью обновление пакетов перезапустит MariaDB, ваша база данных с этого момента продолжит работать вообще без проверки прав, и никто этого не заметит. Сама по себе переменная исчезает только после перезагрузки сервера.
MySQL 8: команда из большинства руководств здесь ничего не делает
Для MySQL ходит тот же рецепт с systemctl set-environment MYSQLD_OPTS=.... Он взят из документации Oracle и подходит к её собственным пакетам. Но юнит из архива Ubuntu выглядит иначе, там стоит просто ExecStart=/usr/sbin/mysqld, без переменной. Команда отрабатывает, об ошибке не сообщает, а сервер всё равно стартует совершенно обычным образом, с активной проверкой прав. И вы сидите перед Access denied и недоумеваете. Проверьте сами:
systemctl cat mysql | grep ExecStart
Если $MYSQLD_OPTS там не встречается, вам понадобится drop-in-файл. А раз уж вы его всё равно создаёте, возьмите сразу лучший вариант: --init-file. С ним сервер стартует совершенно обычно, с проверкой прав, и при запуске выполняет SQL-файл с полными правами. Открытого временного окна, в которое кто-то может зайти без пароля, при этом не возникает. Oracle прямо рекомендует этот вариант вместо --skip-grant-tables.
sudo systemctl stop mysql
printf "ALTER USER 'root'@'localhost' IDENTIFIED WITH caching_sha2_password BY 'ВашНовыйПароль';\n" | sudo tee /var/lib/mysql-files/kh-reset.sql
sudo chown mysql:mysql /var/lib/mysql-files/kh-reset.sql
sudo chmod 600 /var/lib/mysql-files/kh-reset.sql
sudo mkdir -p /etc/systemd/system/mysql.service.d
Часть IDENTIFIED WITH caching_sha2_password здесь решающая и как раз она объясняет, почему бесчисленные руководства проваливаются именно в этом месте, не показывая никакой ошибки. На Debian и Ubuntu учётная запись root@localhost и в MySQL использует плагин auth_socket. Простое IDENTIFIED BY 'пароль' действительно записывает хеш пароля, но плагин не меняет. В mysql.user после этого по-прежнему стоит auth_socket, служба запускается чисто, ни одного сообщения об ошибке не появляется, и тем не менее любой вход по паролю заканчивается строкой ERROR 1698 (28000): Access denied for user 'root'@'localhost'. Особенно коварно вот что: кто делает контрольную проверку из-под системного пользователя root, того auth_socket пропускает без пароля, и сброс кажется удавшимся. Поэтому проверяйте из другой учётной записи или через TCP. Только для очень старых клиентов, которые не умеют caching_sha2_password, вместо него ставят mysql_native_password, с оговорками из абзаца ниже.
Каталог /var/lib/mysql-files выбран осознанно: он принадлежит пользователю базы данных и разрешён в профиле AppArmor для mysqld. Если положить файл в /root или /tmp, запуск при некоторых условиях сорвётся из-за AppArmor, а сообщение в журнале окажется малополезным. Теперь сам drop-in-файл:
[Service]
ExecStart=
ExecStart=/usr/sbin/mysqld --init-file=/var/lib/mysql-files/kh-reset.sql
Пустая первая строка ExecStart обязательна, иначе systemd допишет вашу команду к уже имеющейся и откажет службе в запуске с ошибкой конфигурации. Сохраните файл как /etc/systemd/system/mysql.service.d/override.conf, затем:
sudo systemctl daemon-reload
sudo systemctl start mysql
sudo mysql -u root -p
Если вход удался, уберите обе вещи: и SQL-файл, и drop-in-файл:
sudo rm -f /var/lib/mysql-files/kh-reset.sql
sudo rm -f /etc/systemd/system/mysql.service.d/override.conf
sudo systemctl daemon-reload
sudo systemctl restart mysql
Если вам всё же нужен классический путь, замените в drop-in-файле эту строку на ExecStart=/usr/sbin/mysqld --skip-grant-tables --skip-networking, подключитесь через sudo mysql и выполните там сначала FLUSH PRIVILEGES;, затем ALTER USER. Замечание про шифрование: MySQL 8 по умолчанию использует caching_sha2_password. Если после этого очень старое приложение начнёт сообщать «The server requested authentication method unknown to the client», поможет IDENTIFIED WITH mysql_native_password BY '...'. Но это тупик, потому что с версии 8.0.34 такой метод считается устаревшим, а в MySQL 8.4 его уже нет. Лучше обновить клиент.
Сообщения об ошибках дословно
ERROR 1290 (HY000): The MySQL server is running with the --skip-grant-tables option so it cannot execute this statement. Вы забыли FLUSH PRIVILEGES;. Выполните эту команду, после неё заработает и ALTER USER.
ERROR 1288 (HY000): The target table user of the UPDATE is not updatable. Вы следуете старому руководству, где предлагается UPDATE mysql.user SET password=.... Начиная с MariaDB 10.4 права лежат в mysql.global_priv, а mysql.user остался лишь представлением над ней. Берите ALTER USER или SET PASSWORD. Кстати, прямая запись в таблицы прав и раньше была прекрасным способом окончательно развалить учётную запись.
ERROR 1698 (28000): Access denied for user 'root'@'localhost'. Дело не в неверном пароле, а ровно наоборот: учётная запись ждёт аутентификации через сокет, а вы работаете не под системным пользователем root либо передали -p. Попробуйте ещё раз с sudo и без -p.
ERROR 1524 (HY000): Plugin 'unix_socket' is not loaded. Учётная запись ссылается на плагин, которого работающий сервер не знает; типичная ситуация после перехода с MariaDB на MySQL или после копирования каталога данных. Переведите учётную запись описанным выше способом на подходящий метод.
ERROR 2002 (HY000): Can't connect to local MySQL server through socket '/run/mysqld/mysqld.sock' (2). Сервер не запущен. Сначала посмотрите systemctl status и журнал, а не продолжайте эксперименты на стороне клиента.
mysqld: Can't create directory '/run/mysqld/' (Errcode: 13 - Permission denied) или запуск, который тут же обрывается. Так бывает, когда сервер запускали вручную, а не через systemd, потому что тогда каталог времени выполнения отсутствует. Как чинить:
sudo mkdir -p /run/mysqld
sudo chown mysql:mysql /run/mysqld
Job for mysql.service failed because the control process exited with error code. Само по себе это сообщение не говорит ни о чём. Настоящая причина стоит в журнале systemd и в журнале ошибок сервера:
sudo journalctl -u 'mysql*' -u 'mariadb*' -n 60 --no-pager
Шаблон с обоими именами юнитов выбран намеренно, потому что напрашивающийся запрос по одному имени ведёт в тихую ловушку. На Debian с MariaDB mysql.service это всего лишь псевдоним для mariadb.service. systemctl псевдоним разворачивает, а журнал индексирует записи под настоящим именем юнита, поэтому journalctl -u mysql отвечает -- No entries -- с кодом возврата 0, хотя журнал полон. И наоборот, journalctl -u mariadb на системе Ubuntu с MySQL тоже выдаст -- No entries --.
Похожая история с файлом ошибок. Всюду цитируемый /var/log/mysql/error.log существует только на Ubuntu с MySQL. На Debian с MariaDB каталога /var/log/mysql/ нет вообще, потому что log_error в 50-server.cnf закомментирован, а MariaDB пишет в журнал systemd. Подходящая строка в зависимости от системы:
| Система и сервер | Команда |
|---|---|
| Debian или Ubuntu, MariaDB | sudo journalctl -u mariadb -n 60 --no-pager |
| Ubuntu, MySQL | sudo tail -n 60 /var/log/mysql/error.log |
| AlmaLinux, Rocky, RHEL, MySQL | sudo tail -n 60 /var/log/mysql/mysqld.log |
| AlmaLinux, Rocky, RHEL, MariaDB | sudo tail -n 60 /var/log/mariadb/mariadb.log |
Если гадать про путь не хочется, спросите сам сервер: sudo mariadb -e "SHOW VARIABLES LIKE 'log_error';" или то же самое с mysql.
Три самые частые причины в этом месте: опечатка в drop-in-файле (тогда systemd назовёт эту строку), заполненный накопитель (об этом статья Диск заполнен на Linux-сервере) или второй процесс сервера, который ещё работает и держит файлы данных заблокированными. Последнее проверяется командой pgrep -a mariadbd или pgrep -a mysqld, прежде чем запускать службу заново.
Если после сброса вы внутрь попадаете, а ваши приложения по-прежнему получают отказ, проблема лежит в другом месте: приложения вроде WordPress или Nextcloud работают под собственными пользователями базы данных, а не под root. Подробности об этом в статье Как исправить ошибку MySQL «Access denied for user».
Как понять, что всё действительно получилось
Одного успешного входа как доказательства мало, ведь в аварийном режиме он проходит и вовсе без пароля. Поэтому проверьте четыре вещи после того, как служба снова работает в обычном режиме.
Первое: никакой особой конфигурации больше не действует. Первый вывод должен быть пустым, второй не должен показывать дополнительных опций.
systemctl show-environment | grep MYSQLD_OPTS
systemctl cat mariadb | grep ExecStart
Второе: проверка прав снова активна. Вход с намеренно неверным паролем обязан провалиться. Если он проходит, сервер до сих пор работает открытым.
Третье: новый пароль и ожидаемый метод записаны в учётной записи. В MariaDB это запрашивается так, в MySQL то же самое, только без столбца JSON:
sudo mariadb -e "SELECT user, host, plugin FROM mysql.user WHERE user='root';"
sudo mysql -e "SELECT user, host, plugin FROM mysql.user WHERE user='root';"
Четвёртое: сетевой порт снова ведёт себя как раньше. Сервер базы данных, который используется только локально, после уборки должен снова слушать исключительно на 127.0.0.1:
sudo ss -ltnp | grep 3306
grep -rs bind-address /etc/mysql/ /etc/my.cnf /etc/my.cnf.d/
Ключ -s и три пути указаны намеренно. Один только grep -r bind-address /etc/mysql/ в семействе Red Hat обрывается сообщением No such file or directory, потому что каталога /etc/mysql/ там нет. С ключом -s grep молча пропускает отсутствующие пути, и строка подходит для обоих миров.
Уборка, чтобы это не повторилось во второй раз
Пароль сейчас, возможно, лежит в местах, о которых вы не думаете. Команда с -e "ALTER USER ... IDENTIFIED BY '...'" попадает в ~/.bash_history, а команда, набранная в приглашении, в файл истории клиента базы данных. Вычистить нужно и то, и другое:
history -c
rm -f ~/.mysql_history ~/.mariadb_history
Оба имени файла нужны потому, что клиент сменил название. До MariaDB 10.11 включительно, то есть по Debian 12 включительно, он пишет в ~/.mysql_history. Начиная с MariaDB 11, то есть с Debian 13, он пишет в ~/.mariadb_history. Кто удалит там только старый файл, тот незаметно для себя оставит набранный открытым текстом пароль лежать на диске. Ещё лучше вообще не давать истории появиться: export MYSQL_HISTFILE=/dev/null или export MARIADB_HISTFILE=/dev/null перед сеансом, а то и сразу путь через --init-file из раздела про MySQL, при котором пароль вообще не проходит через интерактивный сеанс.
Разумнее пароля, который вы через год снова забудете, выглядит схема, где в повседневной работе пароль не нужен вовсе. На Debian и Ubuntu это означает: root остаётся на unix_socket или, соответственно, на auth_socket, а для всего остального заводятся обычные пользователи ровно с теми правами, которые нужны конкретному приложению. Кому нужен доступ с другой машины, тот пробрасывает его через SSH, вместо того чтобы открывать порт 3306, смотрите статью Подключение к серверу по SSH.
Раз уж вы всё равно заняты базой данных, доделайте сразу и остальное: удалить анонимных пользователей, удалить тестовую базу, отключить удалённый доступ для root. Это делает mariadb-secure-installation или, соответственно, mysql_secure_installation за несколько минут, подробно описано в статье Защита MariaDB и MySQL. А поскольку пароль редко бывает единственным, что не в порядке на только что принятом сервере, стоит пройтись по чек-листу для нового root-сервера.
Последняя мысль о порядке действий: прежде чем переводить работающий продуктивный сервер в состояние без проверки прав, сделайте резервную копию каталога данных или снимок. Сам сброс безобиден, а вот служба, которая после опечатки в юните перестала запускаться, в три часа ночи безобидной уже не выглядит.
Частые вопросы
Я забыл пароль root. Действительно ли нужно останавливать MySQL?
Почему я получаю ERROR 1290, хотя запустил сервер с skip-grant-tables?
Уязвим ли мой сервер во время сброса пароля?
В чём разница между MySQL 8 и MariaDB при сбросе пароля?
После сброса я захожу под root, но мой сайт по-прежнему сообщает Access denied. Почему?
Что произойдёт, если забыть про systemctl unset-environment?
2026 KernelHost GmbH. Все права защищены. Эта инструкция охраняется авторским правом. Публикация на других сайтах, в том числе частично или в изменённом виде, без нашего письменного согласия не разрешается. Цитирование с указанием источника и активной ссылкой мы приветствуем.

