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_DIR | everything else |
|---|---|---|
| Crash and automatic restart | Kept | Kept (same build) |
| Restart from the dashboard | Kept | Kept (same build) |
| Redeploy | Kept | Replaced by the new build |
| Failed redeploy, rolled back | Kept, including anything the failed version wrote | The previous build comes back |
| Expiry | Kept in the snapshot for 7 days | Kept in the snapshot for 7 days |
| Claim (live or expired) | Kept | Kept; an expired stack restarts from its build |
| Server restart | Kept | Kept; live stacks are started again |
| Destroy, or retention running out | Deleted | Deleted |
"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 EXISTSor 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_DIRtoo, 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.