Workload · Running your own NocoDB

Your own NocoDB, self hosted: set it up safely and keep your tables backed up

To run NocoDB self hosted you need one Linux server with Docker, and four pieces working together: NocoDB itself, a worker that handles imports and automations, a PostgreSQL database where your tables and settings live, and Redis to keep things quick. This guide walks you from an empty server to a private, backed-up instance, and explains the licence NocoDB moved to in January 2026, because it decides whether your plan needs a paid licence.

The short version
  1. Plan on 4 vCPU and 8 GB of RAM for real use; 2 vCPU and 2 GB is the floor
  2. You create the admin account privately, before the site is public
  3. HTTPS through Caddy, with the certificate fetched for you
  4. Nightly-ready backup and a restore you can take back
  5. Free for your own organisation; selling access to NocoDB needs a licence

What you are about to run

NocoDB puts a spreadsheet-style screen, with an API behind it, over a real database. The project's quickstart describes the setup as four containers:

  • nocodb, the web app and API you and your team open in the browser.
  • worker, which runs background jobs such as imports, exports and automations.
  • db, the PostgreSQL database holding your data and NocoDB's own metadata.
  • redis, the cache and the queue the worker takes jobs from.

Files people attach to records go into a Docker volume called nocodb_data. Everything else sits in PostgreSQL. Skip the database setting and NocoDB quietly uses a SQLite file instead, which works for a test, but the docs advise PostgreSQL for all deployments and paid licences will not activate without it.

Is NocoDB free for what you want to do?

For a company running it for its own staff, yes; for a business selling access to it, no. Here is how we read the licence; treat it as general guidance, not legal advice. Since release 0.301.0 (13 January 2026) the code ships under the Sustainable Use License 1.0, in the repository's LICENSE.md. It permits using and changing NocoDB solely for internal business purposes of your own, or for personal and non-commercial use, and allows passing copies on only free of charge and for non-commercial purposes. Whoever gets a copy from you must get these terms too, and a changed copy has to say clearly that it was changed.

NocoDB's licence page spells out when you need to pay: hosting or managing NocoDB as a service for others, letting your own customers use NocoDB directly, reselling it, or building it into a platform where customers work in NocoDB itself. The same page says the licence is not OSI-approved; NocoDB calls itself source-available.

Releases up to 0.265.1 carried AGPL-3.0, and copies of those keep it. Under AGPL, distributing copies, changed or unchanged, means handing over the source code, and if you modify it and let people work with your modified copy across the network, AGPL obliges you to offer those people its source. Neither old nor new terms stop you using the free edition inside your organisation.

How big a server does it need?

NocoDB's own figures: at least 2 vCPU, 2 GB RAM and a 10 GB disk, and for production 4 vCPU, 8 GB and 50 GB or more. Those include the database. What grows over time is disk, through attachments (each one capped at 20 MiB unless you raise NC_ATTACHMENT_FIELD_SIZE), so leave room there first.

A Primcast dedicated server gives that job a whole machine: no other customer on it, root access, and a port with no traffic allowance. These are the kinds that fit:

  • For one team, an HPE m710x cartridge (Xeon E3-1585L v5, four 3 GHz cores, NVMe, 16 to 64 GB of memory), which also has its own console.
  • For more people or a lot of attachments, an everyday single-processor server, or a two-processor Xeon E5 or Gold 6130 machine.
  • Leave aside the X-Gene machine, our one ARM cartridge, since Ubuntu 16.04 and 18.04 are all it runs, and the Docker this guide needs wants newer.

On rack servers and blades you get a console screen and a power button that work even when the system will not start; on cartridges it is the m510 and m710x models that include one, and the others give you power control and nothing more. A ready server is handed over within 30 minutes of approval. One made to your specification takes hours, not minutes. You manage the server; we keep the hardware and the network running.

Up and running in an evening

  1. Put Ubuntu 24.04 on the server through the control panel, log in, and type sudo -i.
  2. Install Docker Engine and its Compose plugin following Docker's own Ubuntu instructions.
  3. Paste the block below. It downloads NocoDB's quickstart-demo Compose file from release 2026.10.1 and adjusts it.
TAG=2026.10.1; DIR=/opt/nocodb; YML=$DIR/docker-compose.yml
SRC=https://raw.githubusercontent.com/nocodb/nocodb/$TAG/docker-compose/examples/quickstart-demo/docker-compose.yml
ENGINE=$(docker version --format '{{.Server.Version}}' 2>/dev/null)
if ! [ "${ENGINE%%.*}" -ge 28 ] 2>/dev/null; then echo "STOP - needs Docker Engine 28 or newer (found '$ENGINE')"
elif [ -e $DIR/.env ]; then echo "STOP - $DIR/.env is already there, nothing changed"
else
sed -i 's/^IPV6=.*/IPV6=yes/' /etc/default/ufw && ufw allow OpenSSH && ufw --force enable &&
mkdir -p $DIR && curl -fsSL "$SRC" -o $YML &&
sed -i -e "s/quickstart_demo_pw_change_me/\${PG_PW}/g" \
  -e "s|'http://localhost:8080'|\"\${SITE_URL}\"|g" \
  -e "s|'8080:8080'|'127.0.0.1:8080:8080'|" \
  -e "s|nocodb/nocodb:latest|nocodb/nocodb:\${NC_TAG}|g" $YML &&
sed -i '/NC_REDIS_URL:/a\      NC_AUTH_JWT_SECRET: "${NC_JWT}"' $YML &&
[ "$(grep -c 'NC_AUTH_JWT_SECRET: "${NC_JWT}"' $YML)" = 2 ] &&
grep -q "'127.0.0.1:8080:8080'" $YML && ! grep -q 'quickstart_demo\|:latest' $YML &&
PW=$(openssl rand -hex 24) && [ -n "$PW" ] && JWT=$(openssl rand -hex 32) && [ -n "$JWT" ] &&
install -m 600 /dev/null $DIR/.env &&
printf 'PG_PW=%s\nNC_JWT=%s\nSITE_URL=http://localhost:8080\nNC_TAG=%s\n' "$PW" "$JWT" "$TAG" > $DIR/.env &&
docker compose -f $YML up -d && echo "NocoDB is starting"
fi

What the block changes in that file, and nothing else: the demo password becomes a random one kept in /opt/nocodb/.env (readable by root only), a second random value becomes NC_AUTH_JWT_SECRET for both the app and the worker, the site address and image version come from that same file, and port 8080 listens on 127.0.0.1 only. The firewall goes on with SSH allowed, for IPv4 and IPv6. It refuses to run twice, since fresh credentials would clash with the database already created. Halted before .env was written? Fix the problem and paste again. If only the final docker compose up failed, keep .env (the database may already hold that password) and run cd /opt/nocodb && docker compose up -d by itself.

NocoDB's settings reference puts NC_AUTH_JWT_SECRET among the mandatory settings, used to make authentication tokens, and notes that NocoDB generates a random one if you leave it out. Setting it keeps it in .env and your backups. Never change it later; we expect a new value to log everyone out (our reasoning, not a documented statement).

It stops on Docker Engine older than 28, because only from 28 does a 127.0.0.1 port stay out of reach of other machines (moby#45610). NocoDB's noco.sh installer is quicker, but it puts the site online before an admin exists.

Making yourself the admin before anyone else can

Whoever signs up first becomes super admin, so that should be you, in private. On your own laptop run ssh -N -L 127.0.0.1:8080:127.0.0.1:8080 you@server-ip, leave it open, then load http://localhost:8080 in your browser and create your account. Then, in the super admin settings, turn off open sign-up if only invited people should get in.

Now give it a name: add an A record for your chosen name that points at the server (plus AAAA when it has IPv6), then paste this as root. Caddy, from Ubuntu 24.04's own packages, gets the HTTPS certificate on its own and forwards the Host and X-Forwarded-Proto headers that NocoDB needs behind a proxy. The block also points NC_SITE_URL at the new address, so invitation and password-reset emails link to the right place.

NAME=tables.example.com
DIR=/opt/nocodb; YML=$DIR/docker-compose.yml
if [ "$NAME" = tables.example.com ]; then echo "STOP - replace tables.example.com with your name"
elif ! getent hosts "$NAME" > /dev/null; then echo "STOP - $NAME has no DNS record yet"
else
apt-get update && apt-get install -y caddy &&
printf '%s {\n\treverse_proxy 127.0.0.1:8080\n}\n' "$NAME" > /etc/caddy/Caddyfile &&
caddy validate --config /etc/caddy/Caddyfile --adapter caddyfile &&
ufw allow 80,443/tcp && systemctl reload caddy &&
sed -i "s|^SITE_URL=.*|SITE_URL=https://$NAME|" $DIR/.env &&
grep -qx "SITE_URL=https://$NAME" $DIR/.env &&
docker compose -f $YML up -d --force-recreate nocodb worker && echo "live at https://$NAME"
fi

The last line rebuilds the two NocoDB containers with --force-recreate; a plain restart would keep the old address. Caddy runs directly on the server, so the firewall rule for 80 and 443 is what lets visitors in. To send email, add the NC_SMTP_* settings (host, port, username, password and sender address) to the environment of the nocodb and worker services, and recreate them the same way.

Keeping your tables safe

NocoDB's backup page asks for three things together: your database, the volume with attachments, and your config files. Paste this as root; it saves two scripts next to the stack and takes a first backup straight away. The database dump uses PostgreSQL's custom format with pg_restore, our change from the docs' plain SQL dump.

install -d -m 700 /var/backups/nocodb && cd /opt/nocodb &&
cat > backup.sh <<'EOF' &&
set -euo pipefail; umask 077
OUT=/var/backups/nocodb; S=$(date +%F-%H%M%S); DC="docker compose -f /opt/nocodb/docker-compose.yml"
$DC exec -T db pg_dump -U nocodb -Fc nocodb > "$OUT/db-$S.part"
mv "$OUT/db-$S.part" "$OUT/db-$S.dump"
$DC exec -T db pg_restore -l <"$OUT/db-$S.dump" >/dev/null
docker run --rm -v nocodb_nocodb_data:/data:ro -v "$OUT":/o alpine tar -czf "/o/data-$S.tar.gz" -C /data .
tar -C /opt/nocodb -czf "$OUT/config-$S.tar.gz" .env docker-compose.yml
tar -tzf "$OUT/data-$S.tar.gz" >/dev/null; tar -tzf "$OUT/config-$S.tar.gz" >/dev/null
echo "saved $S"
EOF
cat > restore.sh <<'EOF' &&
set -euo pipefail; S=${1:?give the backup stamp}
OUT=/var/backups/nocodb; DC="docker compose -f /opt/nocodb/docker-compose.yml"
for f in db-$S.dump data-$S.tar.gz config-$S.tar.gz; do [ -s "$OUT/$f" ]; done
[ -e /opt/nocodb/.env ] && bash /opt/nocodb/backup.sh
tar -xzf "$OUT/config-$S.tar.gz" -C /opt/nocodb
$DC up --no-start; $DC up -d db; until $DC exec -T db pg_isready -U nocodb; do sleep 2; done
$DC stop nocodb worker
$DC exec -T db dropdb -U nocodb --if-exists nocodb
$DC exec -T db createdb -U nocodb -T template0 nocodb
$DC exec -T db pg_restore -U nocodb -d nocodb < "$OUT/db-$S.dump"
docker run --rm -v nocodb_nocodb_data:/data -v "$OUT":/o:ro alpine sh -c \
  "find /data -mindepth 1 -delete && tar -xzf /o/data-$S.tar.gz -C /data"
$DC up -d
EOF
chmod 700 backup.sh restore.sh && bash backup.sh

Each run leaves three files stamped with date and time to the second, lists the dump's contents with pg_restore -l to prove it is readable, and checks the two archives the same way. To run it every night, write this line into /etc/cron.d/nocodb-backup: 30 2 * * * root cd /opt/nocodb && bash backup.sh >> /var/log/nocodb-nightly.log 2>&1 && find /var/backups/nocodb -mtime +21 -type f -delete. Old copies go only after a good backup. Then pull the folder onto a second machine with rsync -a, run from there, and leave out --delete.

Going back to an earlier copy is bash /opt/nocodb/restore.sh STAMP, as root. Before it touches anything, it confirms the three files are there and takes a fresh backup of what you have now, config included, and stops if that fails. Changed your mind? Run it again with the stamp that safety backup printed. The safety backup needs the database, so if everything is stopped, start it with cd /opt/nocodb && docker compose up -d db first. Moving servers, as root on the new machine:

  1. Install Docker Engine (28 or later) plus its Compose plugin, and switch on the firewall as the first block does.
  2. Make /opt/nocodb, and /var/backups/nocodb with install -d -m 700; the restore script reads from there.
  3. Copy both scripts into /opt/nocodb and the chosen backup's three files into /var/backups/nocodb.
  4. Run bash /opt/nocodb/restore.sh with that stamp.
  5. Move your DNS record to the new server and paste the Caddy block again.

Updating without surprises

The version is pinned in .env, so nothing changes until you decide. NocoDB's upgrade guide says back up, read the changelog, then pull and start the new image; expect a short pause. So run the backup, set NC_TAG to the new release in /opt/nocodb/.env, then cd /opt/nocodb && docker compose up -d --pull always.

Two more things from NocoDB's hardening list are worth doing on day one: cap Docker's log files in /etc/docker/daemon.json, and point your monitoring at /api/v1/health. If you filter outgoing traffic, allow the licence check NocoDB makes to app.nocodb.com every 6 hours over port 443.

What the free edition will not do

NocoDB's FAQ promises no cap on the number of projects, records or fields in Community Edition. Paid plans hold back the rest, as the purchase page lists them. Business brings single sign-on, permissions per table and field, and teams, while Scale brings audit logs and extra workspaces; Enterprise adds SCIM. You buy Business or Scale from your NocoDB Cloud account, Enterprise is sold by NocoDB's sales team, and none of them activates unless NocoDB runs on PostgreSQL.

Pick something else, or budget for that licence, if you plan to sell NocoDB access to clients or need single sign-on from day one. If you want the tables to trigger workflows, n8n on your own server pairs with it, and a larger database can move to its own PostgreSQL machine.

Questions people ask

Can my company use NocoDB for free?
Yes, for internal use at any size, in our reading of the licence page: it lists startups and enterprises using NocoDB internally as free use. You need a commercial licence only when access to NocoDB itself is what you sell or hand to customers.
What happens to attachments when I restore?
They come back with everything else. The restore script empties the nocodb_data volume and unpacks the archive from the same backup, so files and the records that point to them match. If you move attachments to S3-compatible storage, back up that bucket separately.
Can I connect NocoDB to a database I already run?
Yes, it can open external databases, but by default it refuses ones on local network addresses. Turning on NC_ALLOW_LOCAL_EXTERNAL_DBS lets them through, and NocoDB's docs flag that setting as a security risk, so use it only when you trust everyone with an account.

Give your tables a server of their own

Choose a machine with room for your attachments, put Ubuntu 24.04 on it and follow the steps above.

Get a server for NocoDB

Read next