Roblox DataStores
Persist data across sessions in Roblox with DataStoreService: loading on join,
saving on leave and shutdown, safe updates, retries, and ordered stores for
leaderboards. Server-side only.
When to use
- Use to save/load player progress (coins, inventory, levels), build persistent leaderboards, or fix data loss, overwrites, and throttling.
- Use when server code calls
DataStoreService,GetDataStore,GetAsync,SetAsync,UpdateAsync, orGetOrderedDataStore.
When not to use: general scripting, services, remotes, the client/server
split → roblox-luau. High-frequency temporary state (matchmaking, per-round) →
memory stores (a different service). Engine-agnostic persistence theory →
save-systems.
Core workflow
- Enable Studio access once. File → Game Settings → Security → Enable Studio
Access to API Services (use a test place; Studio hits live data). DataStores
work only from server
Scripts, neverLocalScripts. - Get a store, then read/write by key.
DataStoreService:GetDataStore("Name"); key per player is usually"Player_" .. player.UserId. - Wrap every call in
pcall.GetAsync/SetAsync/UpdateAsyncare network calls that can fail; an unguarded failure errors the thread and risks data loss. - Load on
PlayerAdded, save onPlayerRemoving, and alsoBindToClose. A leaving player and a shutting-down server both need a final save. - Prefer
UpdateAsyncfor read-modify-write (multi-server safe) overSetAsync(blind overwrite). On a failed load, do not overwrite with defaults — abort the save so you don't wipe good data. - Use
OrderedDataStorefor ranked data (leaderboards) viaGetSortedAsync. Test by joining, changing data, rejoining, and confirming it persisted.
Patterns
1. Load on join (pcall-guarded)
2. Save with UpdateAsync (multi-server safe)
3. Save on leave AND on shutdown
4. Retry with backoff (transient failures)
5. Increment a counter
6. Leaderboard with OrderedDataStore
Pitfalls
- Unhandled failure wipes progress → always
pcallAsync calls; on a failed load, mark the session and refuse to save so defaults never overwrite real data. SetAsyncrace between servers → two servers writing the same key can clobber each other. UseUpdateAsyncfor read-modify-write so each write sees the latest.- Yielding inside the
UpdateAsynccallback → the callback can't calltask.waitor other Async functions; compute the new value beforehand and return it. - No
BindToClosesave → players in the server at shutdown lose unsaved progress; addgame:BindToCloseand wait for saves to finish within its budget. - Throttling / "too many requests" → respect per-key and per-minute limits; don't
save on every value change. Batch and save on a timer / on leave.
GetAsyncis cached briefly, so immediate re-reads may be stale. - Storing non-serializable values → only JSON-serializable data persists: numbers,
strings, booleans, and tables with string/number keys.
Instances,Vector3,CFrame, and functions do not — serialize them to plain tables first. - Testing without API access → DataStores silently can't be used in Studio until
Enable Studio Access to API Services is on (and they don't work from a
LocalScript). DataStoreKeyInfois nil for ordered stores →OrderedDataStoredoesn't support versioning/metadata; use a regularDataStorewhen you need those.
References
- For session locking (preventing duplicate data across servers), versioning/
metadata with
DataStoreSetOptions, ordered-store pagination (AdvanceToNextPageAsync), the key error codes and request limits, and Right-to-be-Forgotten compliance, readreferences/sessions-and-limits.md.
Related skills
roblox-luau— services, instances, events, and the server/client model.save-systems— engine-agnostic serialization, slots, and migration.
