AMX Mod X talks to MySQL through the MySQL module, which backs the sqlx include. That is what stats plugins, ban systems (AMXBans), shops and VIP systems use to persist data across map changes and across servers. Enabling it is three steps. Using it without freezing your server is the part people get wrong.
1. Enable the module
The module ships with the AMX Mod X base package but is commented out by default. Edit addons/amxmodx/configs/modules.ini:
fun engine fakemeta cstrike csx hamsandwich nvault mysql
Uncomment (or add) mysql. Confirm the file exists:
ls -la /home/steam/hlds/cstrike/addons/amxmodx/modules/mysql_amxx_i386.so file /home/steam/hlds/cstrike/addons/amxmodx/modules/mysql_amxx_i386.so
It must be ELF 32-bit LSB shared object, Intel 80386. The module statically carries what it needs to speak the MySQL wire protocol — you do not need to install a 32-bit MySQL client library on the host, and trying to is a common wild goose chase.
2. Configure sql.cfg
addons/amxmodx/configs/sql.cfg holds the default credentials that plugins pick up via get_cvar_string("amx_sql_host") and friends:
amx_sql_host "127.0.0.1" amx_sql_user "amxx" amx_sql_pass "a-real-password" amx_sql_db "amxx" amx_sql_table "amx_amxbans" amx_sql_type "mysql" amx_sql_timeout "60"
Two things that bite:
127.0.0.1andlocalhostare not the same to MySQL.localhostmeans a Unix socket;127.0.0.1means TCP. Grants are per-host, so'amxx'@'localhost'will not authenticate a TCP connection from'amxx'@'127.0.0.1'. Use127.0.0.1and grant to it.sql.cfgis world-readable if you are careless. It contains a plaintext database password.chmod 640it and make sure the HLDS user owns it.
3. Create the database and user
CREATE DATABASE amxx CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE USER 'amxx'@'127.0.0.1' IDENTIFIED BY 'a-real-password'; GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, INDEX ON amxx.* TO 'amxx'@'127.0.0.1'; FLUSH PRIVILEGES;
Do not grant ALL PRIVILEGES and do not use root. An AMXX plugin from a forum is not code you have audited; it should not be able to drop your database. If MySQL is on another host, grant to that host's IP and make sure MySQL is not listening on 0.0.0.0 without a firewall. Setup details: MariaDB for AMX Mod X.
4. Restart and verify
Modules load at startup, not at map change. Restart, then:
amxx modules
Currently loaded modules:
name version author status
[ 6] MySQL 1.9.0.5258 AMXX Dev Team running
Then confirm an actual connection. The cleanest test is a plugin that uses the database — install your stats or ban plugin and watch addons/amxmodx/logs/error_*.log at startup. A working connection is silent; a broken one is very loud.
5. Threaded queries, or your server will freeze
This is the single most important thing on this page. SQL_Execute() is blocking: the entire game loop stops until MySQL answers. On a healthy local socket that is a few milliseconds and you will not notice. When the database is on another host, or under load, or the network hiccups, it is 200ms — and every player on the server just froze for 200ms.
Use the threaded API instead. The pattern:
#include <amxmodx>
#include <sqlx>
new Handle:g_sqlTuple
public plugin_init()
{
register_plugin("Example SQL", "1.0", "you")
new host[64], user[32], pass[32], db[32]
get_cvar_string("amx_sql_host", host, charsmax(host))
get_cvar_string("amx_sql_user", user, charsmax(user))
get_cvar_string("amx_sql_pass", pass, charsmax(pass))
get_cvar_string("amx_sql_db", db, charsmax(db))
g_sqlTuple = SQL_MakeDbTuple(host, user, pass, db)
SQL_ThreadQuery(g_sqlTuple, "QueryHandler",
"CREATE TABLE IF NOT EXISTS players (steamid VARCHAR(32) PRIMARY KEY, points INT DEFAULT 0)")
}
public QueryHandler(failstate, Handle:query, error[], errnum, data[], size, Float:queuetime)
{
if (failstate != TQUERY_SUCCESS)
{
log_amx("SQL error %d: %s", errnum, error)
return
}
// read rows here with SQL_ReadResult / SQL_MoreResults
}
The rules: build the tuple once in plugin_init(), never inside a per-frame or per-spawn handler; always check failstate; never call SQL_FreeHandle() on a handle you did not create in that callback. Getting the last one wrong crashes the server — see crash in SQLx threaded queries.
Common errors
Access denied for user 'amxx'@'localhost' (using password: YES)— the grant does not match the host MySQL sees you coming from. Grant to'amxx'@'127.0.0.1'and setamx_sql_hostto127.0.0.1. Full walkthrough: Access denied from an AMXX plugin.Can't connect to MySQL server on '10.0.0.5' (111)— errno 111 is connection refused: MySQL is bound to127.0.0.1only, or a firewall is dropping 3306. See errno 111.Module failed to loadfor mysql — the.sois missing frommodules/, or it is 64-bit. Reinstall from the AMXX base package for your exact version.- Server stutters every round — a plugin using
SQL_Execute()on a hot path. Convert it toSQL_ThreadQuery(); see server freezes on MySQL query. Unknown database 'amxx'— the database name insql.cfgdoes not exist. Case matters on Linux.
Verification checklist
amxx modulesshows MySQL asrunning.- Your stats/bans plugin creates its tables — check with
SHOW TABLES;in the database. - A ban issued in-game appears as a row in MySQL.
- Data survives a map change and a server restart.
error_*.loghas no SQL lines after a full map.









