Self-Hosting ToolJet with Docker Compose

Want to build internal dashboards, admin panels, and CRUD apps without writing a full-stack application from scratch? ToolJet is an open-source low-code platform that connects to your databases, APIs, and third-party services — then lets you drag-and-drop a UI on top. Think Retool, but self-hosted and free.

What Is ToolJet?

ToolJet is an open-source low-code platform for building internal tools. It provides a visual app builder with 45+ pre-built UI components (tables, charts, forms, modals), connectors to 50+ data sources (PostgreSQL, MySQL, MongoDB, REST APIs, GraphQL, Google Sheets, Stripe), and a built-in database (ToolJet Database powered by PostgREST). You build apps by connecting data sources, writing queries, and binding results to UI components — no frontend framework needed.

Prerequisites

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

Docker Compose Configuration

Create a docker-compose.yml:

services:
  tooljet:
    image: tooljet/tooljet-ce:v3.20.122-lts
    container_name: tooljet
    restart: unless-stopped
    ports:
      - "80:80"
    environment:
      SERVE_CLIENT: "true"
      PORT: "80"
      TOOLJET_HOST: "http://localhost"           # Change to your domain
      LOCKBOX_MASTER_KEY: "${LOCKBOX_MASTER_KEY}"
      SECRET_KEY_BASE: "${SECRET_KEY_BASE}"
      PG_HOST: postgresql
      PG_DB: tooljet_production
      PG_USER: postgres
      PG_PASS: "${PG_PASS}"
      PG_PORT: "5432"
      TOOLJET_DB: tooljet_db
      TOOLJET_DB_HOST: postgresql
      TOOLJET_DB_USER: postgres
      TOOLJET_DB_PASS: "${PG_PASS}"
      TOOLJET_DB_PORT: "5432"
      PGRST_JWT_SECRET: "${PGRST_JWT_SECRET}"
      PGRST_HOST: "localhost"                    # PostgREST runs inside the container
      PGRST_DB_URI: "postgres://postgres:${PG_PASS}@postgresql/tooljet_db"
      PGRST_LOG_LEVEL: "info"
      PGRST_DB_PRE_CONFIG: "postgrest.pre_config"
      DEPLOYMENT_PLATFORM: "docker"
      CHECK_FOR_UPDATES: "true"
    depends_on:
      postgresql:
        condition: service_healthy
    networks:
      - tooljet
    platform: linux/amd64

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

volumes:
  tooljet-postgres:

networks:
  tooljet:

Create a .env file alongside your docker-compose.yml:

# Database password — CHANGE THIS
PG_PASS=change-this-strong-password

# Encryption key for datasource credentials (32-byte hex)
# Generate with: openssl rand -hex 32
LOCKBOX_MASTER_KEY=change-me-run-openssl-rand-hex-32

# Session cookie encryption (64-byte hex)
# Generate with: openssl rand -hex 64
SECRET_KEY_BASE=change-me-run-openssl-rand-hex-64

# JWT secret for ToolJet Database (32-byte hex)
# Generate with: openssl rand -hex 32
PGRST_JWT_SECRET=change-me-run-openssl-rand-hex-32

Generate the secrets and start:

# Generate secrets
sed -i "s/change-me-run-openssl-rand-hex-32/$(openssl rand -hex 32)/" .env
sed -i "s/change-me-run-openssl-rand-hex-64/$(openssl rand -hex 64)/" .env
sed -i "s/change-this-strong-password/$(openssl rand -base64 16)/" .env

docker compose up -d

Initial Setup

  1. Open http://your-server-ip in your browser
  2. Create your admin account with email and password
  3. You’ll land on the dashboard — create your first app with Create new app
  4. Add a data source under Data Sources (e.g., your PostgreSQL, MySQL, or REST API)
  5. Start building by dragging components from the right panel onto the canvas

Configuration

SettingEnvironment VariableDefaultNotes
Public URLTOOLJET_HOSThttp://localhostMust match your domain for OAuth and SSO
Disable signupsDISABLE_SIGNUPSfalseRestrict to invited users only
Session timeoutUSER_SESSION_EXPIRY14400 min (10 days)In minutes
Multiplayer editingENABLE_MULTIPLAYER_EDITINGtrueReal-time collaboration on apps
SMTP senderDEFAULT_FROM_EMAIL[email protected]Change to your domain
API payload limitMAX_JSON_SIZE50mbMax import/request body size
UI languageLANGUAGEenInterface language

SMTP Configuration

For email invitations and password resets:

SMTP_DOMAIN=smtp.yourdomain.com
SMTP_PORT=587
SMTP_USERNAME=your-smtp-user
SMTP_PASSWORD=your-smtp-password
DEFAULT_FROM_EMAIL=[email protected]

Advanced Configuration

Adding Redis for Workflows

ToolJet workflows (scheduled jobs, background tasks) require Redis. Add this to your docker-compose.yml:

  redis:
    image: redis:7-alpine
    container_name: tooljet-redis
    restart: unless-stopped
    volumes:
      - tooljet-redis:/data
    networks:
      - tooljet

Then add Redis environment variables to the ToolJet service:

REDIS_HOST=redis
REDIS_PORT=6379

Add tooljet-redis: under the volumes: section.

Running Dedicated Workers

For production deployments with heavy workflow usage, run a separate worker container:

  tooljet-worker:
    image: tooljet/tooljet-ce:v3.20.122-lts
    container_name: tooljet-worker
    restart: unless-stopped
    environment:
      WORKER: "true"
      TOOLJET_WORKFLOW_CONCURRENCY: "5"
      # ... same env vars as main tooljet service
    depends_on:
      - postgresql
      - redis
    networks:
      - tooljet
    platform: linux/amd64

Reverse Proxy

ToolJet serves on port 80 by default. For Nginx Proxy Manager or Caddy, proxy to http://tooljet:80.

Set TOOLJET_HOST to your full public URL (e.g., https://tooljet.yourdomain.com) for OAuth callbacks and link generation to work. See Reverse Proxy Setup.

Backup

Back up the PostgreSQL database — it contains all app definitions, queries, data source configs, and ToolJet Database content:

docker exec tooljet-db pg_dump -U postgres tooljet_production > tooljet-backup.sql
docker exec tooljet-db pg_dump -U postgres tooljet_db > tooljet-db-backup.sql

See Backup Strategy for automated approaches.

Troubleshooting

”Internal server error” on first load

Symptom: ToolJet shows a 500 error on the first request. Fix: PostgreSQL may not have finished initializing. Wait 30 seconds and refresh. Check logs: docker logs tooljet. If LOCKBOX_MASTER_KEY or SECRET_KEY_BASE is empty, the app crashes — verify your .env has all keys populated.

Data source connections fail

Symptom: You can add a data source but queries return connection errors. Fix: If connecting to a database on the host machine, use host.docker.internal (Docker Desktop) or the host’s IP (Linux). For databases in other Docker containers, put them on the same Docker network as ToolJet.

ToolJet Database queries return errors

Symptom: The built-in ToolJet Database (PostgREST) shows errors when creating tables or querying. Fix: Verify PGRST_DB_URI points to the correct database (tooljet_db, not tooljet_production). Ensure the tooljet_db database was created by PostgreSQL — you may need to create it manually: docker exec tooljet-db createdb -U postgres tooljet_db.

App loads but components don’t render

Symptom: The app builder opens but the canvas is blank or components are invisible. Fix: Clear your browser cache. ToolJet CE only supports linux/amd64 — if you’re on ARM (Apple Silicon, Raspberry Pi), the containers will fail silently. Check docker logs tooljet for architecture errors.

Resource Requirements

ResourceMinimumRecommended
RAM2 GB4 GB
CPU1 core2 cores
Disk5 GB20 GB
Architecturex86_64 onlyNo ARM support

ToolJet is moderate in resource usage — the main container plus PostgreSQL runs comfortably on 2 GB RAM. Adding Redis and worker containers increases the footprint to about 3-4 GB.

Verdict

For building internal tools and admin panels, ToolJet wins on flexibility. It connects to more data sources than any competitor in this space, the visual builder is polished, and the ToolJet Database feature means you can prototype without setting up a separate backend. If you’re choosing between ToolJet and Appsmith, ToolJet edges ahead on data source variety and multiplayer editing. Appsmith is simpler for single-developer use. See the Appsmith vs ToolJet comparison for a detailed breakdown.

FAQ

Is ToolJet Community Edition really free?

Yes. ToolJet CE (tooljet/tooljet-ce) is AGPL-3.0 licensed with no feature restrictions for self-hosting. The Enterprise Edition adds SSO, audit logs, granular permissions, and priority support.

Can ToolJet connect to REST APIs?

Yes. ToolJet supports REST API, GraphQL, gRPC, and WebSocket data sources. You can chain queries, use JavaScript transformations on responses, and bind results to UI components — making it a strong option for building API dashboards.

Does ToolJet support mobile-responsive apps?

ToolJet includes a mobile layout editor, but it’s primarily designed for desktop internal tools. The responsive features work for simple layouts but complex apps may need manual mobile optimization.

How do I upgrade ToolJet?

Update the image tag in your docker-compose.yml to the new version, then run docker compose pull && docker compose up -d. ToolJet handles database migrations automatically on startup. Back up your PostgreSQL database before upgrading.

Comments