To reset a MySQL root password on Linux, stop the database server, start it once with --skip-grant-tables, change the password, then restart it normally. This guide covers the full recovery on Ubuntu, Debian, AlmaLinux and Rocky, for both MySQL 8 and MariaDB, including the checks that stop you from leaving the server wide open.
The method works because --skip-grant-tables tells the server to ignore the privilege tables entirely, so any local user can connect as root without a password. That is also why you must combine it with --skip-networking and finish the job quickly.
Before you start
You need:
- SSH access to the VPS with a user that can run
sudo, or the browser console in your VPS provider's control panel if SSH is unavailable. - The name of your database service. On most systems it is
mysql; on MariaDB installs it ismariadb. - A new root password ready. Use a long random string and store it in your password manager.
Check which engine you are running:
systemctl status mysql --no-pager
systemctl status mariadb --no-pagerOne of the two will report active (running).
Tip: On Debian and Ubuntu, the MySQL and MariaDB root account often uses the auth_socket or unix_socket plugin. That means sudo mysql already logs you in without a password. Try it before doing any recovery:
sudo mysql -u root -e "SELECT VERSION();"If that prints a version number, you do not need skip-grant-tables at all. Just set a password for the account:
ALTER USER 'root'@'localhost' IDENTIFIED BY 'NewStrongPassword!';Stop the MySQL service
Stop the running server so the recovery instance can take over the socket and data directory.
sudo systemctl stop mysqlFor MariaDB:
sudo systemctl stop mariadbConfirm nothing is left running:
pgrep -a mysqldAn empty result is what you want. If a process is still listed, wait a few seconds and check again.
Start MySQL with skip-grant-tables
Warning: While the server runs with --skip-grant-tables, every local user has full access to every database and no password is checked. Always add --skip-networking so the server only listens on the local socket, and complete the next two steps without leaving the session.
Start the recovery instance in the background:
sudo mysqld_safe --skip-grant-tables --skip-networking &Give it a few seconds to open the socket, then connect:
sudo mysql -u rootYou should land on the mysql> prompt with no password prompt.
Note: mysqld_safe is deprecated in newer MySQL releases and may not exist on MySQL 8.4 and later. If you get mysqld_safe: command not found, start the server binary directly instead:
sudo mysqld --skip-grant-tables --skip-networking --user=mysql &Reset the MySQL root password
At the mysql> prompt, reload the grant tables first. Without this, MySQL refuses to run account statements while --skip-grant-tables is active.
FLUSH PRIVILEGES;Then set the new password:
ALTER USER 'root'@'localhost' IDENTIFIED BY 'NewStrongPassword!';If you also have a root@127.0.0.1 or root@::1 account, repeat the statement for each host value you see in SELECT user, host FROM mysql.user;.
On MariaDB 10.3 and older, ALTER USER is not available. Use:
SET PASSWORD FOR 'root'@'localhost' = PASSWORD('NewStrongPassword!');Leave the client:
exit;Restart MySQL normally
Shut down the recovery instance cleanly:
sudo mysqladmin shutdownThen start the service as usual:
sudo systemctl start mysqlBefore you move on, make sure skip-grant-tables was never written into a configuration file:
grep -r "skip-grant-tables" /etc/mysql/ /etc/my.cnf /etc/my.cnf.d/ 2>/dev/nullNo output means the setting only ever existed on the command line, which is correct.
Check it works
Log in with the new password:
mysql -u root -p -e "SELECT user, host, plugin FROM mysql.user WHERE user='root';"Enter the password when prompted. You should see the root rows and the authentication plugin in use.
Confirm the server is healthy and listening only where it should be:
sudo systemctl status mysql --no-pager
sudo ss -tlnp | grep 3306If port 3306 is bound to 0.0.0.0, decide whether remote access is intended. For a single-server setup, binding to 127.0.0.1 is the safer default.
Troubleshooting
ERROR 1290 (HY000): The MySQL server is running with the --skip-grant-tables option so it cannot execute this statement— you skippedFLUSH PRIVILEGES;. Run it first, then repeat theALTER USERstatement.Access denied for user 'root'@'localhost'after the restart — the password change was never committed. Stop the service and repeat the recovery, making sureFLUSH PRIVILEGES;runs before theALTER USERline.mysqld_safe: command not found— your MySQL build dropped the wrapper script. Startmysqlddirectly with the same flags, or add a temporary systemd drop-in withExecStart=/usr/sbin/mysqld --skip-grant-tables --skip-networking, then remove it after recovery.Can't connect to local MySQL server through socket '/var/run/mysqld/mysqld.sock'— the recovery instance is not running, or it uses a different socket path. Check the error log at/var/log/mysql/error.logand confirm the socket directory exists and is owned by themysqluser.ERROR 1064nearIDENTIFIED BYon MariaDB — you are on a release older than 10.4. Use theSET PASSWORD... = PASSWORD(...)form instead.- The server is reachable on port 3306 during recovery — you forgot
--skip-networking. Shut the instance down immediately withsudo mysqladmin shutdownand start again with both flags.
Next steps
Now that you are back in, tighten the account and protect the server around it:
- Reset the PostgreSQL postgres user password if you run both database engines.
- Automating MySQL backups with mysqldump and cron so a lost password is never the worst case.
- Disable root login and password authentication over SSH to close the other root account on the box.
- Linux security: hardening the sudoers file for least privilege to limit who can reach the database socket at all.
