AI Agent Memory Bloat: Prune Checkpoints, Save ₹18K 2026
Vol. 01 — 2026

AI Agent Memory Bloat: Prune Checkpoints, Save ₹18K 2026

Quick Answer: AI agent memory bloat is the silent cost of unbounded LangGraph checkpoints. A 40-thread agent running 30 steps per task can write 1.2M checkpoint rows in a month, adding ₹18,000 to ₹42,000 in Postgres storage and backup fees. Set a 14-day retention policy, prune by thread age, and keep P95 resume latency under 45ms.

Key Takeaways

  • Checkpoints are not free: Every super-step writes a row plus blobs. A busy support agent can add 6–12 GB per month with no visible product benefit.
  • Retention beats compression: A 14-day thread retention policy removes 80% of rows in most support workloads before compression matters.
  • Prune by thread, not by row: Delete whole threads that are closed and older than the retention window. Row-level deletion leaves orphaned blobs and broken resume paths.
  • Measure resume latency: Pruning is only safe if the P95 resume time stays under 45ms for active threads. I alert at 80ms.

What Checkpoint Bloat Actually Costs

I run agent workloads for Indian SMEs on a 4 vCPU, 8 GB Postgres VPS. The database is not the expensive part. The expensive part is the backup, the replica, and the 3 AM vacuum that blocks writes.

Monthly agent volume Checkpoint rows Table + index size Extra VPS cost
5,000 tasks 150K 1.1 GB ₹0
20,000 tasks 600K 4.4 GB ₹900
60,000 tasks 1.8M 13.2 GB ₹4,800
150,000 tasks 4.5M 33 GB ₹18,000

The jump from 600K to 1.8M rows is where most teams notice. Backups go from 40 seconds to 6 minutes, and the nightly vacuum starts competing with the morning WhatsApp burst. The agent did not get slower because the model changed. It got slower because the database is carrying every dead conversation forever.

Where the Storage Goes

Checkpoint storage has three layers. Most teams only look at the first one.

flowchart LR
    A[Agent super-step] --> B[checkpoints table]
    B --> C[checkpoint_blobs]
    B --> D[checkpoint_writes]
    C --> E[Channel values: messages, docs, tool output]
    D --> F[Pending writes for resume]
    B --> G[Thread metadata]
    G --> H[Retention policy]
    H --> I[Delete closed threads older than 14 days]

The checkpoints table is small. The checkpoint_blobs table holds the channel state: the full message list, retrieved documents, and tool outputs. A single RAG step with 12 retrieved chunks can write 180 KB. Multiply that by 30 steps and 1,500 threads, and you have a storage problem that looks like a code problem.

The Retention Policy I Use in Production

I use a two-tier policy: 14 days for closed threads, 90 days for threads with an open human approval. The approval tier exists because a paused interrupt may sit for weeks in a regulated workflow.

-- migrations/2026_10_26_checkpoint_retention.sql
CREATE INDEX IF NOT EXISTS idx_checkpoints_thread_created
  ON checkpoints (thread_id, created_at);

CREATE INDEX IF NOT EXISTS idx_checkpoint_writes_thread
  ON checkpoint_writes (thread_id);

-- Mark closed threads once per day
WITH closed AS (
  SELECT thread_id
  FROM thread_meta
  WHERE status = 'closed'
    AND updated_at < now() - interval '14 days'
)
DELETE FROM checkpoint_writes w
USING closed c
WHERE w.thread_id = c.thread_id;

WITH closed AS (
  SELECT thread_id
  FROM thread_meta
  WHERE status = 'closed'
    AND updated_at < now() - interval '14 days'
)
DELETE FROM checkpoint_blobs b
USING closed c
WHERE b.thread_id = c.thread_id;

DELETE FROM checkpoints c
USING thread_meta m
WHERE c.thread_id = m.thread_id
  AND m.status = 'closed'
  AND m.updated_at < now() - interval '14 days';

Run this in batches. A single delete of 400K rows will hold locks long enough to stall the agent. I cap each batch at 5,000 threads and loop until the row count stops falling.

# jobs/prune_checkpoints.py
import time
from sqlalchemy import text
from db import engine

BATCH = 5000

def prune_once() -> int:
    with engine.begin() as conn:
        result = conn.execute(text("""
            WITH doomed AS (
                SELECT thread_id
                FROM thread_meta
                WHERE status = 'closed'
                  AND updated_at < now() - interval '14 days'
                LIMIT :batch
            )
            DELETE FROM checkpoints c
            USING doomed d
            WHERE c.thread_id = d.thread_id
            RETURNING c.thread_id
        """), {"batch": BATCH})
        return len(result.fetchall())

total = 0
while True:
    deleted = prune_once()
    total += deleted
    if deleted < BATCH:
        break
    time.sleep(2)

print(f"pruned {total} threads")

Schedule it at 02:30 IST, after the nightly backup and before the 06:00 WhatsApp burst. If your agent serves US clients, move it to their quiet window instead. The point is to avoid the window when humans are waiting on a reply.

Pruning Without Breaking Resume

The dangerous part is deleting a thread that a user is about to resume. I use a soft-close step first: mark the thread closed only when the graph has no pending interrupt and no active run. The check is cheap.

-- Close only idle threads with no pending writes
UPDATE thread_meta m
SET status = 'closed'
WHERE m.status = 'active'
  AND m.updated_at < now() - interval '2 days'
  AND NOT EXISTS (
    SELECT 1 FROM checkpoint_writes w
    WHERE w.thread_id = m.thread_id
  );

If a human approval is pending, the thread stays awaiting_human and is excluded from the 14-day job. This is the single rule that prevents the "resume failed" support ticket. I learned it after deleting a thread that held a ₹2.4L purchase approval in a client workflow. The approval was recoverable from an audit log, but the client lost an afternoon.

War Story: The 6.4M Row Checkpoint Table

In July 2026, a Rajkot support agent started timing out every Monday. The team blamed the model. The P95 first-token latency had climbed from 900ms to 4.2 seconds over six weeks, and the bill had gone from ₹2,400 to ₹9,100 per month.

I pulled the Postgres stats. The checkpoint_blobs table had 6.4M rows and 19 GB of blobs. The agent was storing every retrieved PDF chunk in every checkpoint, including chunks that were never used again. Autovacuum had not completed in nine days because the table was growing faster than it could clean.

The fix took one afternoon: add the thread index, close idle threads, run the batched prune, and cap the RAG step to store document IDs instead of full chunk text. The next Monday, P95 first token was 780ms and the table was 2.1 GB. The model never changed.

Three Queries That Catch Bloat Early

Run these weekly. They take under a second and tell you more than a dashboard.

-- 1. Table sizes
SELECT relname, pg_size_pretty(pg_total_relation_size(relid)) AS size
FROM pg_catalog.pg_statio_user_tables
WHERE relname LIKE 'checkpoint%'
ORDER BY pg_total_relation_size(relid) DESC;

-- 2. Open threads older than 30 days
SELECT count(*) FROM thread_meta
WHERE status <> 'closed' AND updated_at < now() - interval '30 days';

-- 3. Blob bytes per thread (top 10)
SELECT thread_id, pg_size_pretty(sum(octet_length(value))::bigint) AS bytes
FROM checkpoint_blobs
GROUP BY thread_id
ORDER BY sum(octet_length(value)) DESC
LIMIT 10;

The third query is the one that changes behavior. When a client sees that three threads hold 2.8 GB because the agent stored raw HTML, the conversation about pruning ends immediately.

When NOT to Use Checkpoint Pruning

Do not prune if you sell time-travel debugging as a product feature. Do not prune if your compliance team requires a full replay of every agent decision for 12 months. Do not prune threads that hold pending human approvals, and do not prune during an active incident until you have exported the relevant thread IDs.

For regulated workflows, move old checkpoints to cold storage instead of deleting them. A nightly export to S3-compatible storage in Mumbai costs less than the Postgres volume it replaces, and it keeps the audit trail intact.

Frequently Asked Questions

How many checkpoints does a LangGraph agent create per task?

A LangGraph agent writes one checkpoint per super-step. A task with 30 steps across 4 nodes typically creates 30–40 checkpoint rows plus blob rows. At 20,000 tasks per month, that is roughly 600,000 checkpoints, which is where storage and vacuum pressure become visible.

Does deleting checkpoints break LangGraph resume?

Deleting a thread with no pending writes and no active run does not break resume because there is nothing to resume. Deleting a thread with a pending interrupt does break it. Close only idle threads, exclude awaiting_human, and batch deletes to avoid lock contention.

What is a safe checkpoint retention period?

Fourteen days is safe for most support and sales agents. Use 90 days for workflows with human approvals, and 12 months only when a compliance rule requires it. The right number is the shortest period that still covers your longest real conversation.

How much can checkpoint pruning save in India?

On a 4 vCPU, 8 GB VPS, pruning 1.5M stale checkpoints typically removes 10–15 GB, which drops the backup window and can move you to a smaller plan. Clients I have worked with saved ₹9,000–₹18,000 per month between storage, backup, and the extra replica they no longer needed.

Bottom Line

Your agent does not have a memory problem. It has a retention problem. Cap the blobs, close idle threads, prune in batches at 02:30 IST, and watch P95 resume latency instead of the model leaderboard. If you are running LangGraph in production, read the Postgres checkpointer guide and the human-in-the-loop swarm patterns next, then send me your checkpoint table size and I will tell you whether pruning or re-architecture is the cheaper fix.

// FREE DEVELOPER WORKBENCH

Build, format, or test your architecture in real time

Need to test schemas, regex, tokens, or JWTs? Use Deepak's 25+ browser-side developer utilities—100% free, zero ads, zero data logging.

Open Developer Tools →

KEEP READING

← All journal articles Get in touch →