A backend in a single file: a modern interface, simple deployment and flexibility – but also no SQL through the API, a pre-1.0 status and an admin panel that can't be extended
Stefan Flaschko · 08.09.2026
I used PocketBase for LingoDeck, a flashcard app for language learning with spaced repetition, AI-generated cards and text-to-speech. The front end is a Nuxt 4 app, and the back end is PocketBase 0.30 with the JavaScript SDK. Time for an honest review.
PocketBase is an open-source back end (MIT licence) that consists of a single executable file. It is written in Go and ships with everything important:
You start the file with ./pocketbase serve and have a working back end. There are SDKs for JavaScript and Dart. In the app, the Nuxt pages and the Nuxt server talk to PocketBase through the JS SDK. I haven't needed realtime or OAuth so far.

The admin panel is fast, tidy and surprisingly complete for a back end you install in a minute. The screenshot shows the cards collection with search and filter. Everything else is in one place too:
The interface is aimed at developers, though. It is not a replacement for a classic CMS when editors maintain content. More on that below.
The data model is free, and the access rules are more powerful than they first look. Every collection has five rules (list, view, create, update, delete), and each of them is a short expression. Users may, for example, only see and create their own records:
notes.listRule = 'owner = @request.auth.id'
notes.createRule = '@request.auth.id != "" && owner = @request.auth.id'
Or content is publicly readable, but can only be changed by signed-in users who created the record themselves:
articles.listRule = ''
articles.updateRule = '@request.auth.id != "" && author = @request.auth.id'
An empty rule means "everyone", null means "superusers only".
There is no middleware and no endpoint that checks this. PocketBase evaluates the rules on every request, even when someone calls the API directly. That saves you custom permission logic.
When rules aren't enough, there are several options:
pb_hooks) directly inside the programauth-refresh and then call external APIs whose keys shouldn't end up in the browser.The limit is the admin panel. It can't be extended. There are no custom views, dashboards or fields in the back end. If you need, say, your own user management or a dashboard, you have to build it in the app yourself, and I wouldn't use PocketBase for clients without building a custom interface. With Payload or Statamic, that is easier.
This is a mixed bag. On the plus side, almost everything can be described as text:
getFullList, getList, create, update and delete, plus filter, sort and expand.types.d.ts) that the editor and the assistant can use.Less good: the truth about the schema lives in the database, not in the code. Unlike a code-first system such as Payload, an assistant only sees the schema if the migrations are complete. And the risk of outdated code is real. PocketBase changed a lot with version 0.23, and many examples online still belong to the old API. In my case, the first migrations were written against an older version. As a result, created and updated were missing, and sorting by -updated failed with a 400 error. An additional migration fixed it.
My conclusion: well suited as long as you keep the migrations in the repository and tell the assistant which version you use. In my project, that is written down in AGENTS.md, together with the rules and quirks.
This is the biggest advantage for me. A deployment is a single file, and in my setup it works like this:
pb_migrations/ are uploaded./api/health) confirms that it is running again.There is no database to install and maintain separately, and no dependencies to build. Updating PocketBase itself is a new file as well (./pocketbase update). Operation is simple too: PocketBase listens on 127.0.0.1:8090, and nginx forwards /pb there and handles SSL.
Two things to keep in mind:
pb_data.The file is about 32 MB. On my machine, the process used about 60 MB of memory with roughly 500 records. There is no licence, no per-user pricing and no limit to one project. In return, you take care of the server and backups yourself.
Here you have to distinguish. The requirements are low, but PocketBase is not static hosting. It needs:
pb_data folder with the database and uploadsCompared with a Node-based CMS that is an advantage, compared with a site builder or static pages it is a disadvantage. On top of that, SQLite allows exactly one write at a time and can't be spread across several servers. For an app on one server that's no problem. If you want to scale horizontally, PocketBase is a problem.
This is only partly true, so here is a closer look. Under the hood PocketBase is real SQL, because the data lives in a SQLite file. From the outside, though, access doesn't go through SQL but through its own API with its own filter syntax:
const defaults = await pb.collection('cards').getFullList({
filter: 'owner = ""',
})
const progress = await pb.collection('card_progress').getFullList({
expand: 'card',
})
That is pleasant for most queries, but it is no more than what the API offers:
expand.There are ways out. View collections are defined with your own SQL SELECT; they are read-only but can be queried like any other collection. In hooks you can also run raw SQL. If you expect joins, reports and complex queries from the start, choose a classic database such as Postgres instead. PocketBase doesn't support any database other than SQLite.
PocketBase is still version 0.x. The project itself points out that backward compatibility isn't guaranteed before 1.0. The move to 0.23 showed that: collections and migrations were reworked, and old examples no longer work. With your own migrations and pinned versions it is manageable, but you should plan an update deliberately and not take it along with the next deploy.
On top of that, PocketBase is essentially maintained by a single person. The project is very active and stable, but for a long-lived product you have to accept that risk. Because the data sits in a SQLite file and the API rules are readable, a later move would still be possible.
As with any back end that has a database, the content isn't part of Git. The migrations only describe the structure. If PocketBase starts without the pb_data folder that holds the database (on a new machine or a rebuilt server), it begins with an empty database. The migrations then create the structure but no content, and the app starts with empty collections.
PocketBase can create backups automatically, including to S3, but you have to set that up and test it now and then. A backup is only a backup once you've restored it successfully.
[email protected] doesn't match [email protected]. You should therefore lowercase email addresses in the form already.emailVisibility.PocketBase is a good fit for small to medium apps with login, user data and clear access rules, especially when you run the back end yourself and want as little infrastructure as possible. The admin panel is modern, the rules save a lot of back-end code, and deployment is as simple as it can be for a back end with a database. With migrations in the repository, AI assistants work well with it too.
The disadvantages are real: there is no SQL through the API, the admin panel can't be extended, scaling is limited to one server, and with version 0.x you have to plan updates carefully. For projects where editors maintain content, I would choose a CMS such as Payload or Statamic. PocketBase is an interesting product, but for a new project I would choose a different system such as Payload or Appwrite.