--- 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.

When SQLite is the right call

SituationUse
One server, data local to itSQLite
Several servers sharing ranks/bansMySQL
A web panel reads the data liveMySQL
You want zero moving partsSQLite
Heavy concurrent writes from many processesMySQL

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.

1. Enable the module

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.

2. Where the database file lives

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

3. Connect from a plugin

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.

4. Writing rows safely

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.

5. Inspecting the database

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.

Common errors

Backups

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.

Verification

  1. amxx modules lists SQLite as running.
  2. The .sq3 file appears in data/sqlite/ after the first map.
  3. sqlite3 file.sq3 ".tables" shows your plugin's tables.
  4. A value written in-game survives a map change and a full restart.

If you later outgrow one server, the same plugin code moves to the MySQL module with a different tuple and an upsert rewrite.