---
title: "Why We Chose PocketBase for Our AI SEO Infrastructure"
canonical: https://snezzi.com/blog/why-we-chose-pocketbase-for-ai-seo-infrastructure/
source: https://snezzi.com/blog/why-we-chose-pocketbase-for-ai-seo-infrastructure/
published: 2026-08-17
modified: 2026-08-17
author: "Gautham Seshadri"
category: "Engineering"
---

> Canonical page: https://snezzi.com/blog/why-we-chose-pocketbase-for-ai-seo-infrastructure/

An AI SEO operation needs more than a database. It needs authentication, a job runner, file storage, an admin surface, and an API that agents and people can use without inventing a second security model. We chose PocketBase because one Go process covered that operating shape with fewer moving parts.

The choice worked, but not for the generic reasons most backend comparisons list. The useful story is what the single binary removed from our architecture, the four production costs it introduced, and the point at which we would replace it.

## The Shape of the Problem

Our workload combines ordinary records with asynchronous jobs and unusually large generated artefacts. A content record moves through research, drafting, review, publication, and performance tracking. Agents need authenticated API access to the same records the editorial team sees. Reports, source material, model transcripts, and images can be much larger than the metadata that describes them.

We could have assembled a database, identity provider, object store, admin framework, and queue. That would have given each concern a specialised service, but it would also have created more deployment units and more boundaries where permissions or schemas could drift.

[PocketBase describes itself as an open-source backend with embedded SQLite, realtime subscriptions, built-in authentication, a dashboard UI, and a REST-style API](https://pocketbase.io/docs/). That package matched the first version of our problem closely enough to justify accepting a single-process constraint.

## What the Binary Actually Bought Us

The operational gain was one process. Copy the binary and data directory, set storage configuration where required, and start it. The API, authentication rules, file handling, and admin UI ship together. The upstream [PocketBase repository](https://github.com/pocketbase/pocketbase) also makes the framework path available when the standalone application needs custom Go code.

Everything behind the process is an API. Agents reuse PocketBase authentication instead of carrying a separate database credential. The frontend and CLI use the same collection rules. A worker can point at the same service with an appropriately scoped token rather than acquiring its own identity stack.

Workflow can hang off record events. When a post moves from generation to review, a hook can start the next step. The state machine remains close to the row instead of depending on a message bus whose consumer may be unavailable while a write succeeds.

That compact architecture also supports Snezzi's managed delivery model. The [Brand Brain](/brand-brain/) keeps approved claims and editorial rules close to the content workflow, while the [Content Agent](/content-engine/) produces pages the client team reviews before publication. The client buys accountable execution, not access to a backend.

## Cost One: The Schema File Does Not Deploy

Our most expensive misunderstanding was treating a generated schema snapshot as a deployment mechanism. It is not. A snapshot describes a database state. Editing the file does nothing to a running instance unless a migration applies the change.

That failure mode is quiet. Development works because the local database already contains the field. Production accepts the new binary but still has the old collection shape. The first real request then fails somewhere far from the deploy that caused it.

We now treat every schema change as code. The migration creates or alters the field, and a contract test starts from the previous schema, runs the migration, and asserts the resulting type, validation rules, and API behaviour. PocketBase's own setup creates a `pb_migrations` directory intended for committed migration files; the [official documentation explains that relationship directly](https://pocketbase.io/docs/).

We also test rollback and restore procedures against a copy of production-shaped data. A migration is incomplete until the team knows how to recover from a partial deploy without editing the database by hand.

The general lesson is simple: generated state is evidence, not execution. If production depends on a field, an index, or an API rule, the deployment must contain an executable transition and a test that proves it ran.

## Cost Two: Large Bodies Do Not Belong in the Row

SQLite can hold large text values, but the application layer still applies field limits. PocketBase text and JSON fields have validation constraints, and oversize writes fail rather than quietly truncating. Report bodies, source packs, and raw model transcripts reached those limits sooner than ordinary product records did.

The fix was to separate bounded metadata from payloads that can grow. Status, tenant, timestamps, and foreign keys stay in the record. Large reports and transcripts move to native file fields backed by local disk in development and S3-compatible object storage in production. Amazon's [S3 documentation describes the object-storage model](https://docs.aws.amazon.com/AmazonS3/latest/userguide/Welcome.html) that this separation relies on.

One HTML path still keeps a compact representation in a bounded field, so we minify that content and enforce a size ceiling before the write. The important decision is made before persistence: unbounded content is a file, not a row.

## Cost Three: Skip `e.Next()` and the Chain Stops

PocketBase request and record hooks form a chain. A custom hook must call `e.Next()` when later handlers should continue. Missing that call does not produce a helpful architectural error. The current handler runs, the rest of the chain does not, and downstream behaviour simply disappears.

We encountered this where a security hook completed successfully but prevented later processing from running. The endpoint looked healthy from the outside because it returned a response. Only the absent side effect revealed that the chain had stopped.

The safeguard is a contract test around every deploy-critical hook. The test asserts both the hook's own behaviour and the next observable effect in the chain. We also keep hooks narrow: validate or enrich, call the next handler, then perform only the post-processing that genuinely belongs there.

This is a Go application, so the control flow is explicit even when the failure is subtle. The language's [standard embedding support](https://pkg.go.dev/embed) is part of what makes a self-contained binary practical, but a small deployment unit does not remove the need to test lifecycle behaviour across handlers.

## Cost Four: The Logs Are in Another SQLite File

PocketBase request logs are stored in a separate embedded database rather than appearing only on standard output. That is useful once you know where to query. It is confusing during an incident if the team tails process output and assumes silence means no request reached the application.

We changed the operating runbook. Application logs, request logs, background-job state, and deployment events are separate evidence sources. Diagnostics check each source explicitly and correlate timestamps rather than relying on one terminal stream.

The larger lesson is that “one binary” does not mean “one observability surface.” Embedded components still have their own persistence and retention behaviour. Those details belong in the runbook before an incident, not in a chat thread discovered during one.

## The Boundary: When We Would Walk Away

PocketBase remains a good fit while one process can own writes and vertical scaling is acceptable. SQLite's own guidance distinguishes embedded local storage from client-server databases designed for broad concurrency and centralised control; its [appropriate-use guide](https://www.sqlite.org/whentouse.html) is the clearest statement of that boundary.

We would move away when sustained concurrent writes become the bottleneck, when independent services need direct database ownership, when multi-region write availability becomes mandatory, or when a larger engineering organisation needs isolated deployment and failure domains.

We would not migrate because the record count looks impressive or because a client-server database is more conventional. A migration earns its cost when the workload crosses a measured constraint. Until then, the simpler system gives us fewer credentials, fewer schemas, and fewer services to reconcile.

This is also why the engineering layer remains invisible to clients. Snezzi is an AI-led AEO/SEO agency. Its [Lead Engine](/solution/) combines Brand Brain + 6 Agents to get clients cited in ChatGPT, Google AI, Perplexity, and Claude. PocketBase is an implementation choice inside that managed service, not software the client is expected to operate.

## Frequently Asked Questions

### Is PocketBase suitable for production workloads?

It can be suitable when the team accepts its pre-1.0 maturity, reads the changelog, tests migrations, maintains backups, and works within a single-process write model. The official documentation explicitly cautions that full backward compatibility is not guaranteed before version 1.0.

### What happens to PocketBase schema changes during deployment?

Nothing happens automatically because a schema snapshot changed. A committed migration must apply the transition to the deployed database. Contract tests should then confirm that fields, rules, and indexes have the expected shape.

### Why store large generated content in files instead of records?

Large payloads grow independently of their metadata and can exceed application-level field limits. Keeping status and relationships in the record while moving unbounded bodies to file storage makes validation, retrieval, and retention easier to control.

### Does PocketBase replace a job queue?

Not universally. Record hooks and persisted status transitions can coordinate moderate asynchronous workflows, especially when one process owns them. A dedicated queue becomes more appropriate when workloads need independent scaling, delivery guarantees across services, or high parallelism.

### When should a team choose a client-server database instead?

Choose one when concurrent writers, multi-region availability, independent service ownership, or horizontal database scaling are demonstrated requirements. Do not migrate solely because the embedded design looks unconventional.

## Conclusion

PocketBase gave us one authenticated API, one admin surface, one extensible Go process, and a compact operational model. In exchange, we accepted explicit responsibility for migrations, payload boundaries, hook-chain tests, and multiple observability surfaces.

That trade remains worthwhile while the workload fits one writer and the team values operational simplicity. If you want to see how those engineering choices support a managed AI visibility programme rather than another self-serve tool, [book a strategy session with Snezzi](/strategy-session/).
