Set up Postgres persistence¶
By default skeino keeps everything in memory, which is great for development but loses all threads, runs, and state on restart. Point it at PostgreSQL to get durable, shareable persistence.
Install the extra: pip install 'skeino[postgres]'.
1. Provision a database¶
Any reachable PostgreSQL instance works. For local development:
Create a database (or reuse postgres):
2. Point skeino at it¶
Set checkpointer_scheme="postgres" and a checkpointer_uri. The scheme
switches both the checkpointer and the metadata store to Postgres:
from skeino import create_app, SkeinoSettings
from my_project.graph import graph
app = create_app(
graphs={"my_agent": graph},
settings=SkeinoSettings(
checkpointer_scheme="postgres",
checkpointer_uri="postgresql://postgres:postgres@localhost:5432/skeino",
),
)
Or, with a langgraph.json manifest, via the store.uri field (which supports
environment expansion):
{
"env": ".env",
"graphs": { "my_agent": "./src/graph.py:graph" },
"store": { "uri": "${POSTGRES_URI}" }
}
3. Schema creation¶
On startup skeino creates what it needs:
- the metadata tables
app_threadsandapp_runs(the latter references the former withON DELETE CASCADE), and - the LangGraph checkpoint tables, via the Postgres saver's
setup().
This happens automatically the first time the app boots against the database. If
you manage schema migrations yourself and want to suppress the checkpointer's
auto-setup, pass checkpointer_options={"setup_schema": False} (and create the
checkpoint tables out of band).
4. Let skeino compile your graph against the checkpointer¶
If your graph needs the checkpointer at compile time, register a builder instead of a precompiled graph. skeino resolves the Postgres checkpointer and hands it in:
async def build_graph(checkpointer):
builder = StateGraph(...)
return builder.compile(checkpointer=checkpointer)
app = create_app(
graphs={"my_agent": build_graph},
settings=SkeinoSettings(
checkpointer_scheme="postgres",
checkpointer_uri="postgresql://localhost/skeino",
),
)
Connection notes¶
- skeino depends on
psycopgv3 (with thebinaryandpoolextras), so no separate driver install is needed. - Use a connection string your network can reach from the server process. For
managed Postgres, include
sslmode=require(or your provider's equivalent) in the URI. - The checkpointer runs over a connection pool that validates each
connection before use, so a connection dropped by the server or a connection
pooler (e.g. a Supabase/pgbouncer idle-timeout or recycle) is transparently
replaced rather than wedging later reads with
the connection is closed. Prepared statements are disabled, so a transaction-mode pooler URI works too. Tune the pool's upper bound withcheckpointer_options={"pool_max_size": N}(default10).
Verifying durability¶
Start the server, create a thread and run a graph, then restart the process.
GET /threads/{id}/state should still return the prior state — confirming
checkpoints survived the restart. With the in-memory default, the thread would
be gone.
See Persistence & checkpointers for the full model, and Write a custom checkpointer for non-Postgres backends.