GuidesBusiness and monitoringTeam database with Baserow

Run Baserow, a no-code database, with rootless Podman

Baserow on Debian 13, an open-source alternative to Airtable where your team keeps customers, projects and orders in linked tables with grid, gallery and form views, in rootless Podman with PostgreSQL behind Caddy, invite only from the start.

Tested on Baserow 2.4.1 on Debian 13 (trixie) on a Melonslab server Updated October 14, 2026

Recommended server for this guide

SSD RAID 1 · ECC DDR4 · 10 Gbit

VC-P Alloy · 2 vCPU · 8 GB Memory · 60 GB Storage

Month to month, no lock-in 7-day money-back guarantee

€8.99/mo

Deploy now
On this page

What you will set up

Baserow is a database that works like a spreadsheet. Your team keeps customers, projects, orders or stock in tables, links rows between them, and looks at the same data as a grid, a gallery or a form. A shared link shows a view read-only, a public form adds rows from people without an account, and everything can be read and written over a REST API, webhooks and Baserow's own automations.

Baserow is made by Baserow B.V., a company in the Netherlands, which also sells a hosted Baserow and paid licences for running it yourself. The core is open source under the MIT licence. The code in the premium and enterprise folders of the same source and image is not: it is under Baserow's own licences, and you may use it in production only with a paid subscription. The free part is what Baserow calls the Open Source Edition. We checked this on 14 October 2026.

In the free edition, the interface shows the paid features with a lock. Premium has the kanban, calendar and timeline views, row comments, row colours, row notifications, personal views, survey forms, export to JSON, XML and Excel, chart widgets and the AI features. Advanced and Enterprise add single sign-on, an audit log, roles and permissions per table and field, data sync with other services, secure file serving and more. Baserow sells them per user; the list and prices are on its pricing page. Grid, gallery and form views, linked tables, lookups and formulas, public sharing, export to CSV, the REST API, webhooks, automations and two-factor authentication are free. The free edition we ran had no row limit either: one table took 3,200 rows, more than the 3,000 per workspace of Baserow's free hosted plan.

Here Baserow runs as a pod under a user of its own called baserow, from Baserow's official all-in-one image, with PostgreSQL for its data, behind Caddy from the Podman guide. The image can also run its own PostgreSQL inside the container, but Baserow recommends an external one for anything in production, and it lets the database password live in a Podman secret and the backup run while Baserow keeps working, as in our other guides. Redis, which Baserow uses only as a queue and cache, stays inside the image: there is nothing in it to back up.

Every step below was run on a fresh Melonslab VC-P Alloy (2 vCPU, 8 GB) with Debian 13:

  • Baserow 2.4.0 started with PostgreSQL 17. The first account, which becomes the instance admin, was created over an SSH tunnel, and sign-up was closed, before Caddy was set up. From outside, the sign-up page said "Sign up is disabled".
  • An invited user signed up through the link in the invite, and saw the workspace's database at once. Two-factor authentication with an authenticator app worked for that user.
  • A database with two tables was created, with text, email, phone, single select, date, number, boolean and file fields, a link from projects to customers, a lookup of the customer's email and a formula. The lookup and the formula filled themselves in.
  • A gallery view showed the projects as cards. A shared grid view opened without a login and could not be edited. A public form added a row, and an automation set its status.
  • A database token listed and created rows over the REST API, and a webhook posted each new row to another service, also to another app on the same server with the setting from the Podman guide.
  • Uploaded files went to the data volume, and opened from their address without a login.
  • A capture of the server's traffic showed no outside connections from Baserow, apart from the webhook. In the browser, the guided tour loads video thumbnails from YouTube.
  • An invite and a password reset were sent through an SMTP test server on the same machine. Without email, both were only written to Baserow's log, as step 6 describes.
  • Baserow was updated to 2.4.1, restored from a backup onto a new user with empty volumes, and everything, logins included, came back by itself after a reboot.

The pod used about 2 GB of memory, almost all of it Baserow, which runs its web server, background workers and Redis in the one container.

Baserow has no Swedish interface. It comes in English, French, Dutch, German, Spanish, Italian, Polish, Korean and Ukrainian, which each user picks under My settings > Account > Interface language.

Baserow or NocoDB? Both are self-hosted alternatives to Airtable, and the NocoDB guide sets up the other one the same way. Baserow's core is open source under the MIT licence, while NocoDB moved to a "fair-code" licence in 2026 that does not let you sell it or run it as a paid service. NocoDB's free edition has kanban and calendar views and comments, which are paid in Baserow, and a Swedish interface. Baserow's free edition has two-factor authentication, which is paid in NocoDB, and automations of its own. Baserow uses more memory: about 2 GB against NocoDB's 850 MB.

Before you start

You need:

  • a server set up as in the Podman guide, with Caddy running, and at least 4 GB of memory;
  • an A record and an AAAA record for baserow.example.com pointing at your server.

The examples use baserow.example.com for Baserow and 203.0.113.10 for your server's IPv4 address. Replace them throughout.

1. Create the user

As root:

useradd -m -s /bin/bash baserow
loginctl enable-linger baserow
machinectl shell baserow@

Everything up to step 5 runs as baserow.

2. Create the secrets

openssl rand -hex 24 | tr -d '\n' | podman secret create baserow-db-password -
openssl rand -hex 32 | tr -d '\n' | podman secret create baserow-secret-key -

The first line makes the database password, which both PostgreSQL and Baserow read. The second makes the key Baserow signs password reset links and other tokens with. Podman stores both, not the files below.

3. Define the pod

mkdir -p ~/.config/containers/systemd
cd ~/.config/containers/systemd

Create baserow.pod:

[Pod]
PodName=baserow
# Only Caddy, on this server, can reach Baserow: the port is not open to the internet.
PublishPort=127.0.0.1:8178:80

[Install]
WantedBy=default.target

Create baserow-db.container:

[Container]
ContainerName=baserow-db
Image=docker.io/library/postgres:17
Pod=baserow.pod
Volume=baserow-db:/var/lib/postgresql/data
Environment=POSTGRES_USER=baserow POSTGRES_DB=baserow
Secret=baserow-db-password,type=env,target=POSTGRES_PASSWORD
HealthCmd=pg_isready -U baserow -d baserow
HealthInterval=10s
Notify=healthy
AutoUpdate=registry

[Service]
Restart=always

And baserow-app.container:

[Unit]
Requires=baserow-db.service
After=baserow-db.service

[Container]
ContainerName=baserow-app
Image=docker.io/baserow/baserow:2.4.1
Pod=baserow.pod
Volume=baserow-data:/baserow/data
Environment=BASEROW_PUBLIC_URL=http://localhost:8178
Environment=DATABASE_HOST=127.0.0.1 DATABASE_PORT=5432 DATABASE_NAME=baserow DATABASE_USER=baserow
Environment=BASEROW_DISABLE_GOOGLE_DOCS_FILE_PREVIEW=true
Environment=BASEROW_TRIGGER_SYNC_TEMPLATES_AFTER_MIGRATION=false
Secret=baserow-db-password,type=env,target=DATABASE_PASSWORD
Secret=baserow-secret-key,type=env,target=SECRET_KEY

[Service]
Restart=always
TimeoutStartSec=900

What the settings do:

  • BASEROW_PUBLIC_URL is the address Baserow expects in the browser. It answers only to that address; any other is treated as a site from Baserow's application builder, and gets "Site not found". It starts as http://localhost:8178 for step 4, and step 5 changes it to the public address.
  • The DATABASE_ settings point Baserow at PostgreSQL in the same pod, which turns off the PostgreSQL inside the image. Your tables are real tables in that database.
  • BASEROW_DISABLE_GOOGLE_DOCS_FILE_PREVIEW=true stops Baserow from previewing Word, Excel, PowerPoint and PDF files through Google's document viewer, which fetches the file from your server. PDF files still open in the browser's own viewer.
  • BASEROW_TRIGGER_SYNC_TEMPLATES_AFTER_MIGRATION=false stops Baserow from importing its 158 templates in the background each time it starts. That import shares a queue with webhooks, exports and file imports, and gets five minutes per start, so without this line they waited up to five minutes after every start until all templates were in. In our test, a webhook arrived four and a half minutes late. You import the templates once by hand instead, below.
  • TimeoutStartSec=900 gives the first start time to download the image.
  • The uploaded files, Baserow's login signing key and the image's own Caddy and Redis data go in the baserow-data volume, the rest in the database.

Baserow stays on an exact version, because it changes its database when it updates, and an update should start with a backup (see Update). PostgreSQL follows version 17 by itself through podman-auto-update.timer. Start it:

systemctl --user daemon-reload
systemctl --user start baserow-pod
systemctl --user enable --now podman-auto-update.timer

The first start downloads about 2.1 GB of images, and Baserow then sets up its database, which took six minutes in all in our test. It is ready when this prints OK:

curl -s http://localhost:8178/api/_health/; echo

Then import Baserow's templates, the ready-made databases it offers to new users:

podman exec -d baserow-app /baserow.sh backend-cmd manage sync_templates

The import runs in the background inside the container, and prints only an ID. It took 17 minutes in our test, while Baserow worked as usual; until it is done, the list of templates is incomplete.

4. Create the admin over an SSH tunnel

The first account on a new Baserow becomes its instance admin, and after that anyone can sign up. Caddy does not show Baserow to anyone yet, so do both through an SSH tunnel, which only you have.

On your own computer, open a tunnel to Baserow's port on the server, and keep it open:

ssh -L 8178:127.0.0.1:8178 root@203.0.113.10

Open http://localhost:8178 in your browser. It shows Create account with "Please fill the form below to create the admin user." Enter your E-mail address, Your name and a long Password, and choose Get started.

Baserow then asks you to create your first database. Leave Scratch selected, give it a name, such as Office, and choose Continue. Pick something to track, such as Projects, and Continue; Skip for now passes on the next questions. The last one, Tell us a bit more, would share your answers with Baserow if you ticked "I'm okay with sharing this with the Baserow team"; skipping it sends nothing. You land in a table called Projects with three example rows, in a workspace named after you.

Now close sign-up. Open the menu with your workspace's name at the top left, choose Admin tools, then Settings, and switch off Allow creating new accounts. It is saved at once. Allow signups via workspace invitations stays on, so the people you invite can still create their account.

Your account can also have two-factor authentication: My settings > Two-factor auth > Enable 2FA, with an authenticator app. Close the tunnel when you are done.

5. Put Caddy in front

As baserow, change the BASEROW_PUBLIC_URL line in ~/.config/containers/systemd/baserow-app.container to the public address:

Environment=BASEROW_PUBLIC_URL=https://baserow.example.com

And restart Baserow, which takes about a minute:

systemctl --user daemon-reload
systemctl --user restart baserow-app

Go back to root with exit, switch to machinectl shell caddy@, and add this block at the end of ~/Caddyfile:

baserow.example.com {
    reverse_proxy 127.0.0.1:8178
}

Restart Caddy with systemctl --user restart caddy, and open https://baserow.example.com. Log in with the account from step 4.

To check that sign-up is closed, open https://baserow.example.com/signup in a private window. It says "Sign up is disabled".

6. Email

Baserow sends email for invites, password resets and notifications. This guide's tests used an SMTP test server on the same machine; we did not send to real mailboxes. Without email:

  • Send invite adds the invite to the list of pending invites, but the email is only written to Baserow's log;
  • Forgot? on the login page says "Check your email", but sends nothing.

Until email works, you can find an invite's link in the log, as baserow, and send it yourself:

podman logs baserow-app 2>&1 | grep -o 'https://[^ "<]*/workspace-invitation/[^ "<]*' | tail -1

To send email, point Baserow at your email provider's SMTP server. As baserow, store the password as a secret:

printf %s 'the-smtp-password' | podman secret create baserow-smtp-password -

Then add these lines to baserow-app.container, under the other Secret= lines, with your own server, account and sender address:

Environment=EMAIL_SMTP=true EMAIL_SMTP_HOST=mail.example.com EMAIL_SMTP_PORT=587 EMAIL_SMTP_USE_TLS=true
Environment=EMAIL_SMTP_USER=baserow@example.com FROM_EMAIL=baserow@example.com
Secret=baserow-smtp-password,type=env,target=EMAIL_SMTP_PASSWORD

Restart Baserow:

systemctl --user daemon-reload
systemctl --user restart baserow-app

EMAIL_SMTP_USE_TLS uses STARTTLS, which port 587 needs. For port 465, which uses TLS from the start, replace it with EMAIL_SMTP_USE_SSL=true and change the port. Baserow treats any value of these two as on, false too, so leave out the one you do not use. After the restart, an invite arrives as "You've been invited to collaborate on Baserow", and Forgot? sends "Reset password - Baserow".

7. Invite your team

Choose Invite others in the sidebar. Enter an email address, pick a role, and choose Send invite:

  • Admin can change everything, including the workspace and its members.
  • Member can create and change databases, tables, fields and rows.
  • Editor, Commenter and Viewer have a lock: roles that limit what someone can change need the paid Advanced plan. In the free edition, someone who should only read gets a shared view instead (step 10).

Baserow's API accepts these three roles in the free edition too, but does not enforce them. In our test, a user invited as Viewer through the API added and deleted rows and created a table, as a Member can. Only give workspace access to people who may change its data.

The invite links to https://baserow.example.com/workspace-invitation/…. There the person creates an account with Your name and a Password, and sees the workspace's databases at once. Members in the sidebar lists everyone, with the invites that are still open under Invites.

By default every user can also create workspaces of their own. Allow everyone to create new workspaces under Admin tools > Settings limits that to instance admins.

A workspace holds databases, and a database holds tables. Step 4 made Office with a Projects table. To add a table for customers:

  1. Under the database in the sidebar, choose New table, keep Start with a new table, name it Customers and choose Add table. It starts with the fields Name, Notes and Active, and two empty rows.
  2. Choose + at the end of the column headers. Give the field a name, such as Email, pick Email in the list of types, and choose Create. Add Phone the same way, as a Phone number.
  3. In Projects, add Status as a Single select, with Add an option for Idea, Doing and Done, Due as a Date, Budget as a Number, Paid as a Boolean and Files as a File.

To link each project to a customer, add a field Customer to Projects of the type Link to table, and pick Customers under Select a table to link to. Switch off Allow multiple relationships, since a project has one customer, and keep Create related field in linked table, so that each customer gets a field that lists their projects. A cell in Customer now offers the rows of Customers.

Two kinds of fields use that link:

  • A Lookup shows a field from the linked row. Add Customer email as a Lookup, choose Customer under Select a link to table field and Email as the field to look up. It fills in by itself.
  • A Formula calculates a value from other fields. Add Budget incl. VAT as a Formula with field('Budget') * 1.25. A budget of 40000 shows 50000.00.

Type in the empty row at the bottom to add a row. To open a whole row, point at it and choose the expand icon that appears in place of its number. The open row has a History of changes next to it. Comments is there too, but row comments need the paid Premium plan.

9. Views

Each table opens in a grid view. The view's name at the top left opens the list of views, where + next to a type adds one:

  • Gallery shows each row as a card; Customize cards picks the fields on them.
  • Form makes a form from the table: click the fields in the left column to add them, and edit the title and description. The fields people outside should not fill in, such as Budget and Paid, stay out.
  • Grid adds another grid, for example with a filter for "my open projects".
  • Kanban, Calendar and Timeline have a lock: they need the paid Premium plan.

Filter, Sort, Group and Hide Fields above a view change only that view, for everyone who opens it.

10. Share a view and a form

Share view at the top of a view, then Create a private link, makes a link that works without a login:

  • For a grid or gallery view, the link at https://baserow.example.com/public/grid/… shows the view read-only, with the fields the view shows, lookups included. In our test, typing in a cell changed nothing. Hide the fields that should not be public in the view before you share it. Restrict access with a password adds a password.
  • For a form view, Share form gives a link at https://baserow.example.com/form/… that lets anyone add a row. A test from a private window showed "Thanks for submitting the form!", and the row appeared in the table.

Disable shared link stops a link. Visitors of a shared view or form can report it with Report abuse, which notifies the instance admins.

11. Files

A File field takes files: open a row, choose Add a file under the field, then my device, pick the file, and Upload. Baserow sets no practical size limit; BASEROW_FILE_UPLOAD_SIZE_LIMIT_MB in baserow-app.container sets one.

The files go in the baserow-data volume, under media. Each gets an address with a random part, such as https://baserow.example.com/media/user_files/KfsRem8G…_f002….png, and anyone who has that address can open the file without logging in. Serving files only to logged-in users is the paid Enterprise feature "secure file serving". Keep that in mind before you upload documents with personal data.

To keep files in S3 storage instead, such as Garage on another server, Baserow has AWS_ settings in its documentation; the browser then loads the files from the storage's address. We did not test that.

12. Database tokens and the REST API

Every table can be read and written over a REST API. Open the menu at the top left, My settings > Database tokens > Create token, give it a name, such as scripts, pick the workspace and choose Create token. The switches next to it set whether the token may create, read, update and delete rows, for the whole workspace or per database and table. The … next to its name shows the token behind the eye icon, and Generate new token replaces it.

The address of a table in your browser holds its ID: in https://baserow.example.com/database/88/table/318/1275, the table is 318. To list the projects, from any computer:

curl -s "https://baserow.example.com/api/database/rows/table/318/?user_field_names=true&include=Name,Status" \
  -H "Authorization: Token YOUR_TOKEN"

The answer is JSON with the rows under results, 100 at a time; next holds the address of the next page. It starts with http://, which Caddy redirects to https://, so give curl the -L option to follow it. To add a project:

curl -s -X POST "https://baserow.example.com/api/database/rows/table/318/?user_field_names=true" \
  -H "Authorization: Token YOUR_TOKEN" -H 'Content-Type: application/json' \
  -d '{"Name": "Annual report", "Status": "Idea"}'

View API documentation in the same menu opens documentation made for your own database, with every table and field.

13. Webhooks

A webhook calls an address of yours when rows change, for example a workflow in n8n. Open the ⋮ next to the view's name and choose Webhooks > Create webhook. Give it a Name, enter the URL, choose Let me select individual events and tick, for example, Rows are created. Trigger test webhook sends a sample and shows the answer, and Save turns it on.

Each call is a POST with JSON, with "event_type": "rows.created" and the new rows under items. In our test, it arrived within a second of a row being added, and the webhook's list shows the last call.

Baserow refuses webhook addresses on private networks, such as localhost and 192.168.x.x, with "Invalid URL", so that a webhook cannot be used to reach services inside your network. An address on the internet, such as https://n8n.example.com/webhook/…, works. If n8n runs on this same server, Baserow refuses its address too, until the baserow user has the containers.conf from the Podman guide, step 8; then restart the pod with systemctl --user restart baserow-pod. Rows enter view has a lock: it is one of the paid "advanced webhooks".

14. Automations

Baserow has its own automations in the free edition. Add new... at the bottom of the sidebar > Automation creates one, with a name, and opens a workflow:

  1. Pick what starts it. Local Baserow has Rows are created, Rows are updated and Rows are deleted in a table, and Core has a Periodic trigger.
  2. Choose + below it for an action: create, update or delete rows under Local Baserow, or HTTP, SMTP Email, Slack and AI, among others.
  3. Start test run tries it, and Publish makes it live.

In our test, a workflow started by Rows are created in Projects, with Update a row in Projects, the Row ID taken from the trigger (Previous node > Rows are created in Projects > 0 > Id) and Status set to Idea, gave every project sent through the public form the status Idea. History at the top lists each run.

Baserow's documentation says that, like webhooks, automations refuse private network addresses for HTTP requests and SMTP servers.

15. Outside connections

We captured the server's DNS lookups and outgoing connections while Baserow started, was updated, and while the steps above were done. Apart from Docker Hub, when images were downloaded, the only connections from Baserow were the webhook and the automation test we set up ourselves. Baserow made no update check and sent no usage statistics. Its code sends data to Baserow in two cases, which we did not use: the onboarding questions in step 4 if you tick the box to share them, and a check of the licence once you add a paid licence. Usage statistics with PostHog and error reports with Sentry are off unless you set POSTHOG_PROJECT_API_KEY or SENTRY_DSN.

In the browser, the guided tour that Baserow shows each new user loads video thumbnails from img.youtube.com, and plays the videos from www.youtube-nocookie.com if you open them. The emails Baserow sends load the Inter font from Google Fonts when they are opened, as many emails do. Everything else in the browser came from your own server.

Back up

As baserow:

umask 077
mkdir -p ~/backup
podman exec baserow-db pg_dump -U baserow -Fc baserow > ~/backup/baserow-db.dump
podman volume export baserow-data > ~/backup/baserow-data.tar
podman secret inspect --showsecret --format '{{.SecretData}}' baserow-secret-key > ~/backup/secret-key.txt

The database holds the tables, rows, users, views, shared links, tokens, webhooks and automations; the volume holds the uploaded files and the key Baserow signs logins with. The dump runs while Baserow keeps working, and took a few seconds for our test data. Copy ~/backup to another machine, for example with restic, and keep it private: it holds all your data.

The image also has a backup command of its own, backend-cmd-with-db backup, but it is made for the PostgreSQL inside the image, and Baserow asks you to stop Baserow first. With PostgreSQL in its own container, pg_dump does the job.

To restore into an empty pod, as baserow, with the backup in ~/backup:

systemctl --user stop baserow-pod
podman volume rm baserow-db baserow-data
podman volume create baserow-data
podman volume import baserow-data ~/backup/baserow-data.tar
systemctl --user start baserow-db
podman exec -i baserow-db pg_restore -U baserow -d baserow --no-owner < ~/backup/baserow-db.dump
systemctl --user start baserow-app

Starting only the database first loads the backup before Baserow sets up an empty one. On a new server, follow steps 1 to 3 and step 5, skip step 4, and create the key from the backup instead of a new one in step 2:

tr -d '\n' < ~/backup/secret-key.txt | podman secret create baserow-secret-key -

We restored onto a new user in this way. The rows, files, users and their passwords, the database token, the shared links and the automation all worked, and a browser that was logged in before stayed logged in.

Update

Baserow publishes a release every few weeks, listed on its GitHub page with notes about what changed. To update, as baserow, back up first, then change the version in the Image= line of baserow-app.container, for example to 2.4.2, and restart:

systemctl --user daemon-reload
systemctl --user restart baserow-app
podman image prune -a -f

Baserow updates its database when it starts, which took about two minutes from 2.4.0 to 2.4.1. podman image prune -a removes the old image, which takes about 1.7 GB. Baserow says you can go straight to the newest version from any older one. Baserow does not update its templates by itself; to get the new version's templates, run the sync_templates command from step 3 again. It imports only the ones that changed, and took 13 seconds when none had.

PostgreSQL updates itself within version 17. Moving to a newer major version of PostgreSQL needs a dump and restore, as in Back up.

Troubleshooting

"Site not found" instead of Baserow. The address in the browser is not the one in BASEROW_PUBLIC_URL. Check the line in baserow-app.container, with https:// and no slash at the end, then daemon-reload and restart baserow-app.

curl prints an error page instead of OK. Baserow is still starting. The first start takes several minutes; podman logs baserow-app shows "Baserow is now available" when it is ready.

Strangers can sign up. Allow creating new accounts is still on, step 4. Remove accounts you do not know under Admin tools > Users.

"Check your email", but no email comes. Email is not set up, step 6. The link is in Baserow's log.

A webhook says "Invalid URL". Its address is on a private network, which Baserow refuses, or belongs to another app on this server, which needs the setting from the Podman guide (step 13).

Webhooks arrive minutes late after a restart. The BASEROW_TRIGGER_SYNC_TEMPLATES_AFTER_MIGRATION=false line from step 3 is missing, so Baserow imports templates first.

Run it on your own server

SSD RAID 1 · ECC DDR4 · 10 Gbit

VC-P Alloy

€8.99/mo

vCPU
2
Memory
8 GB
Storage
60 GB
Transfer
10 TB
Performance
SSD · RAID 1
  • Full root access
  • Native /64 IPv6
  • RAID-protected storage
  • Malmö, Sweden
  • Month to month, no lock-in
  • 7-day money-back guarantee
All guides