DEV Community

Great Sage
Great Sage

Posted on

NocoDB self-hosting will quietly delete your bases if you skip this one env var

I've been packaging Railway templates for self-hosted tools, and NocoDB turned into a longer rabbit hole than I expected — not because the app is bad, it's genuinely a solid open-source Airtable alternative — but because its default self-hosting config has a landmine in it that isn't obvious until you hit it.

The landmine: NC_DB unset means SQLite means data loss

NocoDB stores two very different things. Your actual data — if you connect an external Postgres/MySQL source — lives wherever that source is. But NocoDB's own metadata (every base you create inside NocoDB itself, views, filters, users, shared links, API tokens) lives in whatever database NC_DB points at.

Leave NC_DB unset and it defaults to a SQLite file inside the container. That's fine on a machine where the container never gets recreated. It's not fine on any platform-as-a-service (Railway, Render, Fly, a Docker Compose stack without a bind mount) where a redeploy gives you a fresh container filesystem. The SQLite file goes with it. Every base, every user account, gone — silently, no error, no warning at deploy time. You just log back in to an empty instance.

This is an easy thing to miss because NocoDB works perfectly during setup and testing. You only find out on your first redeploy, which by definition happens after you've already put real data in.

The second landmine: attachments aren't in the database either

Even once you've pinned NC_DB to a real Postgres, file-type cell attachments (uploaded images, PDFs, whatever) are written to disk at /usr/app/data, not into the database. If that path isn't on a persistent volume, attachments vanish on redeploy even though your bases and rows survive fine. Two separate things to get right, not one.

Third: the auth secret

NC_AUTH_JWT_SECRET needs to be generated once and then stay pinned. If it regenerates on every boot (which happens if you don't set it explicitly and the image generates a random one at runtime), every session and every issued API token invalidates on restart — annoying for humans, worse if you've got automations hitting the API with a token that just silently stopped working.

What I did about it

I maintain a one-click Railway template for NocoDB (disclosure: I get a referral kickback if you deploy through it — full transparency, not hiding that): [deploy link below]. It ships as two services — NocoDB plus its own dedicated Postgres 17 — wired together over Railway's private network from first boot, with:

  • NC_DB pointed at Postgres from the start, on a volume, so metadata survives redeploys
  • A second volume mounted at /usr/app/data for attachments
  • NC_AUTH_JWT_SECRET generated once and pinned, not regenerated per boot
  • NC_PUBLIC_URL bound to the actual Railway domain so shared views and form links resolve correctly instead of pointing at localhost

If you'd rather not use Railway at all, the fix is the same wherever you run it: set NC_DB to a real database connection string (Postgres or MySQL) before your first deploy, put /usr/app/data on a persistent volume, and pin NC_AUTH_JWT_SECRET explicitly instead of letting it default. The official NocoDB docs cover the exact env vars if you're rolling your own docker-compose.

One gotcha worth knowing regardless of where you host it: whoever signs up first becomes the instance's super admin. Claim it immediately after your first deploy, before you share the URL with anyone.

Deploy link (Railway, Postgres 17 + volumes included): https://railway.com/deploy/nocodb-airtable-al-1?referralCode=Z1xivh&utm_medium=integration&utm_source=template&utm_campaign=inventory

Happy to answer questions on the setup, connecting external data sources, or the Postgres sizing if anyone's running a bigger install.

Top comments (0)