How to Self-Host Grist with Docker Compose

What Is Grist?

Grist is an open-source spreadsheet-database hybrid that combines the flexibility of spreadsheets with the structure of a relational database. It uses full Python for formulas instead of Excel syntax, stores everything in portable SQLite files, and provides granular access controls down to the row and column level. Think Airtable, but self-hosted with no per-seat pricing.

Updated March 2026: Verified with latest Docker images and configurations.

Prerequisites

  • A Linux server (Ubuntu 22.04+ recommended)
  • Docker and Docker Compose installed (guide)
  • 1 GB of free RAM (2 GB recommended)
  • 2 GB of free disk space plus storage for documents
  • A domain name (optional, for remote access)

Docker Compose Configuration

Create a docker-compose.yml file:

services:
  grist:
    image: gristlabs/grist:1.7.11
    container_name: grist
    restart: unless-stopped
    ports:
      - "8484:8484"
    environment:
      # Admin email — first user with this email becomes admin
      - GRIST_DEFAULT_EMAIL=${GRIST_ADMIN_EMAIL}
      # Session encryption key — generate with: openssl rand -hex 32
      - GRIST_SESSION_SECRET=${GRIST_SESSION_SECRET}
      # Require login for all access
      - GRIST_FORCE_LOGIN=true
      # Enable Python formula sandboxing (recommended)
      - GRIST_SANDBOX_FLAVOR=gvisor
      # Boot diagnostics key — generate with: openssl rand -hex 16
      - GRIST_BOOT_KEY=${GRIST_BOOT_KEY}
      # Optional: set default locale
      - GRIST_DEFAULT_LOCALE=en
    volumes:
      - grist_data:/persist
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:8484"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 15s

volumes:
  grist_data:

Create a .env file alongside:

# Admin email — this user gets owner access
GRIST_ADMIN_EMAIL=[email protected]

# Session secret — generate with: openssl rand -hex 32
GRIST_SESSION_SECRET=CHANGE_ME_TO_RANDOM_64_CHAR_HEX

# Boot key for /admin diagnostics — generate with: openssl rand -hex 16
GRIST_BOOT_KEY=CHANGE_ME_TO_RANDOM_32_CHAR_HEX

Start the stack:

docker compose up -d

Initial Setup

  1. Navigate to http://your-server:8484
  2. Sign in with the email address you set in GRIST_ADMIN_EMAIL
  3. You’ll land on your personal workspace — create your first document
  4. Access the admin panel at http://your-server:8484/admin using your boot key

Grist uses email-based authentication by default. The first user who signs in with the configured admin email gets full owner permissions.

Configuration

PostgreSQL Backend (Production)

For multi-user deployments, replace the built-in SQLite with PostgreSQL:

services:
  grist:
    image: gristlabs/grist:1.7.11
    container_name: grist
    restart: unless-stopped
    ports:
      - "8484:8484"
    environment:
      - GRIST_DEFAULT_EMAIL=${GRIST_ADMIN_EMAIL}
      - GRIST_SESSION_SECRET=${GRIST_SESSION_SECRET}
      - GRIST_FORCE_LOGIN=true
      - GRIST_SANDBOX_FLAVOR=gvisor
      - GRIST_BOOT_KEY=${GRIST_BOOT_KEY}
      - TYPEORM_TYPE=postgres
      - TYPEORM_HOST=grist-db
      - TYPEORM_PORT=5432
      - TYPEORM_DATABASE=grist
      - TYPEORM_USERNAME=grist
      - TYPEORM_PASSWORD=${DB_PASSWORD}
    volumes:
      - grist_data:/persist
    depends_on:
      grist-db:
        condition: service_healthy
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:8484"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 15s

  grist-db:
    image: postgres:16-alpine
    container_name: grist-db
    restart: unless-stopped
    environment:
      - POSTGRES_USER=grist
      - POSTGRES_PASSWORD=${DB_PASSWORD}
      - POSTGRES_DB=grist
    volumes:
      - grist_db:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U grist"]
      interval: 10s
      timeout: 5s
      retries: 5

volumes:
  grist_data:
  grist_db:

Key Environment Variables

VariablePurposeDefault
GRIST_DEFAULT_EMAILAdmin user email[email protected]
GRIST_FORCE_LOGINRequire authenticationfalse
GRIST_SESSION_SECRETCookie encryption keyAuto-generated
GRIST_SANDBOX_FLAVORPython formula sandboxingunsandboxed
GRIST_MAX_UPLOAD_IMPORT_MBMax import file size64
GRIST_MAX_UPLOAD_ATTACHMENT_MBMax attachment size64
GRIST_ORG_CREATION_ANYONEAllow any user to create orgsfalse

Advanced Configuration (Optional)

OIDC Authentication

Connect Grist to your existing SSO provider:

environment:
  - GRIST_OIDC_IDP_ISSUER=https://auth.example.com
  - GRIST_OIDC_IDP_CLIENT_ID=grist
  - GRIST_OIDC_IDP_CLIENT_SECRET=${OIDC_SECRET}
  - GRIST_OIDC_IDP_SCOPES=openid email profile

S3-Compatible Storage

Store documents in MinIO or S3 instead of the local filesystem:

environment:
  - GRIST_DOCS_S3_BUCKET=grist-docs
  - GRIST_DOCS_S3_ACCESS_KEY=${S3_ACCESS_KEY}
  - GRIST_DOCS_S3_SECRET_KEY=${S3_SECRET_KEY}
  - GRIST_DOCS_S3_ENDPOINT=http://minio:9000

Reverse Proxy

Grist runs on port 8484. Configure your reverse proxy to forward traffic:

Nginx Proxy Manager: Point your domain to http://grist:8484 with WebSocket support enabled.

For detailed setup, see Reverse Proxy Setup.

Backup

Back up the /persist volume, which contains all documents and configuration:

docker compose stop grist
tar -czf grist-backup-$(date +%Y%m%d).tar.gz /path/to/grist_data
docker compose start grist

If using PostgreSQL, also dump the database:

docker exec grist-db pg_dump -U grist grist > grist-db-backup.sql

For a comprehensive backup strategy, see Backup Strategy.

Troubleshooting

Documents Load Slowly

Symptom: Large documents take 10+ seconds to open. Fix: Grist loads the entire document into memory. Reduce document size by splitting large datasets across multiple documents, or increase container RAM.

Python Formulas Not Working

Symptom: Formula errors mentioning sandbox or Python. Fix: Ensure GRIST_SANDBOX_FLAVOR=gvisor is set. If running on ARM (Raspberry Pi), gvisor isn’t supported — use GRIST_SANDBOX_FLAVOR=unsandboxed and restrict document access to trusted users.

Permission Denied on /persist

Symptom: Container exits with permission errors. Fix: Grist runs as UID 1000 by default. Set ownership: chown -R 1000:1000 /path/to/grist_data.

Admin Panel Shows “Boot Key Required”

Symptom: Cannot access /admin endpoint. Fix: Set the GRIST_BOOT_KEY environment variable and append ?boot-key=YOUR_KEY to the admin URL.

Resource Requirements

  • RAM: 200 MB idle, 500 MB–1 GB under active use (scales with document size)
  • CPU: Low — 1 core sufficient for small teams, 2 cores recommended
  • Disk: 200 MB for the application, plus storage proportional to your documents

Verdict

Grist is the best self-hosted Airtable alternative for teams that need Python formula power and granular access controls. The database-spreadsheet hybrid model works well for structured data that needs computed views — project trackers, inventory systems, CRM-like workflows. It’s significantly more capable than a plain spreadsheet but lighter than a full low-code platform like NocoDB or Baserow. If you just need collaborative document editing, look at CryptPad or OnlyOffice instead.

Comments