Working with MySQL rarely involves just the root user. Each application, developer, or service needs its own account with specific access rights. This is a fundamental security principle: minimal privileges for performing specific tasks.
Let's explore how to create MySQL users, assign them permissions, and manage database access.
Before creating users, you need to log into the MySQL console with an account that has sufficient privileges (typically root):
mysql -u root -p
After entering the password, you'll enter the MySQL interactive console. All subsequent commands are executed within it.
Basic syntax for user creation:
CREATE USER 'username'@'host' IDENTIFIED BY 'password';
Breaking down the components:
username — the user's namehost — the host from which connections are allowedpassword — the user's passwordUser for local connection:
CREATE USER 'webapp'@'localhost' IDENTIFIED BY 'SecurePass123!';
This user can only connect from the same server where MySQL is installed.
User for remote connection from specific IP:
CREATE USER 'admin'@'192.168.1.100' IDENTIFIED BY 'AnotherPass456!';
Access is only allowed from IP address 192.168.1.100.
User with access from any host:
CREATE USER 'remote_user'@'%' IDENTIFIED BY 'RemotePass789!';
The % symbol means "any host". Use this cautiously — open access creates security risks.
User for connections from specific subnet:
CREATE USER 'network_user'@'192.168.1.%' IDENTIFIED BY 'NetworkPass321!';
Access for any address from the 192.168.1.0/24 range.
After creating a user, they have no permissions. You can assign them using the GRANT command.
Basic syntax:
GRANT privileges ON database.table TO 'user'@'host';
MySQL supports a detailed permission system. Here are the main privileges:
Read permissions:
SELECT — data retrieval
Write permissions:
INSERT — adding recordsUPDATE — modifying dataDELETE — removing recordsStructure permissions:
CREATE — creating tables and databasesALTER — modifying table structureDROP — deleting tables and databasesINDEX — managing indexesAdministrative permissions:
CREATE USER — creating usersGRANT OPTION — transferring your permissions to othersRELOAD — reloading privilegesSHUTDOWN — stopping the serverPROCESS — viewing processesSpecial privilege:
ALL PRIVILEGES — all available permissions (except GRANT OPTION)
Full access to specific database:
GRANT ALL PRIVILEGES ON shop_db.* TO 'webapp'@'localhost';
User webapp receives all permissions for the shop_db database and all its tables (the * symbol).
Read-only access to database:
GRANT SELECT ON analytics_db.* TO 'analyst'@'192.168.1.50';
Useful for analysts or reporting systems that don't need to modify data.
Read and write permissions without structure modification:
GRANT SELECT, INSERT, UPDATE, DELETE ON blog_db.* TO 'cms_user'@'localhost';
Typical permission set for applications: work with data but don't touch table structure.
Access to specific table:
GRANT SELECT, UPDATE ON shop_db.orders TO 'order_manager'@'localhost';
Even more precise control — permissions only for the orders table in the shop_db database.
Access to all databases on server:
GRANT ALL PRIVILEGES ON *.* TO 'superuser'@'localhost';
The *.* construction means "all databases, all tables". This is almost equivalent to root access.
Permission to transfer your privileges:
GRANT SELECT, INSERT ON project_db.* TO 'team_lead'@'localhost' WITH GRANT OPTION;
The WITH GRANT OPTION allows the user to transfer their permissions to other users.
After assigning permissions in older MySQL versions, you needed to execute:
FLUSH PRIVILEGES;
This command forces MySQL to re-read the privilege tables. In modern versions (MySQL 5.7+), GRANT and REVOKE commands automatically apply changes, but executing FLUSH PRIVILEGES is still safe and often recommended for assurance.
To see what permissions are assigned to a specific user:
SHOW GRANTS FOR 'username'@'host';
Example:
SHOW GRANTS FOR 'webapp'@'localhost';
The output will show all GRANT commands that were executed for this user.
In MySQL 5.7 and newer:
ALTER USER 'username'@'host' IDENTIFIED BY 'new_password';
In older versions:
SET PASSWORD FOR 'username'@'host' = PASSWORD('new_password');
Example:
ALTER USER 'webapp'@'localhost' IDENTIFIED BY 'NewSecurePass999!';
If you need to remove specific privileges from a user, use REVOKE:
REVOKE privilege ON database.table FROM 'user'@'host';
Examples:
Remove delete permission:
REVOKE DELETE ON shop_db.* FROM 'webapp'@'localhost';
Revoke all permissions on database:
REVOKE ALL PRIVILEGES ON blog_db.* FROM 'cms_user'@'localhost';
Remove ability to transfer permissions:
REVOKE GRANT OPTION ON project_db.* FROM 'team_lead'@'localhost';
After revoking permissions, it's also recommended to execute FLUSH PRIVILEGES;.
When a user is no longer needed, you can delete them:
DROP USER 'username'@'host';
Example:
DROP USER 'old_app'@'localhost';
⚠️ Important: when deleting a user, all their permissions are automatically revoked. If you try to delete a non-existent user, MySQL will throw an error.
CREATE USER 'wordpress'@'localhost' IDENTIFIED BY 'wp_strong_pass_2024';
GRANT SELECT, INSERT, UPDATE, DELETE ON wordpress_db.* TO 'wordpress'@'localhost';
FLUSH PRIVILEGES;
The application gets permissions for basic data operations but cannot modify database structure.
CREATE USER 'backup_user'@'localhost' IDENTIFIED BY 'backup_secure_pwd';
GRANT SELECT, LOCK TABLES, SHOW VIEW ON *.* TO 'backup_user'@'localhost';
FLUSH PRIVILEGES;
This user can read all databases and lock tables to create consistent backups.
CREATE USER 'db_admin'@'192.168.1.%' IDENTIFIED BY 'admin_pass_123';
GRANT ALL PRIVILEGES ON app_*.* TO 'db_admin'@'192.168.1.%';
FLUSH PRIVILEGES;
The administrator gets full access to all databases whose names begin with app_.
CREATE USER 'log_reader'@'%' IDENTIFIED BY 'logs_read_only';
GRANT SELECT ON logs_db.access_logs TO 'log_reader'@'%';
GRANT SELECT ON logs_db.error_logs TO 'log_reader'@'%';
FLUSH PRIVILEGES;
A monitoring system gets access only to log tables and cannot modify anything.
SELECT User, Host FROM mysql.user;
This command displays a list of all users in the system and the hosts from which they can connect.
RENAME USER 'old_name'@'localhost' TO 'new_name'@'localhost';
All user permissions are preserved during renaming.
If you're already connected to MySQL and want to know which user you're working as:
SELECT USER();
%' unnecessarily. If the application works locally — specify localhost. If remotely — a specific IP.ALL PRIVILEGES if SELECT, INSERT, UPDATE is sufficient. Don't grant permissions on all databases (*.*) if only one is needed.CREATE USER 'secure_user'@'%' IDENTIFIED BY 'password' REQUIRE SSL;
Error: ERROR 1396 (HY000): Operation CREATE USER failed for 'user'@'host'
Cause: user already exists.
Solution: check existing users with SELECT User, Host FROM mysql.user; and either delete the old user or use a different name.
Error: ERROR 1045 (28000): Access denied for user 'webapp'@'localhost'
Cause: incorrect password or user doesn't have permission to connect from this host.
Solution: verify the password. Make sure the user was created for the host from which the connection is being made (localhost and 127.0.0.1 are different hosts for MySQL).
Error: application cannot work with database after user creation.
Cause: forgot to assign permissions or execute FLUSH PRIVILEGES.
Solution: check permissions with SHOW GRANTS FOR 'user'@'host'; and assign missing privileges.
Error: ERROR 1044 (42000): Access denied for user 'user'@'host' to database 'db_name'
Cause: user doesn't have permissions for the specified database.
Solution: assign permissions with the GRANT command.
For MySQL, these are different things:
user'@'localhost' — connection via Unix socket (faster, more secure)user'@'127.0.0.1' — connection via TCP/IP to local addressIf you create a user for localhost but the application tries to connect to 127.0.0.1, MySQL will deny access. In such cases, create two users or use %.
Managing users and permissions in MySQL is a fundamental skill for any administrator. Proper access configuration protects data from unauthorized use and minimizes the consequences of potential compromises.
Core principles: give users only the permissions they actually need, restrict access by hosts, use strong passwords, and regularly review the list of active accounts. These simple rules will ensure basic security for your database.