
roblox-datastores
Implement or debug persistent Roblox player data, leaderboards, migrations, and deletion requests. Use for concurrent writes, session locking, recovery, caching, quotas, or choosing DataStores versus MemoryStores.
- Installs
- 305
- Last 24h
- —
- Trending rank
- —
- Security
- Not audited
Install
Works with every agent the skills CLI supports.
npx skills add https://github.com/nonlooped/roblox-suite --skill roblox-datastores- 01
Install
Run the command in your project. The CLI asks which agents to install for.
- 02
Describe the task
Ask your agent for something this skill covers. It loads roblox-datastores automatically when relevant.
- 03
Review the result
The agent follows the SKILL.md steps, scripts and references bundled with the skill.
SKILL.md
The instructions your agent reads when the skill is triggered.
roblox-datastores
Keep persistence on the server. For concurrent writes, use a pure UpdateAsync transform; after a write error, reconcile the backend state before retrying or compensating.
Official sources:
- https://create.roblox.com/docs/cloud-services/data-stores
- https://create.roblox.com/docs/cloud-services/data-stores-vs-memory-stores
- https://create.roblox.com/docs/cloud-services/data-stores/versioning-listing-and-caching
- https://create.roblox.com/docs/cloud-services/data-stores/error-codes-and-limits
- https://create.roblox.com/docs/cloud-services/data-stores/best-practices
- https://create.roblox.com/docs/cloud-services/data-stores/data-stores-manager
- https://create.roblox.com/docs/cloud-services/data-stores/observability
- Engine classes: DataStoreService, GlobalDataStore, DataStore, OrderedDataStore, DataStoreOptions, DataStoreKeyInfo, DataStoreSetOptions, etc. (full reference at https://create.roblox.com/docs/reference/engine)
For temporary queues, lobbies, and caches, use MemoryStoreService. Read types-of-datastores.md when choosing a store.
Cross-reference:
- roblox-networking/SKILL.md for server-authoritative validation before any write.
- roblox-core/SKILL.md for service acquisition patterns, pcall discipline, and Luau serialization rules.
- roblox-gamepasses/SKILL.md when purchases affect persistent state (re-verify ownership on load).
- roblox/SKILL.md for the overall hub and Engine API link.
Choose a store
-
Is the data temporary/ephemeral, high-churn, or only needed while servers are active (queues, lobbies, rate counters, short caches)?
→ Use MemoryStoreService (queues, sorted maps, etc.). DataStores are overkill and more expensive/slower. See references and official comparison. -
Do you need sorted numeric queries for leaderboards or rankings?
→ Use OrderedDataStore (via GetOrderedDataStore). Values must be integers. No versioning/metadata. Special GetSortedAsync + DataStorePages iteration. Limits differ (see references/limits...). -
Do you need versioning for recovery/audit, user-defined metadata, key listing, or the richer DataStore API (
ListKeysAsync,ListVersionsAsync,GetVersionAsync,GetVersionAtTimeAsync)? → Use the full DataStore created viaGetDataStorewithDataStoreOptions(this returns the modern DataStore class). Preferred for serious player data. -
Simple key-value persistent storage without the above needs?
→ UseDataStoreService:GetDataStore()(returns a standard DataStore) orDataStoreService:GetGlobalDataStore()(legacy global store, scope"u"). For new work, prefer a namedGetDataStorestore.
Scopes vs prefixes (modern recommendation): For new work, prefer key prefixes (e.g. "profiles/User_1234") + ListKeysAsync filtering over legacy scopes. Scopes prepend to keys automatically. Use DataStoreOptions.AllScopes=true + empty scope string for cross-scope listing (see references).
Studio access: If Studio needs backend APIs, use a dedicated test experience. Do not enable Studio sessions to read or write production data.
Recommended safety workflows
Player data profile pattern (most common)
See references/best-practices-and-gotchas.md for full examples and variations (including session locking and profile patterns).
High-level server flow (in a ModuleScript required by ServerScriptService scripts):
-
On PlayerAdded:
- Load with pcall + GetAsync (consider UseCache=false for critical fresh read after potential previous server issues).
- Merge with defaults.
- Store in a server-side table or profile object (e.g.
playerData[player.UserId] = data). - Apply any gamepass perks (cross-ref monetization skill) or other state.
- Optionally fire a Remote to client for initial UI sync (never trust client data).
-
During play:
- Mutate the in-memory profile.
- Periodically auto-save (every 30-60s) using UpdateAsync or SetAsync with proper transform.
- On important events (purchase, level up, trade), save immediately with UpdateAsync for safety.
-
On PlayerRemoving / BindToClose / character death:
- Final save with pcall.
- Clean up in-memory data.
- For BindToClose, yield up to ~30 seconds to finish final saves (Roblox gives a bounded grace period).
-
On errors during write:
- Use the verification pattern: after a failed write, immediately GetAsync with UseCache=false to see actual backend state before deciding retry/refund/rollback.
Use UpdateAsync with a pure (non-yielding) transform function for any value that can be concurrently modified by multiple servers. It reads-then-writes atomically from the perspective of the last writer.
Serialization rules (critical): Only nil, booleans, numbers (no inf/-inf/nan), strings (valid UTF-8), buffer, and tables of the above. No functions, no metatables on saved tables, no cycles. Use HttpService:JSONEncode on suspect data during development to preview what will actually be stored. See references/core-operations... .
Leaderboards with OrderedDataStore
Populate on relevant value changes or on save (use SetAsync or Increment).
Query with GetSortedAsync(ascending, pageSize, min?, max?) → DataStorePages. Iterate pages with GetCurrentPage() + AdvanceToNextPageAsync().
For bulk reads, use BatchGetAsync(keys) to fetch multiple ordered entries in one request. Each key counts as one OrderedRead (see error-codes-and-limits for quotas; missing keys are omitted).
Remember: only numbers; keys are strings; page iteration has its own limits; no versioning.
Display in a ScrollingFrame or via UI on demand. Cache top-N in a standard DataStore if you want fast global access without repeated queries.
See references for full page iteration example and limits differences.
Versioning and recovery
Every Set/Update/Increment on a standard DataStore (not Ordered) creates hourly-versioned backups: the first write to a key in a given UTC hour creates a new version snapshot, and subsequent writes in the same hour overwrite that hourly version. Versioned backups expire ~30 days after being superseded; the current version never expires.
- ListVersionsAsync(key, sort, minDate?, maxDate?, pageSize?)
- GetVersionAsync(key, version)
RemoveVersionAsyncis deprecated. Do not present it as a current cleanup API; retain versions for recovery and useListVersionsAsync,GetVersionAsync, orGetVersionAtTimeAsyncto inspect them.- GetVersionAtTimeAsync(key, timestampMillis): find the version current at a specific past time.
- To revert: read the desired version, then SetAsync the value + metadata back (this creates a new current version).
Also use the daily Snapshot Data Stores Open Cloud API before risky publishes.
The Data Stores Manager in Creator Hub lets you browse keys, view metadata/version history, compare versions, and revert (with proper permissions).
See references/versioning-metadata-recovery.md for complete code samples including the "revert to time of incident" pattern.
Caching control
By default: GetAsync results are cached locally for 4 seconds. Subsequent Gets within the window return cache and do not count against limits. Writes update the cache immediately.
For verification after writes (especially after errors), or when you suspect staleness due to other servers:
local opts = Instance.new("DataStoreGetOptions")
opts.UseCache = false
local value, info = store:GetAsync(key, opts)
This always hits the backend and counts against quotas. Use sparingly but correctly.
See references/versioning-metadata-recovery.md (for listing, caching, serialization) and references/best-practices-and-gotchas.md .
Limits, quotas, throttling, and budgets
There are experience-level limits (scale with concurrent users across the whole experience) and per-server limits (configurable via DataStoreService:SetRateLimitForRequestType). Experience-level pools are shared between game servers and Open Cloud v2 Data Store traffic. Bulk external jobs can throttle live players (and vice versa).
UpdateAsync counts against both read and write budgets.
Use DataStoreService:GetRequestBudgetForRequestType(Enum.DataStoreRequestType.XXX) to check before heavy operations and wait in a loop if necessary.
Queues exist (size 30); when full, you get throttle errors (301-306 range or the newer *Throttled errors).
Full tables of every error code (101 KeyNameEmpty ... through all the *ExperienceThrottled and *GameServerThrottled variants), experience formulas (base 300 + concurrentUsers × N), and server-side errors are in references/limits-quotas-throttling-error-codes.md .
Observability dashboard (Creator Hub → Monitoring → Data Stores) shows real-time request counts by API/status, quota usage percentages, storage bytes vs limit.
Data Stores Manager shows total size vs storage limit (500 MB + 1 MB × lifetime users, measured as compressed latest-version size), per-DS key counts, etc. Do not pre-compress values before writing.
Call SetRateLimitForRequestType early in server init (once per type) to raise limits for migration scripts or busy servers. Base + (perPlayer * numPlayers). Constraints per type are documented.
Handle uncertain writes
Every datastore call must be inside pcall.
On failure:
- Log the specific error code/message.
- Retry reads with bounded exponential backoff and jitter.
- Treat an invoked write that returns an error as unknown, not rejected: reconcile with a fresh
GetAsyncusingUseCache = false. - Replay a write only when the caller has proved the entire operation idempotent. Never automatically replay
IncrementAsync. - Return explicit
committed,rejected, orunknownstatus so purchase/recovery code cannot mistake ambiguity for failure.
Never assume a failed Set/Update/Increment means "no change occurred." UpdateAsync transforms can run multiple times and must not yield or produce external side effects.
RemoveAsync creates a "tombstone" (Get returns nil) but older versions remain queryable for 30 days (unless explicitly removed).
Management and monitoring
- Use Data Stores Manager (Creator Hub) for inspection, key search by prefix, version compare/revert, and scheduled deletion (with cooldown). The Manager's Revert button can revert an individual key to a previous version. The Restore button is for data stores that are marked for deletion, not for reverting a key's value. Restoring a deleted key during its cooldown requires calling
UpdateAsyncfrom the Engine API orUpdateDataStoreEntryfrom Open Cloud. - Observability dashboard for trends and quota forecasting.
- For bulk delete/migrate: Open Cloud Data Stores APIs or the official Batch Processor CLI/tool.
- Set up notifications for approaching/exceeding storage limits.
- Review usage regularly.
See references/versioning-metadata-recovery.md (covers listing/caching) and the manager/observability pages (linked in best-practices-and-gotchas.md).
Checklists
Before any critical write:
- Wrapped in pcall with explicit committed/rejected/unknown handling
- Using UpdateAsync where races are possible, with a pure transform
- Providing userIds array for RTBF/GDPR tracking where appropriate
- Metadata supplied on every write (even if unchanged) when using full DataStore
- Value is serializable (test with JSONEncode during dev)
- Key naming consistent with prefixes for easy listing
On player join/load:
- Fresh load or cache-aware as appropriate
- Merge defaults safely
- Re-verify ownership on PlayerAdded (gamepasses, etc.) and re-apply any non-owned one-time consumable grants
- Client sync only of non-sensitive derived state
Before publishing risky data-logic changes:
- Take a daily snapshot via Open Cloud if available
- Test thoroughly on a copy experience
Ongoing:
- Monitor dashboard and manager
- Clean test/temporary data
- Use MemoryStores for anything that doesn't need to survive server death
- Prefer fewer data stores + larger cohesive objects + key prefixes
Common mistakes
- Calling from client/LocalScript (immediate error + security hole).
- Storing functions, metatables, or non-serializable data.
- Using SetAsync for contended values (race condition → lost updates or duplication).
- Ignoring throttle errors and hammering (leads to dropped requests and player frustration).
- Hardcoding studio API access on live places.
- Creating hundreds of tiny data stores instead of organizing by key/prefix.
- Relying on cached Gets for post-write verification.
- Forgetting that OrderedDataStore has no versioning and only integers.
- Not handling the case where RemoveAsync has already been called (nil + tombstone).
- Exceeding storage quota without monitoring (leads to surprise costs and potential write failures).
Scripts folder usage
The scripts/ directory contains maturity-labeled examples:
SafeDataStore.lua: reviewed example with automatic read retries, explicit write states, opt-in idempotent replay, and uncached reconciliation.BudgetMonitor.lua: experimental budget-inspection example.
Adapt and validate them in a dedicated test experience; this directory is not a versioned production library.
How to proceed in practice
- Read the relevant references/ file(s) for the exact feature you are implementing.
- Implement using the patterns and code samples here + in references.
- Test budget behavior under load (use multiple test servers or the rate-limit setter for simulation).
- Add observability and alerts early.
- Profile storage growth with the Manager/Dashboard.
Combine this guidance with static analysis, focused tests, Studio integration, observability, and current official sources before trusting a persistence system.
For the authoritative class/property reference for any specific method, always cross-check https://create.roblox.com/docs/reference/engine (search for DataStoreService, GlobalDataStore, etc.).
<!-- catalog:references:start -->Reference index
- best-practices-and-gotchas.md: Design player profiles, session locking, saves, or deletion workflows.
- core-operations-and-patterns.md: Implement reads, writes, transforms, or error recovery.
- limits-quotas-throttling-error-codes.md: Diagnose throttling, inspect budgets, or plan request and storage usage.
- types-of-datastores.md: Choose standard, ordered, or temporary storage and organize keys.
- versioning-metadata-recovery.md: Inspect versions, preserve metadata, or recover data after an incident.
FAQ
What does the roblox-datastores skill do?
Implement or debug persistent Roblox player data, leaderboards, migrations, and deletion requests. Use for concurrent writes, session locking, recovery, caching, quotas, or choosing DataStores versus MemoryStores.
How do I install roblox-datastores?
Run npx skills add https://github.com/nonlooped/roblox-suite --skill roblox-datastores in your project. The skills CLI detects the agents you use and copies the skill into the right folder. For Claude Code you can also place the skill folder at ~/.claude/skills/roblox-datastores/ (personal) or .claude/skills/roblox-datastores/ (project).
Which AI agents can use roblox-datastores?
Any agent that supports the open Agent Skills format (a folder with a SKILL.md file), including Claude Code, Codex, Cursor, GitHub Copilot, Gemini CLI, Windsurf, Cline, OpenCode and more.
Is roblox-datastores safe to install?
No partner security audit is available yet. Skills can run commands and scripts, so read the SKILL.md and any bundled files before installing.
Who maintains roblox-datastores?
It is published by nonlooped in nonlooped/roblox-suite. It has 305 recorded installs.
More from nonlooped/roblox-suite
roblox-vfx
nonlooped/roblox-suite
Build or optimize Roblox particles, beams, trails, and highlights. Use for emitter shapes, flipbooks, one-shot bursts, animation-marker effects, distance culling, and overdraw.
roblox-animation
nonlooped/roblox-suite
Build or debug Roblox character animations, IK poses, animation-marker events, and UI or object tweens. Use for locomotion, emotes, motion blending, and timed effects.
roblox-user-interfaces
nonlooped/roblox-suite
Build or debug Roblox HUDs, menus, and world-space interfaces. Use for GUI containers, responsive layouts, input handling, ViewportFrame previews, and particle effects in 2D UI.
roblox-testing
nonlooped/roblox-suite
Test, debug, or profile Roblox experiences. Use for Studio debugging, TestEZ, logging, connection cleanup, memory or network diagnostics, MicroProfiler, Scene Analysis, and Script Profiler.
roblox
nonlooped/roblox-suite
Route Roblox Luau tasks to specialist skills. Use for architecture decisions, broad development requests, or work spanning several Roblox systems.
roblox-mcp
nonlooped/roblox-suite
Connect an AI client to Roblox Studio through MCP. Use for connection setup, Studio tools, multiple Studio windows, playtesting, or combining MCP with Script Sync.
Popular code quality & workflow skills
grill-me
mattpocock/skills
A relentless interview to sharpen a plan or design.
improve-codebase-architecture
mattpocock/skills
Scan a codebase for deepening opportunities, present them as a visual HTML report, then grill through whichever one you pick.
handoff
mattpocock/skills
Compact the current conversation into a handoff document for another agent to pick up.
domain-modeling
mattpocock/skills
Build and sharpen a project's domain model. Use when discussing codebase terminology, writing or editing a GLOSSARY.md, or recording or editing an ADR.
codebase-design
mattpocock/skills
Shared vocabulary for designing deep modules. Use when the user wants to design or improve a module's interface, find deepening opportunities, decide where a seam goes, make code more testable or AI-navigable, or when another skill needs the deep-module vocabulary.
diagnosing-bugs
mattpocock/skills
Diagnosis loop for hard bugs and performance regressions. Use when the user says "diagnose"/"debug this", or reports something broken/throwing/failing/slow.