Installing ThingsBoard PE using Docker (Windows)
This guide covers a single-node ThingsBoard Professional Edition (PE) installation using Docker Compose on Windows. By the end, you will have a fully functional ThingsBoard instance running on your machine. For cluster setup, see Cluster Setup with Docker Compose.
Prerequisites
Section titled “Prerequisites”Ensure your server meets the minimum requirements:
| Use case | CPU | RAM | Recommended services |
|---|---|---|---|
| Development / PoC | 1 core | 4 GB | ThingsBoard, PostgreSQL |
| Production (small) | 2 cores | 8 GB | ThingsBoard, PostgreSQL, Kafka |
| Production (recommended) | 4+ cores | 16+ GB | ThingsBoard, PostgreSQL, Kafka, Cassandra |
Install Docker: Docker Desktop for Windows
Step 1. Obtain the license key
Section titled “Step 1. Obtain the license key”We assume you have already chosen your subscription plan or decided to purchase a perpetual license. If not, navigate to the pricing page to select the best license option for your case. See How to get a pay-as-you-go subscription or How to get a perpetual license for details.
Step 2. Create docker compose file
Section titled “Step 2. Create docker compose file”Create a dedicated directory for your ThingsBoard installation and navigate to it. All subsequent commands in this guide should be run from this directory.
mkdir C:\thingsboardcd C:\thingsboardThingsBoard uses a message queue to route messages between its internal services. Select the option that matches your infrastructure:
- In Memory (default) — built-in queue, no extra setup required. Suitable for development and PoC. Not recommended for production or multi-node deployments.
- Kafka — high-throughput, durable queue. Run it yourself or use a managed service such as AWS MSK.
- Confluent Cloud — fully managed Kafka service. Use this if you want Kafka without managing the infrastructure yourself.
Create the docker-compose.yml file. Replace PUT_YOUR_LICENSE_SECRET_HERE with your license secret from Step 1:
notepad docker-compose.ymlPaste one of the configurations below, save, and exit. Or use the download button to save the file directly.
services: postgres: restart: always image: "postgres:18" ports: - "5432" environment: POSTGRES_DB: thingsboard POSTGRES_PASSWORD: postgres volumes: - postgres-data:/var/lib/postgresql healthcheck: test: ["CMD-SHELL", "pg_isready -U postgres -d thingsboard"] interval: 10s timeout: 5s retries: 5 start_period: 10s thingsboard-pe: restart: always image: "thingsboard/tb-pe-node:4.3.1.1PE" ports: - "8080:8080" - "1883:1883" - "8883:8883" - "9090:9090" - "7070:7070" - "5683-5688:5683-5688/udp" logging: driver: "json-file" options: max-size: "100m" max-file: "10" environment: TB_SERVICE_ID: tb-pe-node TB_LICENSE_SECRET: PUT_YOUR_LICENSE_SECRET_HERE TB_LICENSE_INSTANCE_DATA_FILE: /data/license.data REPORTS_SERVER_ENDPOINT_URL: http://tb-web-report:8383 SPRING_DATASOURCE_URL: jdbc:postgresql://postgres:5432/thingsboard SPRING_DATASOURCE_PASSWORD: postgres # Pre-configured for optional Trendz Analytics integration DEFAULT_TRENDZ_URL: http://trendz:8888 DEFAULT_TB_URL: http://thingsboard-pe:8080 volumes: - license-data:/data depends_on: postgres: condition: service_healthy tb-web-report: restart: always image: "thingsboard/tb-pe-web-report:4.3.1.1PE" ports: - "8383" depends_on: - thingsboard-pe environment: HTTP_BIND_ADDRESS: 0.0.0.0 HTTP_BIND_PORT: 8383 LOGGER_LEVEL: info LOG_FOLDER: logs LOGGER_FILENAME: tb-web-report-%DATE%.log DOCKER_MODE: true DEFAULT_PAGE_NAVIGATION_TIMEOUT: 120000 DASHBOARD_IDLE_WAIT_TIME: 3000 USE_NEW_PAGE_FOR_REPORT: truevolumes: postgres-data: name: tb-postgres-data driver: local license-data: name: tb-pe-license-data driver: localServices started:
postgres— PostgreSQL databasethingsboard-pe— ThingsBoard application nodetb-web-report— reporting component for PDF/PNG dashboard exports
This example runs Kafka locally as a Docker container. If you already have a Kafka broker or use a managed service (e.g. AWS MSK), remove the kafka service and update TB_KAFKA_SERVERS to point to your broker.
services: postgres: restart: always image: "postgres:18" ports: - "5432" environment: POSTGRES_DB: thingsboard POSTGRES_PASSWORD: postgres volumes: - postgres-data:/var/lib/postgresql healthcheck: test: ["CMD-SHELL", "pg_isready -U postgres -d thingsboard"] interval: 10s timeout: 5s retries: 5 start_period: 10s kafka: restart: always image: bitnamilegacy/kafka:4.0 ports: - 9092:9092 - 9093 environment: ALLOW_PLAINTEXT_LISTENER: "yes" KAFKA_CFG_LISTENERS: "PLAINTEXT://:9092,CONTROLLER://:9093" KAFKA_CFG_ADVERTISED_LISTENERS: "PLAINTEXT://:9092" KAFKA_CFG_LISTENER_SECURITY_PROTOCOL_MAP: "CONTROLLER:PLAINTEXT,PLAINTEXT:PLAINTEXT" KAFKA_CFG_INTER_BROKER_LISTENER_NAME: "PLAINTEXT" KAFKA_CFG_AUTO_CREATE_TOPICS_ENABLE: "false" KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: "1" KAFKA_TRANSACTION_STATE_LOG_MIN_ISR: "1" KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR: "1" KAFKA_CFG_PROCESS_ROLES: "controller,broker" KAFKA_CFG_NODE_ID: "0" KAFKA_CFG_CONTROLLER_LISTENER_NAMES: "CONTROLLER" KAFKA_CFG_CONTROLLER_QUORUM_VOTERS: "0@kafka:9093" KAFKA_CFG_LOG_RETENTION_MS: "300000" KAFKA_CFG_SEGMENT_BYTES: "26214400" volumes: - kafka-data:/bitnami thingsboard-pe: restart: always image: "thingsboard/tb-pe-node:4.3.1.1PE" ports: - "8080:8080" - "1883:1883" - "8883:8883" - "9090:9090" - "7070:7070" - "5683-5688:5683-5688/udp" logging: driver: "json-file" options: max-size: "100m" max-file: "10" environment: TB_SERVICE_ID: tb-pe-node TB_LICENSE_SECRET: PUT_YOUR_LICENSE_SECRET_HERE TB_LICENSE_INSTANCE_DATA_FILE: /data/license.data REPORTS_SERVER_ENDPOINT_URL: http://tb-web-report:8383 SPRING_DATASOURCE_URL: jdbc:postgresql://postgres:5432/thingsboard SPRING_DATASOURCE_PASSWORD: postgres # Pre-configured for optional Trendz Analytics integration DEFAULT_TRENDZ_URL: http://trendz:8888 DEFAULT_TB_URL: http://thingsboard-pe:8080 TB_QUEUE_TYPE: kafka TB_KAFKA_SERVERS: kafka:9092 volumes: - license-data:/data depends_on: postgres: condition: service_healthy kafka: condition: service_started tb-web-report: restart: always image: "thingsboard/tb-pe-web-report:4.3.1.1PE" ports: - "8383" depends_on: - thingsboard-pe environment: HTTP_BIND_ADDRESS: 0.0.0.0 HTTP_BIND_PORT: 8383 LOGGER_LEVEL: info LOG_FOLDER: logs LOGGER_FILENAME: tb-web-report-%DATE%.log DOCKER_MODE: true DEFAULT_PAGE_NAVIGATION_TIMEOUT: 120000 DASHBOARD_IDLE_WAIT_TIME: 3000 USE_NEW_PAGE_FOR_REPORT: truevolumes: postgres-data: name: tb-postgres-data driver: local license-data: name: tb-pe-license-data driver: local kafka-data: name: tb-pe-kafka-data driver: localServices started:
postgres— PostgreSQL databasekafka— Kafka broker (local, single-node)thingsboard-pe— ThingsBoard application nodetb-web-report— reporting component for PDF/PNG dashboard exports
First create a Confluent Cloud account, create a Kafka cluster, and obtain your API Key. Replace CLUSTER_API_KEY, CLUSTER_API_SECRET, and localhost:9092 with your Confluent Cloud values:
services: postgres: restart: always image: "postgres:18" ports: - "5432" environment: POSTGRES_DB: thingsboard POSTGRES_PASSWORD: postgres volumes: - postgres-data:/var/lib/postgresql healthcheck: test: ["CMD-SHELL", "pg_isready -U postgres -d thingsboard"] interval: 10s timeout: 5s retries: 5 start_period: 10s thingsboard-pe: restart: always image: "thingsboard/tb-pe-node:4.3.1.1PE" ports: - "8080:8080" - "1883:1883" - "8883:8883" - "9090:9090" - "7070:7070" - "5683-5688:5683-5688/udp" logging: driver: "json-file" options: max-size: "100m" max-file: "10" environment: TB_SERVICE_ID: tb-pe-node TB_LICENSE_SECRET: PUT_YOUR_LICENSE_SECRET_HERE TB_LICENSE_INSTANCE_DATA_FILE: /data/license.data REPORTS_SERVER_ENDPOINT_URL: http://tb-web-report:8383 SPRING_DATASOURCE_URL: jdbc:postgresql://postgres:5432/thingsboard SPRING_DATASOURCE_PASSWORD: postgres # Pre-configured for optional Trendz Analytics integration DEFAULT_TRENDZ_URL: http://trendz:8888 DEFAULT_TB_URL: http://thingsboard-pe:8080 TB_QUEUE_TYPE: kafka TB_KAFKA_SERVERS: localhost:9092 TB_QUEUE_KAFKA_REPLICATION_FACTOR: 3 TB_QUEUE_KAFKA_USE_CONFLUENT_CLOUD: true TB_QUEUE_KAFKA_CONFLUENT_SASL_JAAS_CONFIG: 'org.apache.kafka.common.security.plain.PlainLoginModule required username="CLUSTER_API_KEY" password="CLUSTER_API_SECRET";' TB_QUEUE_CORE_POLL_INTERVAL_MS: 1000 TB_QUEUE_CORE_PARTITIONS: 2 TB_QUEUE_RULE_ENGINE_POLL_INTERVAL_MS: 1000 TB_QUEUE_TRANSPORT_REQUEST_POLL_INTERVAL_MS: 1000 TB_QUEUE_TRANSPORT_RESPONSE_POLL_INTERVAL_MS: 1000 TB_QUEUE_TRANSPORT_NOTIFICATIONS_POLL_INTERVAL_MS: 1000 TB_QUEUE_VC_INTERVAL_MS: 1000 TB_QUEUE_VC_PARTITIONS: 1 volumes: - license-data:/data depends_on: postgres: condition: service_healthy tb-web-report: restart: always image: "thingsboard/tb-pe-web-report:4.3.1.1PE" ports: - "8383" depends_on: - thingsboard-pe environment: HTTP_BIND_ADDRESS: 0.0.0.0 HTTP_BIND_PORT: 8383 LOGGER_LEVEL: info LOG_FOLDER: logs LOGGER_FILENAME: tb-web-report-%DATE%.log DOCKER_MODE: true DEFAULT_PAGE_NAVIGATION_TIMEOUT: 120000 DASHBOARD_IDLE_WAIT_TIME: 3000 USE_NEW_PAGE_FOR_REPORT: truevolumes: postgres-data: name: tb-postgres-data driver: local license-data: name: tb-pe-license-data driver: localServices started:
postgres— PostgreSQL databasethingsboard-pe— ThingsBoard application nodetb-web-report— reporting component for PDF/PNG dashboard exports
You can update the default Rule Engine queue configuration using the UI. See Rule Engine Queues for details.
Docker compose parameters
Section titled “Docker compose parameters”Ports (host:container)
| Port mapping | Description |
|---|---|
8080:8080 | Web UI and REST API. The left value is the host port — change it if 8080 is already in use. |
1883:1883 | MQTT — plaintext IoT device connections |
8883:8883 | MQTT over TLS — encrypted IoT device connections |
5683:5683/udp | CoAP — plaintext IoT protocol |
5684:5684/udp | CoAP over DTLS — encrypted CoAP |
5685:5685/udp | LwM2M CoAP — plaintext Lightweight M2M |
5686:5686/udp | LwM2M CoAP over DTLS — encrypted LwM2M |
5687:5687/udp | LwM2M — plaintext Lightweight M2M (Bootstrap) |
5688:5688/udp | LwM2M over DTLS — encrypted Lightweight M2M (Bootstrap) |
7070:7070 | Edge RPC (gRPC) — connections from ThingsBoard Edge nodes |
9090:9090 | Remote Integration Executor (gRPC) — used by external integration services |
Environment variables
| Variable | Description |
|---|---|
POSTGRES_PASSWORD | Password for the PostgreSQL postgres user. Must match SPRING_DATASOURCE_PASSWORD. Change the default value in production. |
SPRING_DATASOURCE_URL | PostgreSQL JDBC connection URL. Specifies the host and database name. Default: jdbc:postgresql://postgres:5432/thingsboard. |
SPRING_DATASOURCE_USERNAME | PostgreSQL username ThingsBoard connects as. Default: postgres. |
SPRING_DATASOURCE_PASSWORD | PostgreSQL password ThingsBoard uses to connect. Must match POSTGRES_PASSWORD. |
TB_QUEUE_TYPE | Message queue type. Options: in-memory (default, single-node only), kafka, rabbitmq. |
TB_KAFKA_SERVERS | Kafka bootstrap servers. Required when TB_QUEUE_TYPE=kafka. Default: localhost:9092. |
TB_LICENSE_SECRET | ThingsBoard PE license secret key. |
DEFAULT_TRENDZ_URL | Trendz Analytics endpoint — pre-configured for optional Trendz integration. |
Volumes
| Volume | Description |
|---|---|
tb-postgres-data | Persists PostgreSQL data across container restarts and upgrades. |
tb-pe-kafka-data | Persists Kafka data. Only present when TB_QUEUE_TYPE=kafka. |
tb-pe-license-data | Persists license instance data — prevents license re-activation on each restart. |
For the full list of configuration parameters, see the Configuration Reference.
Step 3. Initialize database schema and system assets
Section titled “Step 3. Initialize database schema and system assets”Before starting ThingsBoard, initialize the database schema and load built-in assets. Choose the option that matches your goal:
- With demo data — also loads a sample tenant account, pre-built dashboards, and demo devices. Useful for exploring the platform before deploying to production.
- Clean install — initializes the database with system data only (rule chains, widget bundles, system dashboards).
docker compose run --rm -e INSTALL_TB=true -e LOAD_DEMO=true thingsboard-pedocker compose run --rm -e INSTALL_TB=true thingsboard-peThe container exits automatically once initialization is complete.
Step 4. Start the platform
Section titled “Step 4. Start the platform”Start all containers:
docker compose up -dMonitor the startup. The line confirming the platform is ready will be highlighted:
docker compose logs -f thingsboard-pePress Ctrl+C to detach from the log stream — containers will continue running in the background.
Open http://localhost:8080 in your browser. You should see the ThingsBoard login page. Use the following default credentials:
| Role | Password | With demo data | Clean install | |
|---|---|---|---|---|
| System Administrator | [email protected] | sysadmin | ✅ | ✅ |
| Tenant Administrator | [email protected] | tenant | ✅ | ❌ |
| Customer User | [email protected] | customer | ✅ | ❌ |
See Getting Started for your next steps after login.
Inspect logs and control containers
Section titled “Inspect logs and control containers”Stream the ThingsBoard container logs:
docker compose logs -f thingsboard-peStop all containers:
docker compose downStart all containers:
docker compose up -dInstall Trendz Analytics (optional)
Section titled “Install Trendz Analytics (optional)”You may optionally install Trendz Analytics at any time. The Trendz compose file extends the main docker-compose.yml — always run them together.
-
Create the Trendz database in PostgreSQL:
Terminal window docker compose -f docker-compose.yml exec -it postgres psql -U postgres -c "CREATE DATABASE trendz;" -
Create the
docker-compose-trendz.ymlfile with the following content:docker-compose-trendz.yml services:trendz:restart: alwaysimage: "thingsboard/trendz:1.15.1"ports:- "8888:8888"environment:TB_API_URL: http://thingsboard-pe:8080SPRING_DATASOURCE_URL: jdbc:postgresql://postgres:5432/trendzSPRING_DATASOURCE_USERNAME: postgresSPRING_DATASOURCE_PASSWORD: postgresSCRIPT_ENGINE_DOCKER_PROVIDER_URL: trendz-python-executor:8181SCRIPT_ENGINE_TIMEOUT: 30000volumes:- trendz-conf:/trendz-config-files- trendz-data:/datadepends_on:postgres:condition: service_healthytrendz-python-executor:restart: alwaysimage: "thingsboard/trendz-python-executor:1.15.1"ports:- "8181:8181"environment:EXECUTOR_MANAGER: 1EXECUTOR_SCRIPT_ENGINE: 6THROTTLING_QUEUE_CAPACITY: 10THROTTLING_THREAD_POOL_SIZE: 6NETWORK_BUFFER_SIZE: 5242880volumes:- trendz-python-executor-conf:/python-executor-config-files- trendz-python-executor-data:/datavolumes:trendz-conf:name: trendz-confdriver: localtrendz-data:name: trendz-datadriver: localtrendz-python-executor-conf:name: trendz-python-executor-confdriver: localtrendz-python-executor-data:name: trendz-python-executor-datadriver: local
Trendz docker compose parameters
Section titled “Trendz docker compose parameters”| Parameter | Description |
|---|---|
8888:8888 | Trendz HTTP port |
trendz-conf | Docker volume for Trendz configuration |
trendz-data | Docker volume for Trendz data |
trendz-python-executor-conf | Docker volume for Trendz Python executor configuration |
trendz-python-executor-data | Docker volume for Trendz Python executor data |
Start the platform with Trendz
Section titled “Start the platform with Trendz”Bring up all containers (including Trendz) as a single Compose project:
docker compose -f docker-compose.yml -f docker-compose-trendz.yml up -ddocker compose -f docker-compose.yml -f docker-compose-trendz.yml logs -f thingsboard-peInspect logs with Trendz
Section titled “Inspect logs with Trendz”Stream ThingsBoard logs:
docker compose -f docker-compose.yml -f docker-compose-trendz.yml logs -f thingsboard-peStream Trendz logs:
docker compose -f docker-compose.yml -f docker-compose-trendz.yml logs -f trendzStop all containers:
docker compose -f docker-compose.yml -f docker-compose-trendz.yml downStart all containers:
docker compose -f docker-compose.yml -f docker-compose-trendz.yml up -dUpgrading Trendz
Section titled “Upgrading Trendz”Trendz Analytics has a different version system and should be updated separately from the ThingsBoard platform. See Trendz Docker Upgrade.
Upgrading
Section titled “Upgrading”When a new ThingsBoard release becomes available, update your installation to benefit from the latest features and security patches.
See the Upgrade Instructions for detailed steps.
Troubleshooting
Section titled “Troubleshooting”DNS issues
Section titled “DNS issues”If you observe errors related to DNS issues, for example:
127.0.1.1:53: cannot unmarshal DNS messageConfigure your system to use Google public DNS servers.