Docs / Guides

Data and persistence

Where services can write files that survive redeploys, expiry and claiming, and what doesn't survive.

Every service gets one persistent directory, exposed as $DATA_DIR. Write databases, uploads and anything else you need to keep there. Everything else on disk belongs to a single deploy and is replaced by the next one.

$DATA_DIR#

The directory is per service, and it is the same directory across every generation of the stack. It is set for the running process and for the build commands. Read it from the environment rather than hard-coding a path:

import os
import sqlite3

db = sqlite3.connect(os.path.join(os.environ.get("DATA_DIR", "."), "todos.db"), check_same_thread=False)
const path = require("node:path");
const dbFile = path.join(process.env.DATA_DIR ?? ".", "app.db");

Falling back to . keeps the code working on your machine, where DATA_DIR isn't set.

Services in the same stack each get their own directory and don't share one.

What survives what#

event$DATA_DIReverything else
Crash and automatic restartKeptKept (same build)
Restart from the dashboardKeptKept (same build)
RedeployKeptReplaced by the new build
Failed redeploy, rolled backKept, including anything the failed version wroteThe previous build comes back
ExpiryKept in the snapshot for 7 daysKept in the snapshot for 7 days
Claim (live or expired)KeptKept; an expired stack restarts from its build
Server restartKeptKept; live stacks are started again
Destroy, or retention running outDeletedDeleted

"Everything else" covers the service's source directory, its virtualenv or node_modules, build output, and any file the process writes next to its code. A redeploy extracts the new source into a fresh directory, and older generations are deleted once the new one is live. A SQLite file written to the working directory is therefore gone after the next deploy, even though it looked fine until then.

Destroying a stack (agentserve destroy, DELETE /v0/stacks/{id}, or deleting the project in the dashboard) deletes its data immediately. An unclaimed stack that expired and isn't claimed within 7 days is deleted the same way.

AgentServe keeps no backups and no history of $DATA_DIR. If the data matters, export it yourself.

SQLite tips#

SQLite in $DATA_DIR is the intended way to keep state today. A few habits make it hold up:

  • Create the schema at start-up, idempotently. Use CREATE TABLE IF NOT EXISTS or a migration tool that records what it has applied. The process starts against an existing file after every redeploy, restart and restore, and against an empty directory only the first time.
  • Run migrations when the process starts, not in build. Build commands also see $DATA_DIR, but during a redeploy they run while the previous generation is still serving from the same file.
  • Write migrations that the previous version can live with. A failed redeploy rolls the code back but not the data. Adding a column or a table is safe; renaming or dropping one can break the version that comes back.
  • Turn on WAL mode (PRAGMA journal_mode=WAL) if the service reads and writes concurrently, for example from several threads or async workers.
  • Commit explicitly. A crash restarts the process with backoff; uncommitted writes are lost.
  • Keep uploads next to the database. User files go in $DATA_DIR too, for example $DATA_DIR/uploads/.

Static services#

Static services have no process, so nothing writes at runtime. Their files are the build output, rebuilt on every deploy. A frontend that needs to store something should call a backend service that writes to its own $DATA_DIR.

No managed databases yet#

There is no managed Postgres, Redis or object storage. A service with an addon key in the manifest is rejected: "add-ons (postgres, redis) are not supported in this version yet". If your app needs Postgres, connect to a database you host elsewhere by passing its connection string in env, and keep in mind that the manifest is uploaded with your source, so anything in env is stored by the server in plain text.

See Limits for the other constraints on a stack.

Stuck? Your agent can read /skill.md, or connect it over MCP.