--- title: "How to Use the SQLite Module in AMX Mod X" excerpt: "Enable the SQLite module in AMX Mod X, where the .sq3 file lives, when SQLite beats MySQL, and how to avoid the locking problems." category: "installation" tags: "amxmodx, sqlite" image: "" read_time: "8 min read" ---
The SQLite module gives AMX Mod X a real SQL database with no server, no credentials, and no network. It uses the same sqlx API as MySQL, so the same plugin code works against either backend — only the connection tuple changes. For a single game server that needs to persist points, ranks, shop purchases or settings, SQLite is often the correct choice and MySQL is over-engineering.
| Situation | Use |
|---|---|
| One server, data local to it | SQLite |
| Several servers sharing ranks/bans | MySQL |
| A web panel reads the data live | MySQL |
| You want zero moving parts | SQLite |
| Heavy concurrent writes from many processes | MySQL |
SQLite is also a strictly better choice than nvault for anything structured. nVault is a flat key/value store with no queries and a well-earned reputation for corruption; see nVault corruption.
addons/amxmodx/configs/modules.ini:
fun engine fakemeta cstrike csx hamsandwich sqlite
Check the file is there and is 32-bit:
ls -la /home/steam/hlds/cstrike/addons/amxmodx/modules/sqlite_amxx_i386.so file /home/steam/hlds/cstrike/addons/amxmodx/modules/sqlite_amxx_i386.so
You do not need to install SQLite on the host. The module carries its own engine. Restart the server — modules load at startup, not on map change.
The SQLite module puts databases under:
cstrike/addons/amxmodx/data/sqlite/
The directory must exist and must be writable by the user running HLDS. If it is not, every query fails and — depending on the plugin — you get silence, not an error.
mkdir -p /home/steam/hlds/cstrike/addons/amxmodx/data/sqlite chown -R steam:steam /home/steam/hlds/cstrike/addons/amxmodx/data chmod 755 /home/steam/hlds/cstrike/addons/amxmodx/data/sqlite
Same sqlx API as MySQL. The only difference is the tuple: for SQLite the "database" is the file name and the host/user/pass are ignored.
#include <amxmodx>
#include <sqlx>
new Handle:g_tuple
public plugin_init()
{
register_plugin("SQLite Example", "1.0", "you")
// For SQLite the 4th argument is the database file (created if absent)
g_tuple = SQL_MakeDbTuple("", "", "", "server_stats")
SQL_ThreadQuery(g_tuple, "OnTableReady",
"CREATE TABLE IF NOT EXISTS points ( \
steamid TEXT PRIMARY KEY, \
name TEXT, \
points INTEGER DEFAULT 0 \
)")
}
public OnTableReady(failstate, Handle:query, error[], errnum, data[], size, Float:queuetime)
{
if (failstate != TQUERY_SUCCESS)
log_amx("SQLite error %d: %s", errnum, error)
}
Set amx_sql_type to sqlite in configs/sql.cfg if you use a plugin that reads the backend from there:
amx_sql_type "sqlite" amx_sql_db "server_stats"
That produces data/sqlite/server_stats.sq3.
Always escape values that come from a player. Names contain quotes, semicolons and worse, and SQLite is as injectable as MySQL:
new name[64], escaped[128], steamid[35], q[256]
get_user_name(id, name, charsmax(name))
get_user_authid(id, steamid, charsmax(steamid))
SQL_QuoteString(Empty_Handle, escaped, charsmax(escaped), name)
formatex(q, charsmax(q),
"INSERT OR REPLACE INTO points (steamid, name, points) VALUES ('%s', '%s', %d)",
steamid, escaped, points)
SQL_ThreadQuery(g_tuple, "OnSaved", q)
INSERT OR REPLACE is SQLite's upsert; MySQL uses ON DUPLICATE KEY UPDATE. This is the one place the two backends genuinely differ in a plugin, and it is why "SQLite and MySQL are drop-in interchangeable" is only 95% true.
Install the sqlite3 CLI on the host (this is for you, not for the module):
sudo apt install -y sqlite3 cd /home/steam/hlds/cstrike/addons/amxmodx/data/sqlite sqlite3 server_stats.sq3 sqlite> .tables sqlite> SELECT * FROM points ORDER BY points DESC LIMIT 10; sqlite> .quit
Do not run write queries against the file while the server is running. SQLite locks the whole database on write, and a long external transaction will make the game server's queries fail.
database is locked — two writers. Usually you left a sqlite3 shell open with an uncommitted transaction, or two game servers point at the same .sq3 file. One server, one file.unable to open database file — data/sqlite/ does not exist, or is not writable by the HLDS user. Check with sudo -u steam touch data/sqlite/test.Module failed to load — the .so is missing or is not 32-bit. See the module load fix.no such table: points — the plugin queried before its CREATE TABLE callback completed. Threaded queries are asynchronous; chain the first read off the create callback rather than firing both from plugin_init().CREATE TABLE is missing IF NOT EXISTS and failing silently.The whole database is one file, which is the best thing about SQLite:
sqlite3 server_stats.sq3 ".backup '/home/steam/backups/stats-$(date +%F).sq3'"
Use .backup, not cp — copying a file that is being written produces a corrupt database. Put that in cron.
amxx modules lists SQLite as running..sq3 file appears in data/sqlite/ after the first map.sqlite3 file.sq3 ".tables" shows your plugin's tables.If you later outgrow one server, the same plugin code moves to the MySQL module with a different tuple and an upsert rewrite.