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
- Open
http://your-server-ipin your browser - Create your admin account with email and password
- You’ll land on the dashboard — create your first app with Create new app
- Add a data source under Data Sources (e.g., your PostgreSQL, MySQL, or REST API)
- Start building by dragging components from the right panel onto the canvas
Configuration
| Setting | Environment Variable | Default | Notes |
|---|---|---|---|
| Public URL | TOOLJET_HOST | http://localhost | Must match your domain for OAuth and SSO |
| Disable signups | DISABLE_SIGNUPS | false | Restrict to invited users only |
| Session timeout | USER_SESSION_EXPIRY | 14400 min (10 days) | In minutes |
| Multiplayer editing | ENABLE_MULTIPLAYER_EDITING | true | Real-time collaboration on apps |
| SMTP sender | DEFAULT_FROM_EMAIL | [email protected] | Change to your domain |
| API payload limit | MAX_JSON_SIZE | 50mb | Max import/request body size |
| UI language | LANGUAGE | en | Interface 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
| Resource | Minimum | Recommended |
|---|---|---|
| RAM | 2 GB | 4 GB |
| CPU | 1 core | 2 cores |
| Disk | 5 GB | 20 GB |
| Architecture | x86_64 only | No 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.
Related
Get self-hosting tips in your inbox
Get the Docker Compose configs, hardware picks, and setup shortcuts we don't put in articles. Weekly. No spam.
Comments