Workload · Running your team's database

Baserow self hosted: your team's no-code database on a server of your own

To run Baserow on your own server, you start the official baserow/baserow Docker image, which packs the app, its PostgreSQL database, Redis and a Caddy web server into a single container. The install itself is short. What deserves your attention is the order: whoever signs up first becomes the administrator, so that has to be you, while the page is still hidden from everyone else. This guide takes you through it step by step, then email, backups and updates.

The short version
  1. One container holds the app, PostgreSQL, Redis and HTTPS
  2. Make the admin account through an SSH tunnel, before going public
  3. Settings in an env file only root can read (mode 600)
  4. Nightly copies of the data volume, made the way Baserow's docs do it
  5. Open-source core carries the MIT Expat licence; paid features need a key

How big a server does Baserow need?

Less than you might fear, though Baserow does not print a number. Its documentation lists the software it supports, Docker 19.03 or newer and PostgreSQL 14 or newer with 15 recommended, but no memory or processor minimum. The image looks after the software side for you: it brings its own PostgreSQL 15.

What uses memory is the number of background workers. In this image Baserow runs one background worker by default. If memory is tight, setting BASEROW_RUN_MINIMAL to any value, together with that single worker, folds the export jobs into the same process to save memory; the trade-off, the docs say, is that a slow job such as a large snapshot can hold up quick ones like live row updates. The other thing that grows is disk: every file your team attaches to a row lives in the data volume, beside the database.

One container or several?

Baserow offers two Docker routes. The one-container image is the one its own compose file tells most people to use. The multi-container docker-compose.yml splits the backend, frontend, workers, PostgreSQL and Redis into separate containers, and its header calls it the choice for advanced users.

You wantPick
The fewest moving parts and secrets made for youThe baserow/baserow image
To reach PostgreSQL with your own tools, or scale out laterThe multi-container compose file
To run Baserow on several machines at onceBaserow's per-service images, deployed with Helm or Kubernetes

This guide uses the single image, described in a short compose file so the settings live in one place you can read later.

The server that fits

Baserow wants a machine to itself more than a big one. With our dedicated servers you rent the whole computer: nobody else's work runs on it, you log in as root, and nobody meters the port, so imports and exports never turn into a bandwidth bill.

  • Small cartridge servers suit a Baserow that is the only job on the box.
  • Everyday single-processor servers suit Baserow next to the tools that read from it, such as a workflow tool calling its API.
  • Reinstall the operating system from the control panel whenever you want a clean start, with no ticket.

A ready server is handed over within 30 minutes of the payment being approved, and it is month to month. After a firewall slip, an out-of-band console still reaches the machine: rack servers and blades have one, and so do two cartridge models, m510 and m710x.

Install it privately first

  1. Log in, become root with sudo -i, and paste the block below.
  2. It switches IPv6 on for the firewall before adding any rule, allows only SSH, and turns the firewall on without the yes/no prompt that would swallow the next line.
  3. It creates baserow.env with mode 600, so only root can read it, and adds the address you will use for the first visit plus a random SECRET_KEY. Both lines are written by the shell's built-in echo, so no separate process ever carries the key as an argument.
  4. It starts Baserow with its web port bound to 127.0.0.1, which means only the server itself can reach it.
sed -i 's/^IPV6=.*/IPV6=yes/' /etc/default/ufw
ufw allow OpenSSH
ufw --force enable
# Docker Engine 28 or newer with the Compose plugin (docs.docker.com); check with: docker version
mkdir -p /opt/baserow && cd /opt/baserow
install -m 600 /dev/null baserow.env
echo "BASEROW_PUBLIC_URL=http://localhost:8080" >> baserow.env
echo "SECRET_KEY=$(tr -dc 'a-z0-9' < /dev/urandom | head -c50)" >> baserow.env
cat > compose.yml <<'EOF'
services:
  baserow:
    container_name: baserow
    image: baserow/baserow:2.4.1
    env_file: baserow.env
    ports:
      - "127.0.0.1:8080:80"
    volumes:
      - baserow_data:/baserow/data
    restart: unless-stopped
volumes:
  baserow_data:
    name: baserow_data
EOF
docker compose up -d
docker logs -f baserow

Wait until the log says Baserow is now available, then press Ctrl+C. You do not set DATABASE_PASSWORD here: with the built-in database, the image generates that password on first start and keeps it in the data volume, in /baserow/data/.pgpass. You only set it yourself when you point Baserow at your own separate PostgreSQL.

Claim the admin account, then open the doors

Baserow's admin documentation says the first account created on a self-hosted instance automatically becomes the Instance Admin, with full control over every user and workspace. So you make that account through an SSH tunnel, signed in with your personal account, while the page is still invisible to the internet.

Once you are in, open Admin tools, then Settings, and under Account restrictions switch off Allow creating new accounts. The sign-up page vanishes. Once sign-ups are off, a second switch shows up, Allow signups via workspace invitations; turn it on if colleagues should join by invitation. Then make the site public:

# On your own computer, signing in with your usual account on the server:
ssh -N -L 8080:127.0.0.1:8080 you@your-server
# Open http://localhost:8080, create your account, switch off new sign-ups.

# Back on the server, as root, once your domain's DNS points here:
cd /opt/baserow
sed -i 's|^BASEROW_PUBLIC_URL=.*|BASEROW_PUBLIC_URL=https://tables.example.com|' baserow.env
echo "BASEROW_CADDY_ADDRESSES=:443" >> baserow.env
cat > compose.override.yml <<'EOF'
services:
  baserow:
    ports:
      - "80:80"
      - "443:443"
EOF
ufw allow 80,443/tcp
docker compose up -d

BASEROW_PUBLIC_URL must be exactly the address people type, or Baserow treats the visit as a published app and you get the wrong page. With :443, the built-in Caddy fetches the certificate and sends plain HTTP to HTTPS. Ports listed without an address, like these, are published on 0.0.0.0 and [::]. On IPv4, ufw never sees that traffic; on IPv6 as well when Docker manages IPv6 itself, but otherwise ufw filters it, hence the allow line. Keep private ports on 127.0.0.1: from Docker Engine 28 on (moby#45610), other machines cannot reach such a binding.

Getting email out

Until you configure a mail server, Baserow sends nothing. Invitations and password-reset messages only appear in its worker log. Add these lines to baserow.env, then run docker compose up -d from /opt/baserow:

  • EMAIL_SMTP=true, which tells Baserow to start sending.
  • EMAIL_SMTP_USER and EMAIL_SMTP_PASSWORD from your mail provider, with its server in EMAIL_SMTP_HOST and EMAIL_SMTP_PORT, and FROM_EMAIL for the sender address.
  • EMAIL_SMTP_USE_TLS=true for STARTTLS-style connections, usually on port 587, or for port 465 EMAIL_SMTP_USE_SSL=true. Set one, not both: Baserow counts any non-empty value as yes.

The SMTP password now sits in the same mode-600 file as your secret key, which is why that file stays readable by root alone.

Nightly copies you can restore

Baserow keeps everything in one place: the database, uploaded files and the generated secrets all live in the data volume. Its documentation backs that volume up with Baserow stopped, by archiving it with tar, and the script below does the same each night at 04:15 server time, so expect a short pause then. Pasting the block only writes the script and the schedule. The start command runs even if the copy fails, so Baserow always comes back.

mkdir -p -m 700 /var/backups/baserow
cat > /opt/baserow/backup.sh <<'EOF'
t=$(date +%F)
docker stop baserow
docker run --rm -v baserow_data:/baserow/data -v /var/backups/baserow:/backup ubuntu tar cf /backup/baserow-$t.tar /baserow/data
docker start baserow
cp /opt/baserow/baserow.env /var/backups/baserow/env-$t
EOF
chmod 700 /opt/baserow/backup.sh
echo '15 4 * * * root umask 077; bash /opt/baserow/backup.sh > /var/log/baserow-backup.log 2>&1' > /etc/cron.d/baserow-backup

To restore, the docs unpack the archive into a fresh volume and start Baserow on it. Before a big change you can also stop Baserow and use the image's own backend-cmd-with-db backup command, which saves just the database. Then copy the archives somewhere other than this server: they hold your data and your secrets. You manage the server; we keep the hardware and the network running.

Staying on the current version

Updating is a change of one line. Take a backup, change the image tag in compose.yml to the new release, run docker compose up -d, and watch docker logs -f baserow. Baserow upgrades its own database as it starts, and you can jump straight to the newest release without the ones in between. A very large jump can print column errors from the background workers while it migrates; the docs say these stop on their own. Only a data volume created before version 1.24 needs an extra step first, a one-time PostgreSQL upgrade the docs describe.

What you pay for, and what you don't

Baserow's LICENSE file calls everything outside its listed exceptions (the docs/, premium/ and enterprise/ folders, plus third-party components) the Baserow Open Source Edition, and makes it available under the "MIT Expat" license, so your team can use it without paying anyone. Of those exceptions, the documentation folder uses Creative Commons CC BY-SA 4.0, bundled outside code stays under whatever licence its owner chose, and the two paid-feature folders each carry a LICENSE file of their own. To use those features in production you buy one of three self-hosted licence types, Premium, Advanced or Enterprise (only one kind per instance), and register its key in Admin tools, under Manage licenses; the key works only for your instance's ID. People without a paid seat keep working alongside those who have one.

Want something to automate around your tables? See n8n on your own server. For a database with a ready-made API on top, read self-hosted Supabase.

Questions people ask

Can I run Baserow on my own server for free?
Yes. Baserow's open-source edition (all the code not named as an exception in its LICENSE file) carries the MIT Expat licence and needs no key. Some features live in the premium and enterprise code, and using those in production means buying a licence for your instance. Users without a paid seat can still share workspaces with those who have one.
Why does Baserow show the wrong page at my address?
Check that BASEROW_PUBLIC_URL matches the address in your browser. If it does not, Baserow treats any other address as a published application. Set it to the exact address, including https and any port, and restart with docker compose up -d.
How can I keep outsiders from registering?
Open Admin tools, then Settings, and switch off Allow creating new accounts under Account restrictions. Only the instance admin sees that page, which is why you create the first account yourself, over an SSH tunnel, before the site is public.
Where are Baserow's passwords and keys stored?
In this setup, your secret key and SMTP password are in baserow.env, readable only by root. The database password is generated by the image on first start and stored inside the data volume, so your backup archive contains it too. Keep those archives somewhere safe.
Do I need to know Docker to run it?
A little. You will paste a few commands, read a log and edit a text file. The single-container image keeps that to a minimum: one container to start, stop, update and back up.

Give your team's tables a home

Choose a server, install Ubuntu, and follow the steps above: a ready server is handed over within 30 minutes of the payment being approved.

Get a server for Baserow

Read next