Baserow Installation Guide: Build a No-Code Relational Database via Docker

Set up a self-hosted no-code database hub. Step-by-step tutorial to installing Baserow on a VPS using Docker Compose.

Baserow Installation Guide: Build a No-Code Relational Database via Docker

Baserow is an open-source, no-code relational database platform that serves as a powerful alternative to Airtable. Under the hood, it is built on a modern, robust stack: a Django-based backend, a Nuxt.js-based frontend, PostgreSQL for relational storage, Redis for caching and message brokerage, and Celery for asynchronous task queueing.

When deploying Baserow in a production environment on a Virtual Private Server (VPS), deploying via Docker Compose offers container isolation, easy updates, and robust service orchestration. However, production deployments require careful tuning of the database backend, client upload limits, WebSocket connections, and Cross-Origin Resource Sharing (CORS) configurations.

This guide provides a comprehensive, production-grade guide to deploying Baserow on a VPS using Docker Compose.


1. Architectural Overview and Data Flow

To deploy and maintain Baserow successfully, you must understand how its components interact.

graph TD
    Client[Web Browser] -->|HTTP / HTTPS| Proxy[Nginx / Caddy Proxy]
    Client -->|WebSocket /ws/| Proxy
    Proxy -->|Port 3000| Frontend[Nuxt.js Frontend]
    Proxy -->|Port 8000| Backend[Django Backend]
    Backend -->|SQL| DB[(PostgreSQL Database)]
    Backend -->|Cache / Queue| Cache[(Redis Cache)]
    Celery[Celery Worker/Beat] -->|Tasks| Cache
    Celery -->|Write SQL| DB
  • Nuxt.js Frontend: Runs on Node.js. It renders the user interface and communicates with the Django backend.
  • Django Backend: Serves the REST API, executes database operations, and handles authentication.
  • PostgreSQL: The single source of truth. Every Baserow table is physically represented as a PostgreSQL table.
  • Redis & Celery: Redis acts as the message broker for Celery. Celery handles long-running asynchronous tasks (such as importing large CSV files, recalculating formulas, and sending notifications).
  • WebSocket Service: Provides real-time synchronization so multiple users can collaborate on the same table simultaneously.

2. Production-Grade docker-compose.yml

Instead of the simplified "all-in-one" container which bundles PostgreSQL, Redis, and Django together, a production-ready setup separates these concerns. This allows you to scale, backup, and configure each component independently.

Create a project directory /opt/baserow and create the following docker-compose.yml file:

version: '3.8'

services:
  db:
    image: postgres:15-alpine
    container_name: baserow-db
    restart: unless-stopped
    environment:
      POSTGRES_USER: ${POSTGRES_USER:-baserow}
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-change_me_secret_password}
      POSTGRES_DB: ${POSTGRES_DB:-baserow}
    volumes:
      - pgdata:/var/lib/postgresql/data
    command: >
      postgres
      -c max_connections=150
      -c shared_buffers=512MB
      -c work_mem=16MB
      -c maintenance_work_mem=128MB
      -c effective_cache_size=1536MB
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U $$POSTGRES_USER -d $$POSTGRES_DB"]
      interval: 10s
      timeout: 5s
      retries: 5

  redis:
    image: redis:7-alpine
    container_name: baserow-redis
    restart: unless-stopped
    volumes:
      - redisdata:/data
    command: redis-server --appendonly yes
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 10s
      timeout: 5s
      retries: 5

  backend:
    image: baserow/baserow:1.24.0
    container_name: baserow-backend
    restart: unless-stopped
    depends_on:
      db:
        condition: service_healthy
      redis:
        condition: service_healthy
    ports:
      - "8000:8000"
    environment:
      # Database config
      DATABASE_HOST: db
      DATABASE_NAME: ${POSTGRES_DB:-baserow}
      DATABASE_USER: ${POSTGRES_USER:-baserow}
      DATABASE_PASSWORD: ${POSTGRES_PASSWORD:-change_me_secret_password}
      DATABASE_PORT: 5432

      # Redis config
      REDIS_HOST: redis
      REDIS_PORT: 6379

      # Security & URLs
      SECRET_KEY: ${SECRET_KEY:-generate_a_secure_random_key_here}
      BASEROW_JWT_SIGNING_KEY: ${BASEROW_JWT_SIGNING_KEY:-generate_another_secure_key}
      PUBLIC_BACKEND_URL: ${PUBLIC_BACKEND_URL:-http://localhost:8000}
      PUBLIC_WEB_FRONTEND_URL: ${PUBLIC_WEB_FRONTEND_URL:-http://localhost:3000}
      BASEROW_CORS_ALLOWED_ORIGINS: ${BASEROW_CORS_ALLOWED_ORIGINS:-http://localhost:3000}
      BASEROW_EXTRA_ALLOWED_HOSTS: ${BASEROW_EXTRA_ALLOWED_HOSTS:-localhost,127.0.0.1}

      # Performance & File Limits
      BASEROW_AMOUNT_OF_ALLOWED_UPLOAD_BYTES: 104857600 # 100MB
      BASEROW_ROW_PAGE_SIZE_LIMIT: 200

      # Run commands
      BASEROW_TRIGGER_SYNC_TEMPLATES: "true"
    volumes:
      - backend_media:/baserow/data/media
    command: backend

  frontend:
    image: baserow/baserow:1.24.0
    container_name: baserow-frontend
    restart: unless-stopped
    depends_on:
      - backend
    ports:
      - "3000:3000"
    environment:
      PUBLIC_BACKEND_URL: ${PUBLIC_BACKEND_URL:-http://localhost:8000}
      PUBLIC_WEB_FRONTEND_URL: ${PUBLIC_WEB_FRONTEND_URL:-http://localhost:3000}
    command: frontend

  celery-worker:
    image: baserow/baserow:1.24.0
    container_name: baserow-celery-worker
    restart: unless-stopped
    depends_on:
      db:
        condition: service_healthy
      redis:
        condition: service_healthy
    environment:
      DATABASE_HOST: db
      DATABASE_NAME: ${POSTGRES_DB:-baserow}
      DATABASE_USER: ${POSTGRES_USER:-baserow}
      DATABASE_PASSWORD: ${POSTGRES_PASSWORD:-change_me_secret_password}
      DATABASE_PORT: 5432
      REDIS_HOST: redis
      REDIS_PORT: 6379
      SECRET_KEY: ${SECRET_KEY:-generate_a_secure_random_key_here}
      PUBLIC_BACKEND_URL: ${PUBLIC_BACKEND_URL:-http://localhost:8000}
    volumes:
      - backend_media:/baserow/data/media
    command: celery-worker

volumes:
  pgdata:
    driver: local
  redisdata:
    driver: local
  backend_media:
    driver: local