Persistence without a database: the KVS
Most beginners think the only way to make data survive a restart is a database. It is not. FiveM ships a per-resource key-value store, the KVS, that writes small values straight to disk for you. No schema, no connection string, no SQL. One caution up front: the KVS stores values as plain, unencrypted data on the server's disk. Never put secrets in it (no passwords, API keys, Tebex/Discord tokens, or webhook URLs) -- those belong in environment-controlled config, not the KVS. In this lesson you build a counter that climbs every time you run a command, then survives a full server restart untouched. Once it works, you will know exactly when the KVS is the right tool and when it is the wrong one.
Build it
Make the resource folder
Inside your server's resources folder, create this folder:
resources/qu_kvs_counter
Create the files
Create this exact file layout:
resources/qu_kvs_counter/
fxmanifest.lua
server.lua
Write fxmanifest.lua
Open fxmanifest.lua and paste this:
fx_version 'cerulean'
game 'gta5'
server_script 'server.lua'
The KVS natives are available in Cfx scripting contexts, but this lesson deliberately uses them in a server_script so the saved counter is controlled by the server and not by a player's local client store. There is no lua54 'yes' line; Lua 5.4 is the only Lua runtime.
Write the lesson code
Open server.lua and paste this:
local KEY = 'visit_count'
-- Read the saved number on startup. An unset integer key reads as 0.
local count = GetResourceKvpInt(KEY)
print(('[qu_kvs_counter] loaded count from KVS: %d'):format(count))
RegisterCommand('countup', function()
count = count + 1
SetResourceKvpInt(KEY, count)
print(('[qu_kvs_counter] count is now %d (saved to disk)'):format(count))
end, true)
RegisterCommand('countshow', function()
print(('[qu_kvs_counter] current count: %d'):format(GetResourceKvpInt(KEY)))
end, true)
RegisterCommand('countreset', function()
DeleteResourceKvp(KEY)
count = 0
print('[qu_kvs_counter] count deleted from KVS, back to 0')
end, true)
Start and test it
Open server.cfg and add this line:
ensure qu_kvs_counter
Save, then run these two lines in the txAdmin Live Console, in order. refresh makes the server notice the new folder you just created; ensure then starts it:
refresh
ensure qu_kvs_counter
(If the resource was already running from a previous attempt, restart qu_kvs_counter reloads it instead. But a brand-new resource needs refresh first, or the console replies that it cannot find the resource.)
Run the counter up three times, then check it:
countup
countup
countup
countshow
Now the real proof: a cold boot of the whole server, the thing a database is supposed to survive. This is NOT restart qu_kvs_counter. Do a real full restart of the FXServer process: in the txAdmin web panel, open the dashboard and click Restart (the server-control button at the top), or in a raw console window press Ctrl+C to stop FXServer and then launch it again. When the resource loads, watch the startup line:
The number came back as 3 with no database anywhere. That is the KVS doing its job.
Keep reading the full lesson
Sign in to start, then unlock every step of this lesson and the full FiveM School with a membership.
- How it works
- Common mistakes
- What you can do now
- Try it yourself
The remainder of Persistence without a database: the KVS is available to FiveM School members.