# Keep sandbox checkpoints in the Postgres your agents already use

> Agno's PostgresCheckpointStore saves sandboxed ClaudeAgent and CodexAgent checkpoints as rows in PostgreSQL, so every AgentOS replica that shares the database can restore a session on fresh compute.

- Published: 2026-10-21
- Author: Yash Pratap Solanky
- Categories: Changelog
- Canonical: https://www.agno.com/blog/keep-sandbox-checkpoints-in-the-postgres-your-agents-already-use
- Markdown: https://www.agno.com/blog/keep-sandbox-checkpoints-in-the-postgres-your-agents-already-use.md

Agno's `PostgresCheckpointStore` writes each sandbox checkpoint into two PostgreSQL tables, so any replica that reaches your database can restore a session's workspace on a new computer. Sandboxed agents already coordinate their turns through Postgres, and now their checkpoints can sit in the same place with no S3 bucket or shared disk.

```python
from agno.agents.claude import ClaudeAgent
from agno.db.postgres import PostgresDb
from agno.sandbox.docker import DockerSandbox
from agno.sandbox.postgres_checkpoints import PostgresCheckpointStore

db = PostgresDb(db_url="postgresql+psycopg://ai:ai@localhost:5532/ai")

agent = ClaudeAgent(
    id="coder",
    db=db,
    sandbox=DockerSandbox(
        "agno-sandbox:local",
        checkpoints=PostgresCheckpointStore(db),
    ),
)
```

We tried it as two replicas would see it: two separate `PostgresDb`, `PostgresCheckpointStore`, `DockerSandbox` and agent instances on one Postgres, with real Docker containers. The agent is Agno's `FixtureAgent`, a credential-free runtime that echoes the input and records each run in a workspace file. Replica A ran the first turn, its warm container was removed, and replica B ran the second.

```text
replica A turn 1: RunStatus.completed 'Write the first draft'
checkpoint: CheckpointRef(sha256='d5de630b5548...', size=622)
# warm container removed (generation 3209530595559429)
replica B turn 2: RunStatus.completed 'Revise the draft' resumed warm: False
fixture-effects.txt: ['0011382e-...', '4c33fb30-...']
run ids: ['0011382e-...', '4c33fb30-...']
```

Replica B started a new container, pulled the checkpoint out of Postgres and found turn 1's run already listed in the workspace file. The store had created its tables on first use, in the `ai` schema of the db:

```text
-- tables
('ai', 'agno_sandbox_checkpoints')
('ai', 'agno_sandbox_checkpoints_chunks')
-- header rows (sha256, size, created_at)
d5de630b5548 622 1793218480
d9f427f87d85 648 1793218482
-- chunk rows (sha256, seq, bytes)
d5de630b5548 0 622
d9f427f87d85 0 648
```

Each checkpoint is a header row named by the SHA-256 of its bytes, plus chunk rows of up to `chunk_bytes`, which defaults to 8 MiB. These archives fit in one chunk; with `chunk_bytes=128` the same 622 bytes became 5 rows. The header and every chunk go in one transaction, so an interrupted upload leaves nothing readable. When replica B stored the exact bytes replica A had stored, `put` returned the same reference and the tables still held 2 headers and 2 chunks.

Reads check what they get. After one stored byte was overwritten, and again after the object was deleted:

```text
tampered read: SandboxError Checkpoint checksum mismatch
after delete: CheckpointObjectNotFound Checkpoint object is missing
chunk rows left for it: 0
```

Deleting a header removes its chunks through a cascade. A reader that already holds its private copy keeps working after the delete. Pass a separate `PostgresDb` to keep checkpoint bytes out of your system of record, and set `table_name`, `max_bytes` or `scratch` when the defaults don't fit.

See the [cookbook](https://github.com/agno-agi/agno/blob/main/cookbook/frameworks/sandbox/docker/sandbox_postgres_checkpoints.py), and learn more about [sandboxes and checkpoints](https://docs.agno.com/agent-platform/production#sandboxes-and-checkpoints) in the documentation.
