Innovate | Connect | Transform
ContactService statusUSD $

GICT knowledge base

How to Self-Host Baserow with Docker on Ubuntu 24.04

Published

Self Hosted
How to Self-Host Baserow with Docker

A spreadsheet works well until several people need to collect and update the same records through a browser. Baserow gives you a database with a spreadsheet-style grid and forms for adding rows, without requiring you to build an application first.

This guide helps you install Baserow 2.3.3 on Ubuntu 24.04 using its supported all-in-one Docker image. You will create an account, import a small CSV, edit a record, and submit a form. Access stays private through an SSH tunnel; no domain is required.

Before you start

Use an Ubuntu 24.04 VPS with 6 GB of RAM and a sudo-capable SSH account, plus a browser and SSH client on your own computer. The examples use ubuntu as the server username and SERVER_IP as the server address. Replace both where necessary. Start with a new Baserow data volume; these are not upgrade or migration instructions.

The all-in-one image runs the application, PostgreSQL, Redis, background workers, and a web proxy together. It is convenient for a small single-server installation, but does not provide high availability. A hosted service is simpler if you do not want responsibility for the server and database. Larger deployments may benefit from Baserow’s separate-service installation.

This article uses the free grid and form features, not paid views or advanced permissions. Public HTTPS and email delivery are outside its scope. Save the account password: password-reset email cannot be delivered until you configure a mail service.

Install Docker and create the project

On a fresh Ubuntu server, install Docker and the Compose plugin:

$ sudo apt-get update
$ sudo apt-get install -y docker.io docker-compose-v2 curl
$ sudo systemctl enable --now docker
$ sudo docker version
$ sudo docker compose version

If you already have a working Docker installation from another source, keep it and check that docker compose version succeeds. Do not mix package sources just to follow this command block.

Create a directory for this installation:

$ mkdir -p ~/baserow
$ cd ~/baserow

Create ~/baserow/compose.yaml with the following contents:

services:
  baserow:
    image: baserow/baserow:2.3.3
    environment:
      BASEROW_PUBLIC_URL: http://localhost:18093
    ports:
      - "127.0.0.1:8080:80"
    volumes:
      - baserow_data:/baserow/data
    restart: unless-stopped

volumes:
  baserow_data:

BASEROW_PUBLIC_URL must match the address you enter in your browser. Here that will be the local end of the SSH tunnel, not the VPS address. Despite the variable’s name, setting it does not make the server publicly reachable.

The named volume holds the all-in-one image’s persistent data, including its embedded database and uploaded files. Keep the mount at /baserow/data. Removing the volume deletes that data; recreating a container is not the same as deleting its volume.

The host binding is deliberately 127.0.0.1:8080. Only the VPS itself can connect to that port directly, and PostgreSQL and Redis have no published host ports.

Start Baserow and wait for readiness

Validate the Compose file, download the image, and start it:

$ sudo docker compose config --quiet
$ sudo docker compose pull
$ sudo docker compose up -d
$ sudo docker compose ps

The image is large, and the first start initializes the database. Give it time to finish rather than repeatedly recreating the container. Check startup progress with:

$ sudo docker compose logs --tail=60 baserow

Wait for the logs to report that Baserow is available and for the container’s health status to become healthy. A container showing only Up may still be initializing. Confirm that the web service responds:

$ curl -I -H "Host: localhost:18093" http://127.0.0.1:8080

The Host header matches BASEROW_PUBLIC_URL; Baserow routes other hostnames differently. An HTTP 302 redirect to the signup or login page is normal. For example, our running installation returned:

HTTP/1.1 302 Found
Location: /login

The response checks the web entry point. The account and table checks below verify that the application works behind it.

Open the private workspace

On your computer, open a separate terminal and start the tunnel:

$ ssh -N -o ExitOnForwardFailure=yes -L 127.0.0.1:18093:127.0.0.1:8080 ubuntu@SERVER_IP

Leave it open, then visit http://localhost:18093 in your browser. SSH forwards that local connection to Baserow on the VPS. Browsing to http://SERVER_IP:8080 will not work with this loopback-only configuration.

Create the first account with your name, email address, and a strong password. At Create your first database, choose File, enter Inventory Lab as the database name, and select Continue. Leave the import screen open while preparing the sample below. Use synthetic records while checking the installation, not customer data.

This tunnel is a network-access choice, not a substitute for account permissions. Anyone else using the installation needs an authorized SSH route or another separately configured access method. Do not remove the loopback binding merely to send someone a form link.

Import a CSV, edit a row, and test a form

On your computer, create a file named inventory.csv with these contents:

Item,Category,Status
Monitor,Office,In use
Router,Network,Spare
Keyboard,Office,In use

On Import your data, enter Inventory as the table name. Leave Import a CSV file selected, choose inventory.csv, and keep First row is header set to yes. Review the import preview before confirming: it should show three fields and three data rows, not an extra record containing the headings. Select Continue, then use Skip for now on the optional profile questions. Wait for the imported table to open. On a fresh installation, this job may wait behind Baserow’s initial template preparation; leave it running rather than submitting the import again.

Finish the welcome tour if it appears, then open the resulting grid. Double-click the Router record’s Status cell, change it from Spare to In use, press Enter, then reload the page and verify that the change remains. These fields can stay as text for this first check; converting them into richer field types is not required to finish installation.

Open the Grid view dropdown, choose Form, name it Inventory intake, and select Add form. Select Add all to include Item, Category, and Status. The imported text fields are required by default; leave Item required. Choose Share form > Create a private link. Open that link in a separate browser session that is not signed in to Baserow but still has access through the tunnel.

Fill Category and Status, but first try submitting without an Item. The form should require it before accepting the submission. Then submit a new synthetic record, such as Desk lamp, Office, and Spare. Return to the authenticated grid and confirm that it now contains four rows, including the new record.

In our 4-vCPU, 6-GB TeamCompute test, importing the CSV produced three rows. The form rejected the missing Item, then accepted one valid submission in 1.30 seconds, including a half-second wait for the success screen. The grid contained exactly four rows afterward. This is a functional check, not a throughput benchmark.

A shared form intentionally accepts submissions from people with its link; it does not give them the database editor. Keep the underlying grid private. Open its normal editor URL in the signed-out session and confirm that Baserow requests a login instead of showing the records.

If the installation does not work

  • The container restarts or stays unhealthy: inspect the startup logs and available memory and disk space. Do not delete the data volume to clear an initialization error. Baserow runs several processes inside this one container.
  • The browser cannot connect: check that the SSH terminal is still open and that local port 18093 is free. If you change the tunnel’s local port, update BASEROW_PUBLIC_URL to match and apply the configuration again with sudo docker compose up -d.
  • The page loads, but the address or links are wrong: use exactly the hostname and port in BASEROW_PUBLIC_URL. localhost, 127.0.0.1, and the VPS address are not interchangeable browser URLs for this setup.
  • A form submits, but you cannot find its row: return to the table used by that form and remove grid filters that might hide the new record. A shared form is an input view of that table, not a separate database.

You now have a working private Baserow installation with an imported table and a tested form. Keep the SSH tunnel open while using it; closing that terminal disconnects your browser without stopping Baserow on the VPS.

Related guides

Account services are unavailable until the production backend is configured.