← All articles
white and black stripe wall

ToolJet: Build Internal Tools Without Writing Full-Stack Code

Development 2026-03-04 · 3 min read tooljet low-code internal tools self-hosted open-source retool alternative dashboard
By Selfhosted Guides Editorial TeamSelf-hosting practitioners covering open source software, home lab infrastructure, and data sovereignty.

Internal tools — admin panels, inventory dashboards, support queues, customer lookup apps — usually fall into a frustrating category: too custom for off-the-shelf software, but not important enough to justify building from scratch. ToolJet exists to solve that problem.

Photo by Erik Mclean on Unsplash

It's an open-source, self-hosted low-code platform where you drag UI components onto a canvas, connect them to databases or APIs, and wire up actions with JavaScript. No backend code, no frontend build system, no deployment pipeline — just a working internal app.

How ToolJet Works

The development model:

  1. Connect data sources — PostgreSQL, MySQL, MongoDB, REST APIs, Google Sheets, Stripe, S3, and dozens more
  2. Build the UI — drag tables, forms, charts, buttons, and inputs onto a canvas
  3. Write queries — SQL or API requests that your components trigger
  4. Add logic — JavaScript for transformations, conditionals, and chaining queries
  5. Deploy — share a URL with your team; ToolJet handles auth

Everything runs inside your ToolJet instance. Credentials for databases and APIs stay on your server.

Docker Deployment

The quickest path is Docker Compose:

# Download the official compose file
curl -LO https://tooljet-deployments.s3.us-west-1.amazonaws.com/docker/docker-compose.yaml

# Configure environment
cp .env.example .env
# Edit .env: set TOOLJET_HOST, SECRET_KEY_BASE, LOCKBOX_MASTER_KEY
# Generate values: openssl rand -hex 32

docker compose up -d

ToolJet needs a PostgreSQL database. The Compose file includes one, or you can point PG_HOST at an existing Postgres instance.

For production:

services:
  tooljet-server:
    image: tooljet/tooljet:latest
    environment:
      TOOLJET_HOST: https://tools.yourdomain.com
      SECRET_KEY_BASE: ${SECRET_KEY_BASE}
      LOCKBOX_MASTER_KEY: ${LOCKBOX_MASTER_KEY}
      PG_HOST: postgres
      PG_DB: tooljet_production
      PG_USER: tooljet
      PG_PASS: ${PG_PASSWORD}
    depends_on:
      - postgres
    restart: unless-stopped

  tooljet-client:
    image: tooljet/tooljet:latest
    command: ["npm", "run", "serve:client"]
    ports:
      - "80:8082"
    restart: unless-stopped

  postgres:
    image: postgres:15
    environment:
      POSTGRES_DB: tooljet_production
      POSTGRES_USER: tooljet
      POSTGRES_PASSWORD: ${PG_PASSWORD}
    volumes:
      - pg_data:/var/lib/postgresql/data
    restart: unless-stopped

volumes:
  pg_data:

Connecting Data Sources

After setup, go to Data SourcesAdd new. For PostgreSQL:

  1. Select PostgreSQL
  2. Enter host, port, database, username, password
  3. Click Test connection
  4. Save

ToolJet stores credentials encrypted using your LOCKBOX_MASTER_KEY. You can also use SSH tunnels for databases not exposed to the internet.

Supported natively:

Building an App: Customer Lookup

A practical example: a support team needs to look up customers by email and see their recent orders.

Step 1: Add a query

In the Query Editor, add a PostgreSQL query named getCustomer:

SELECT id, name, email, plan, created_at
FROM customers
WHERE email = {{textinput1.value}}
LIMIT 1

Add another query getOrders:

SELECT order_id, product, amount, status, created_at
FROM orders
WHERE customer_id = {{queries.getCustomer.data[0].id}}
ORDER BY created_at DESC
LIMIT 20

Step 2: Build the UI

Drag onto the canvas:

Step 3: Wire it up

The {{}} syntax is JavaScript — you can use any JS expression to transform data before it reaches a component.

JavaScript Transformations

Between queries and UI, you can transform data with JavaScript:

// In a query's "Transform" tab:
// Calculate days since signup
return data.map(customer => ({
  ...customer,
  days_since_signup: Math.floor(
    (new Date() - new Date(customer.created_at)) / (1000 * 60 * 60 * 24)
  )
}));

This runs client-side — no additional server code needed.

Access Control

ToolJet has workspace-level roles (Admin, Developer, Viewer) and app-level permissions. Viewers can use apps but not edit them. Apps can also be shared via a public URL (with or without authentication required).

For more granular control, ToolJet Enterprise adds SSO and group-based permissions.

ToolJet vs. Retool

Feature ToolJet Retool
Self-hostable ✓ (paid)
Open source ✓ (AGPL)
Pricing (cloud) Free tier $10/user/month
Data sources 40+ 60+
Component library Good Excellent
Mobile support Beta

Retool has a more polished component library and better mobile support. ToolJet wins on cost (free, self-hostable) and the fact that the source code is auditable.

When to Use ToolJet

ToolJet is a good fit when:

Skip it for customer-facing products (it's optimized for internal use) or when you need custom components that go beyond the provided widget library.

Get free weekly tips in your inbox. Subscribe to Self-Hosted Weekly