Skip to article
HANDBOOK / CREATE A WEBSITE

Persistent site storage

Keep data across requests with ctx.read and ctx.write, instead of global variables.

FORMAT 1Updated September 19, 2026
On this page

A complete persistent counter

GET reads the counter without changing it. POST /increment saves the next value, then redirects to / so the browser displays the updated page. Try it in preview and again after reopening the site. Local preview uses the local site’s real data, so preview form submissions can change that data.

app.lua
return function(ctx)
  local P = ctx.page
  local count = ctx.read("counter", 0)
  if ctx.method == "POST" and ctx.path == "/increment" then
    ctx.write("counter", count + 1)
    return {kind="redirect", path="/"}
  end
  if ctx.method ~= "GET" or ctx.path ~= "/" then
    return {kind="error", status=404, message="Page not found"}
  end
  return {kind="page", page=P.document("Community counter", {
    P.heading{text="Count: " .. tostring(count)},
    P.form{action="/increment", children={P.submit{text="Add one"}}}
  })}
end

Keys and values

For portability, use storage keys of 2–40 lowercase letters, digits or hyphens, beginning with a letter or digit. The cloud accepts a somewhat broader key set; local hosts use the stricter site-name rule.

Store strings, booleans, numbers and JSON-compatible tables. Do not store functions, cyclic structures or host objects. ctx.read(key, default) returns default when the key is absent. ctx.write is allowed only on POST requests. There is no dedicated delete-key API.

Where the data lives

Publishing code does not migrate your local data. Test with disposable data and plan any real migration separately. Keep local backups of your site folder and protect private identity state separately.

Local hostLantern Hub
/lantern-data/sites/<name>/data/<key>.jsonSite-scoped cloud database storage
Stored on the Minecraft computerSurvives fresh execution VMs
Excluded from Upload Hub draftNot copied from local preview storage

Concurrency and limits

The current cloud site storage limit is 128 KiB. Conflicting concurrent writes are rejected rather than silently overwriting one another. The counter is a teaching example, not a transactional high-traffic counter service. Reload and inspect state before resubmitting a conflicting action.

Keep guestbooks and lists bounded. The guestbook template retains a limited number of entries. Data is scoped to a site, but any visitor can invoke routes you expose; your handler is responsible for what those routes allow.

Found a mismatch? Include the exact error, runtime version and a small example with secrets removed.