//------------------------------------------------------------------- //-------------------------------------------------------------------
PocketBase troubleshooting

PocketBase Deployment Troubleshooting: Fix Admin UI Access, CORS, Migrations, and SQLite Lock Errors

PocketBase troubleshooting is one of the most searched topics after deployment because the setup looks simple, but once you put it behind a proxy or add real users, annoying problems start to show up.

This guide covers every common issue you will hit after launch, including the admin UI not loading, CORS failures, broken hooks, migration errors, file upload failures, and SQLite lock errors under light traffic. By the end, you will know how to fix each one and understand when your project is outgrowing what PocketBase can handle.

If you haven’t deployed PocketBase yet, you can check this full PocketBase installation guide on Ubuntu. Then, come back here for PocketBase troubleshooting if you face errors.

1. PocketBase Admin UI Not Loading Behind Nginx or Caddy

This is the number one issue people run into during PocketBase troubleshooting right after deployment. You will see:

  • Blank white screen at yourdomain.com/_/.
  • 404 or 502 errors in the browser.
  • The API responds, but the dashboard does not load.

PocketBase runs on 127.0.0.1:8090 by default. When you put it behind nginx, the proxy needs to be configured correctly. The most common mistake is a missing trailing slash on proxy_pass, or missing headers that PocketBase needs to build correct URLs.

Fix for Nginx

Here is the correct nginx block based on the official PocketBase documentation:

server {
    listen 80;
    server_name example.com;

    client_max_body_size 10M;

    location / {
        proxy_set_header Connection '';
        proxy_http_version 1.1;
        proxy_read_timeout 360s;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_pass http://127.0.0.1:8090;
    }
}

Key things you must check:

  • proxy_pass must end without a trailing slash, not http://127.0.0.1:8090/.
  • proxy_http_version 1.1; is required for SSE to work.
  • proxy_set_header Connection ''; is needed to keep connections alive.

Fix for Caddy

Here is the correct configuration for Caddy:

example.com {
    request_body {
        max_size 10MB
    }
    reverse_proxy 127.0.0.1:8090 {
        transport http {
            read_timeout 360s
        }
    }
}

Running PocketBase in Docker

If you are running PocketBase inside a Docker container, it binds to localhost by default. This means it is invisible to the outside world.

You can fix it by binding to all interfaces:

CMD ["/pb/pocketbase", "serve", "--http=0.0.0.0:8090"]

Without 0.0.0.0, even a perfectly configured nginx will get refused connections.

Serving from a Sub-path

If you need PocketBase at /api/ or /pb/ instead of the root, you must add a rewrite rule:

location /pb/ {
    rewrite /pb/(.*) /$1 break;
    proxy_pass http://127.0.0.1:8090;
}

Also, add a redirect for the admin UI path without a trailing slash:

location = /pb/_ {
    return 302 https://$server_name$request_uri/;
}

2. PocketBase CORS Errors: Why They Happen and How to Fix Them

CORS is another common topic in PocketBase troubleshooting. PocketBase allows all origins (*) by default. So if you are seeing a CORS error, the real problem is never PocketBase itself.

The CORS error in PocketBase looks like this:

Access to fetch at 'https://api.example.com' from origin 'https://app.example.com' 
has been blocked by CORS policy

Real causes for this issue include:

1. Wrong base URL in your SDK:

If you initialize PocketBase with http://localhost:8090 but your frontend is running on https://app.example.com, the browser will block it. Use the public URL:

const pb = new PocketBase('https://api.example.com');

2. HTTP vs HTTPS redirect:

If you type http:// but your server forces HTTPS, the redirect may break the CORS headers. Always use https:// in your SDK init once SSL is active.

3. Reverse proxy stripping or duplicating headers:

If your Caddy or nginx config is manually setting Access-Control-Allow-Origin, it can conflict with PocketBase’s own headers. Remove any manual CORS headers from your proxy config and let PocketBase handle it.

4. 404 masking as CORS:

If your PocketBase URL is wrong and returns a 404, browsers often report it as a CORS error. Double-check that your API URL is reachable.

5. The --origins flag:

If you started PocketBase with --origins=https://myapp.com, then requests from any other origin will fail. Either remove the flag or add all allowed origins:

./pocketbase serve --origins=https://myapp.com,https://www.myapp.com

If You Need Custom CORS in Hooks (v0.23+):

// pb_hooks/main.pb.js
routerUse((e) => {
    e.cors({
        allowOrigins: ["https://myapp.com"],
        allowCredentials: true,
    });
    return e.next();
});

3. PocketBase pb_hooks Not Working: Common Causes and Fixes

JavaScript hooks in pb_hooks/ are powerful, but they fail silently when misconfigured. During PocketBase troubleshooting, broken hooks are not even recognized as the problem.

Hook File Not Loading

File naming matters; PocketBase only auto-loads files with the .pb.js extension. A file named main.js will be ignored.

pb_hooks/
  main.pb.js   ✅ loaded
  helpers.js   ❌ ignored

To check if your hook is loading, add a top-level log at the start of your file:

console.log("Hook loaded: main.pb.js");

Then restart PocketBase and check the output in your terminal or std.log. If you do not see the message, the file was never picked up.

Default Hook Directory

If you do not pass --hooksDir, PocketBase looks for pb_hooks/ next to your pb_data/ directory. If you run the binary from a different working directory, it will not find the hooks. You must use a clear path:

./pocketbase serve --hooksDir=/root/pb/pb_hooks

pb_hooks Deleted and Re-created While Running

If you delete and recreate the pb_hooks folder while PocketBase is running, the file watcher will stop working. The watcher is only initialized on startup if the folder already exists.

Always restart PocketBase after re-creating the hooks directory.

If pb_hooks is a symlink to another directory; changing files inside will not cause PocketBase to reload. You must use a real directory, not a symlink.

Breaking Changes in v0.23+

If you upgraded from before v0.23, your hook syntax may be outdated. The router API changed significantly. Hooks that worked in v0.19 will throw errors in v0.23+.

Old (pre-v0.23):

routerAdd("POST", "/hello", (c) => { ... });

New (v0.23+):

router.POST("/hello", (e) => { ... });

Also, all hook handlers now require e.next() to be called at the end or the chain will break silently:

onRecordCreate((e) => {
    // your logic here
    e.next(); // ← do not forget this
});

4. PocketBase Migration Errors After Upgrading Versions

Migration issues are a common part of PocketBase troubleshooting, especially after version jumps. Here are the migration errors and fixes:

Failed to apply migration on Startup

This usually means you skipped a version that had breaking database changes. PocketBase migrations are sequential; you cannot jump from v0.10 to v0.20 in one step.

To fix it, you must upgrade in stages. For example, if jumping from v0.10 to v0.16, first run v0.13.4, let it migrate, then upgrade to v0.16.

Duplicate Unique Values Blocking Migration

If you get an error like UNIQUE constraint failed during upgrade, you have records with duplicate values in a field that the new version is enforcing as unique at the database level.

Before upgrading, clean up the duplicates in the admin UI or via direct SQL:

-- Find duplicates
SELECT color, COUNT(*) as cnt 
FROM categories 
GROUP BY color 
HAVING cnt > 1;

Then delete or update the duplicates, and try the upgrade again.

Migration File Already Applied, Cannot Re-run

Migrations in PocketBase only run once. If you need to change something in an already-applied migration, you must create a new migration file. To rollback during development:

./pocketbase migrate down

Then fix your migration file and let it reapply on the next startup.

Wrong Collection ID in Go Migrations

When writing Go or JS migrations that create relations, use the collection ID, not the collection name:

// Wrong
Options: &schema.SchemaFieldOptions{
    CollectionId: "users", // this is a name, not an ID
}

// Correct
Options: &schema.SchemaFieldOptions{
    CollectionId: "_pb_users_auth_", // actual ID
}

Export your collections from Admin UI > Settings > Export Collections to find the real IDs.

Automigrate Is Off

If schema changes from the UI are not creating migration files, check that automigrate is enabled. In the admin UI, go to Settings > Application > Auto migrate and enable it. This keeps your pb_migrations/ folder in sync with UI changes automatically.

5. PocketBase File Upload Not Working: How to Fix It

File upload problems are one of the more frustrating areas of PocketBase troubleshooting because the error messages are not obvious.

1. Something went wrong while processing your request:

This is always a file size limit issue at the proxy level, not PocketBase itself.

Nginx fix:

client_max_body_size 50M;

Caddy fix:

request_body {
    max_size 50MB
}

Add this to your server block and reload the proxy config. The default nginx limit is just 1MB, which will block most real-world file uploads.

2. Timeout on Large File Uploads:

If uploads time out before finishing, increase the read timeout in your proxy:

proxy_read_timeout 360s;

The default PocketBase server has a 5-minute read timeout. Your proxy needs to match or exceed this.

3. Field Name Mismatch:

A common mistake in JavaScript: your collection field is named avatar, but you are sending a file in the FormData.

// Wrong
const data = new FormData();
data.append('file', myFile);

// Correct — use your actual field name from the collection
const data = new FormData();
data.append('avatar', myFile);

4. Custom Hooks Conflicting with Upload:

If you have an onRecordCreate hook, it may be interfering with the file upload transaction. Start PocketBase with --dev to see detailed error output:

./pocketbase serve --dev

This will print the full request lifecycle in the terminal, which makes it easy to see where the hook is failing.

6. PocketBase SQLite Database Locked Error (SQLITE_BUSY) Fix

The database is locked (SQLITE_BUSY) error is the most common issue you will hit once you start getting real traffic. It is a core part of any serious PocketBase troubleshooting session.

SQLite uses file-level locking. Even in WAL mode, which PocketBase uses by default, only one writer can hold the lock at a time. If two write operations happen at the same moment, one will wait. If it waits too long, it throws SQLITE_BUSY.

PocketBase already sets busy_timeout and uses WAL mode, but under concurrent load, especially with hooks performing extra writes, lock errors can still occur.

Common causes include:

  • Heavy concurrent API writes, like form submissions and user sign-ups.
  • Hooks that perform additional $app.save() calls during the same transaction.
  • Cascade deletes over large collections.
  • File uploads that hold a transaction open longer than normal.

To prevent this issue, you must consider the following tips.

Keep hooks fast and simple

Do not make external API calls or slow operations inside a hook that also writes to the database. Prepare all data outside the transaction, then do a single fast save.

Do not run two PocketBase instances on the same pb_data

Two processes writing to the same SQLite file will guarantee lock errors. Use a systemd service or Docker to make sure only one instance runs at a time.

Enable WAL mode check

PocketBase enables WAL by default, but you can verify it is active by connecting to your database:

sqlite3 /your/pb_data/data.db "PRAGMA journal_mode;"
# Should output: wal

Add database indexes

Missing indexes on commonly filtered fields cause full table scans, which hold read locks longer. Go to your collection settings and mark frequently filtered fields as searchable or indexed.

Upgrade PocketBase

Version 0.13.0 added auto fail/retry for SELECT queries, which reduced the frequency of lock errors. If you are running an older version, upgrade.

Vertical scaling is your main power

    When SQLite lock errors become frequent, the real fix is more CPU and faster disk I/O, not horizontal scaling. Upgrading to a better VPS is the correct path here.

    If running into lock errors because traffic is growing, you can move to a stronger server. Check out the best Linux VPS options for Docker projects to find a plan that fits your load.

    Is PocketBase Still Right for Your Project? When to Scale Up

    After going through all this PocketBase troubleshooting work, it is worth knowing whether you should keep building on it or plan a migration.

    PocketBase is a great fit for:

    • Side projects, MVPs, and early-stage apps.
    • Apps with mostly read traffic and occasional writes.
    • Small teams who want one binary, no DevOps overhead.
    • Internal tools, admin dashboards, personal apps.
    • Projects with under a few hundred concurrent users.

    Here are the signs you are outgrowing it:

    SignalWhat It Means
    Constant SQLITE_BUSY errorsWrite concurrency is too high for SQLite
    Upload and read timeouts despite proxy tuningServer resources are maxed out
    Hooks timing out on every requestLogic is too heavy for single-binary design
    Need for zero-downtime deploysPocketBase does not support horizontal scaling
    Need multi-region or HA setupNot possible without major custom work

    PocketBase only scales vertically, meaning you get a bigger server. It cannot run as multiple instances sharing the same SQLite file. A properly sized VPS with fast NVMe storage and enough RAM can handle high traffic for most real-world apps.

    If a bigger server is no longer enough and you need multiple servers working together, that’s when it makes sense to switch to Supabase, PostgreSQL with a separate API, or a custom-built backend.

    If your PocketBase app is growing and you need a solid Linux VPS to run it on, PerLod Linux VPS hosting is built for this kind of lightweight, high-performance deployment.

    Final Words on PocketBase Troubleshooting

    PocketBase is a great tool for building a backend fast, but like any tool, it runs into problems once it’s live. Most PocketBase troubleshooting problems come from a few simple causes:

    • Wrong proxy setup
    • Wrong URL
    • Missing file extension
    • Skipped update step
    • Too many writes are hitting SQLite at once

    This guide covers fixes for all the common errors. Go through the section that matches your problem, test it after each fix, and your app should run smoothly. And if your project starts to outgrow a single-server setup, upgrade your server early, before your users notice any slowdown.

    We hope you enjoy this guide. For more information, you can check the PocketBase Official Going to Production Documentation.

    FAQs

    Why is the PocketBase admin UI blank after deployment?

    It is about a reverse proxy misconfiguration. Check your nginx proxy_pass and make sure all the required headers (Host, X-Real-IP, X-Forwarded-For) are set correctly.

    Does PocketBase have CORS disabled?

    No. PocketBase allows all origins by default (*). CORS errors are usually caused by the wrong base URL in your SDK, an HTTP/HTTPS mismatch, or a proxy adding conflicting headers.

    How do I fix the PocketBase Failed to apply migration error?

    Do not skip major versions. Upgrade PocketBase in steps. Each major version may have database changes that must be applied in order.

    Post Your Comment

    PerLod delivers high-performance hosting with real-time support and unmatched reliability.

    Contact us

    Payment methods

    payment gateway
    Perlod Logo
    Privacy Overview

    This website uses cookies so that we can provide you with the best user experience possible. Cookie information is stored in your browser and performs functions such as recognising you when you return to our website and helping our team to understand which sections of the website you find most interesting and useful.