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=admin@example.com

# 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 emailyou@example.com
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.

Frequently Asked Questions

What makes Grist different from NocoDB or Baserow?

Grist supports Python formulas — real Python, not a formula language. This gives you far more computational power than NocoDB or Baserow. Grist also has granular row-level and column-level access control, making it better for multi-team use cases.

Can Grist replace Google Sheets?

For structured data with formulas and sharing, yes. Grist handles formulas (via Python), sorting, filtering, charts, and multi-user access. It lacks real-time cell-by-cell collaboration (changes sync, but not keystroke-by-keystroke like Google Sheets).

Does Grist support importing spreadsheets?

Yes. Grist imports CSV, Excel (.xlsx), and JSON files. It auto-detects column types during import. You can also import from Google Sheets by exporting as CSV first.

Is Grist resource-heavy?

No. Grist uses about 200 MB idle and runs fine on a 1-core VPS with 1 GB RAM for small teams. Document size is the primary scaling factor — very large documents with millions of rows may need more memory.

Can I use Grist as a database backend for an app?

Grist has a REST API that lets you read and write data programmatically. While not a production database, it works well as a lightweight backend for internal tools, dashboards, and prototypes.

Does Grist support self-hosted SSO?

Yes. Grist supports SAML-based SSO and can integrate with OIDC providers like Keycloak and Authentik via a SAML bridge. Configure via the GRIST_SAML_* environment variables.

Comments