Give Your AI Agent a Backend with PocketBase

Give Your AI Agent a Backend with PocketBase

Give an AI agent a task and watch what it actually needs. It has to remember things between runs. It has to store the files it produces. It needs to know which user it is acting for. And the humans watching want to see what it is doing in real time. That is a database, file storage, auth, and a realtime layer, which is usually four services, four configs, and a weekend you will not get back.

PocketBase is all four in a single file. It is an open-source backend that ships as one Go binary with an embedded SQLite database, built-in authentication, file storage, and a realtime subscriptions API. For giving an agent a real backend without standing up half a platform, it is hard to beat. Here is how the pieces map onto what an agent needs, with code you can copy.

Why one binary fits an agent so well

What your agent needs What PocketBase gives it
Memory between runs Collections (records) with a REST API
A place for artifacts Per-record file storage
Knowing who it acts for Built-in auth with tokens
Humans watching live Realtime subscriptions (SSE)

Give the agent a memory

Start in the admin UI and create a collection, say agent_memory, with a couple of fields: key (text), value (text or JSON), and maybe run_id (text). No migrations, no schema files. The moment you save it, PocketBase generates a working REST API for it.

Now your agent can write to its own memory with a plain HTTP call:

import requests

PB = "https://your-pocketbase.vm.elestio.app"

def remember(key, value, token):
    return requests.post(
        f"{PB}/api/collections/agent_memory/records",
        headers={"Authorization": token},
        json={"key": key, "value": value},
        timeout=10,
    ).json()

Reading it back later is just as direct, and PocketBase gives you filtering and sorting for free:

def recall(key, token):
    r = requests.get(
        f"{PB}/api/collections/agent_memory/records",
        headers={"Authorization": token},
        params={"filter": f"key='{key}'", "sort": "-created"},
        timeout=10,
    )
    return r.json()["items"]

That is persistent, queryable memory in about ten lines, backed by real SQLite instead of a JSON file you hope nothing corrupts.

Authenticate the agent

You do not want your agent running as an anonymous client. Create a dedicated user for it, then exchange credentials for a token once at startup:

def login(identity, password):
    r = requests.post(
        f"{PB}/api/collections/users/auth-with-password",
        json={"identity": identity, "password": password},
        timeout=10,
    )
    return r.json()["token"]

token = login("agent@yourteam.io", "a-long-random-secret")

Pass that token in the Authorization header on every call, as in the examples above. Now the agent's writes are attributable, and you can use collection rules to scope exactly what it is allowed to touch.

Let a human watch it work

This is the part that feels like magic the first time. PocketBase pushes create, update, and delete events over Server-Sent Events, so a dashboard can react the instant the agent writes a record. The JavaScript SDK makes it one call:

import PocketBase from "pocketbase";
const pb = new PocketBase("https://your-pocketbase.vm.elestio.app");

// Live-update the UI whenever the agent records a step
pb.collection("agent_memory").subscribe("*", (e) => {
  console.log(e.action, e.record.key, e.record.value);
});

No polling loop, no websocket plumbing. Your ops view lights up as the agent thinks out loud.

Give the agent a custom action

Sometimes REST records are not enough and you want the agent to call one endpoint that does real work on the server. PocketBase lets you add routes in a pb_hooks/main.pb.js file, no rebuild required:

// pb_hooks/main.pb.js
routerAdd("POST", "/agent/summarize", (e) => {
  const data = e.requestInfo().body;
  // ...do server-side work, call out, aggregate records...
  return e.json(200, { ok: true, received: data });
});

Drop the file in, restart, and your agent has a /agent/summarize endpoint that is part of the same authenticated backend. Wrap that same REST surface in an MCP server and an assistant like Claude can call it as a native tool, which turns PocketBase into the agent's hands, not just its notebook.

Things to know before you ship it

Tokens expire. Auth tokens are not forever. For a long-running agent, catch the 401 and re-login rather than assuming the first token lasts all day.

Watch your filter strings. Building filter= by string-concatenating user or model input is how you get surprises. Validate values before they reach the query, the same discipline you would use with any database.

Reconnect your realtime clients. SSE connections drop on network blips. The JavaScript SDK re-subscribes for you, but if you roll your own client, plan for reconnection.

Back up the data directory. Everything lives in PocketBase's data directory, database and uploaded files together. On Elestio the automated backups cover it; if you self-manage, back up the whole directory, not just the database.

The one-file backend

The appeal here is not that PocketBase does anything a big platform cannot. It is that it does the four things an agent needs from one binary you can stand up in minutes, and reason about completely. Less surface area, fewer moving parts, one place to look when something breaks.

Spin one up on the PocketBase page, point your agent at it, and give it a memory it will not lose.

Thanks for reading ❤️ See you in the next one 👋