VillageSQL is a drop-in replacement for MySQL with extensions.
All examples in this guide work on VillageSQL. Install Now →
Hardening by Deployment Context
Not every hardening step carries the same risk/friction tradeoff. Use this table to prioritize:| Step | Internet-facing production | Internal/private network | Dev / staging |
|---|---|---|---|
Run mysql_secure_installation | Required | Required | Recommended |
| Remove anonymous users | Required | Required | Recommended |
| Restrict root to localhost | Required | Required | Recommended |
| Remove test database | Required | Required | Optional |
validate_password component | Required | Required | Optional — can block quick iteration |
| Password expiration policy | Required | Recommended | Skip |
| Least-privilege app accounts | Required | Required | Recommended |
Restrict host in grants (no '%') | Required | Required | Optional |
REQUIRE SSL on accounts | Required | Recommended | Skip |
| Custom CA-signed TLS certificates | Required | Optional — self-signed acceptable | Skip |
bind-address to internal IP | Required | Recommended | Optional |
Disable local_infile | Required | Recommended | Optional |
skip_name_resolve | Recommended | Recommended | Optional |
| Lock or drop unused accounts | Required | Required | Optional |
'%' host grants in place is acceptable only if the instance is unreachable from outside your laptop or CI environment. If staging shares a network with production data, treat it as internal/private at minimum.
mysql_secure_installation
Run this immediately after installing MySQL on any server that isn’t a local dev machine:- Set the root password (if not already set)
- Remove anonymous users
- Disallow remote root login
- Remove the test database
- Reload privilege tables
Root Account Restrictions
Root should only connect fromlocalhost. Verify and enforce this:
'%' host entry, remove it:
Remove Anonymous Users and Test Database
Password Policy
MySQL 8.0+ includes thevalidate_password component, which enforces password complexity rules. It must be installed before use — mysql_secure_installation offers to install it automatically. To install manually:
LOW (length only), MEDIUM (length + character classes), STRONG (+ dictionary check).
Set password expiration for accounts that might be forgotten:
Least-Privilege User Accounts
Application accounts should have the minimum privileges needed. See MySQL User Management for the full GRANT syntax. Common patterns:'%'.
Auditing Existing Privileges
Review all accounts and their privileges:SSL/TLS Connections
MySQL 8.0 enables SSL/TLS by default and auto-generates certificates on startup. Verify it’s active:Network Access
Restrict MySQL to listen only on necessary interfaces. Inmy.cnf:
127.0.0.1 and never expose port 3306 externally. Firewall rules are a second layer of defense, not a substitute for restricting the bind address.
Disabling Risky Features
Turn off features your application doesn’t use:my.cnf:
Frequently Asked Questions
Is the default MySQL 8.4 install secure?
Better than older MySQL defaults, but still not hardened for production. MySQL 8.4 generates a random root password and enables SSL by default. You still need to: verify the root password is strong, remove anonymous users, restrict host access, and configure thevalidate_password component.
Should I run MySQL as root?
Never. MySQL should run as a dedicated low-privilege system user (typicallymysql). Running the server process as root means a MySQL exploit could lead to full system compromise.
Troubleshooting
| Problem | Solution |
|---|---|
ERROR 1045: Access denied for user 'root'@'%' | Root remote login is correctly blocked — create a named admin user with specific host access |
Application can’t connect after adding REQUIRE SSL | Client needs to use SSL flags: --ssl-mode=REQUIRED; verify server certificates are valid |
validate_password rejecting a password | Check current policy: SHOW VARIABLES LIKE 'validate_password%'; ensure the password meets all requirements |
| Can’t find root password on a new install | MySQL 8.0 logs the temporary root password to the error log on first start — find the log path with SELECT @@GLOBAL.log_error;, then grep 'temporary password' <path> |
Host '%' grants still visible after DELETE | Run FLUSH PRIVILEGES after any direct modifications to mysql.user |
See also
- MySQL User Management — creating users and granting least-privilege access
- Password Hashing in MySQL — securing application-level credentials stored in MySQL

