Skip to content
Try for free

ThingsBoard Cloud

Choose your data region

Your data stays in the region you choose, for residency and compliance. No credit card required.

Rather run it yourself? Install on your own servers

Migrating from the legacy all-in-one tb-pe image

Older ThingsBoard PE Docker deployments run a single all-in-one container image (thingsboard/tb-pe) that bundles the core platform and the report engine together. Current installations and the version upgrade steps assume the split layout — thingsboard/tb-pe-node (core platform) and thingsboard/tb-pe-web-report (report engine) as two separate services.

thingsboard/tb-pe was published up to 4.2.1PE (2025-10-14) and stopped there, while thingsboard/tb-pe-node continued through 4.2.1.1PE, 4.2.2.x, and all of 4.3.x. You need this guide if your docker-compose.yml still references thingsboard/tb-pe:<current-tag> as a single service, with no separate report container:

Full example of the old docker-compose.yml
services:
mytbpe:
restart: always
image: "thingsboard/tb-pe:4.2.0PE"
ports:
- "8080:8080"
- "1883:1883"
- "8883:8883"
environment:
TB_QUEUE_TYPE: in-memory
SPRING_DATASOURCE_URL: jdbc:postgresql://postgres:5432/thingsboard
TB_LICENSE_SECRET: PUT_YOUR_LICENSE_SECRET_HERE
TB_LICENSE_INSTANCE_DATA_FILE: /data/license.data
volumes:
- /path/to/thingsboard/.mytbpe-data:/data
- /path/to/thingsboard/.mytbpe-logs:/var/log/thingsboard
postgres:
restart: always
image: "postgres:15"
ports:
- "5432"
environment:
POSTGRES_DB: thingsboard
POSTGRES_PASSWORD: postgres
volumes:
- /path/to/thingsboard/.mytbpe-data/db:/var/lib/postgresql/data

Switch to the split layout before starting any version upgrade — the per-version steps don’t apply to the all-in-one image as-is.

  1. Note your current PE version tag (the tag after thingsboard/tb-pe:) — you’ll reuse it for both new services below without changing the version yet.

  2. Change your existing service’s image from thingsboard/tb-pe:<current-tag> to thingsboard/tb-pe-node:<current-tag>.

  3. Add REPORTS_SERVER_ENDPOINT_URL: http://tb-web-report:8383 to that service’s environment: block. Keep all your other existing environment variables (license, MQTT/CoAP/LWM2M settings, etc.) unchanged.

  4. Add a new tb-web-report service under services:. This is the minimum working definition — the reference compose file in the Docker installation guide additionally sets logging and report timeout options you can copy across if you want to match it exactly:

    tb-web-report:
    restart: always
    image: "thingsboard/tb-pe-web-report:<current-tag>"
    depends_on:
    - <your-thingsboard-service-name>
    environment:
    HTTP_BIND_ADDRESS: 0.0.0.0
    HTTP_BIND_PORT: 8383
    DOCKER_MODE: true
  5. Pull the new images and restart:

    Terminal window
    docker pull thingsboard/tb-pe-node:<current-tag>
    docker pull thingsboard/tb-pe-web-report:<current-tag>
    docker compose up -d
  6. Confirm the platform comes up cleanly on your current version with the new two-container layout — same data, same license, same dashboards and devices — before proceeding to the version upgrade steps.

Optional: moving from local folder bind mounts to Docker named volumes

Section titled “Optional: moving from local folder bind mounts to Docker named volumes”

The steps above only require touching the image and adding the report service — your existing volumes stay as-is. If you’d also like to match the current reference compose file’s use of Docker named volumes instead of local folder bind mounts, do this as a separate step, after confirming the image-split switch works:

  1. Stop the stack before copying any data — copying a live PostgreSQL data directory can produce a corrupt snapshot:

    Terminal window
    docker compose stop
  2. Create named volumes for your database and license data:

    Terminal window
    docker volume create --name tb-postgres-data
    docker volume create --name tb-pe-license-data
  3. Copy your existing bind-mounted data into the new volumes (adjust the source paths to match your current docker-compose.yml; 799 is the user ID of the ThingsBoard non-root Docker user):

    Terminal window
    docker run --rm -v /path/to/thingsboard/.mytbpe-data/db:/source -v tb-postgres-data:/destination alpine sh -c "cp -a /source/. /destination/"
    docker run --rm -v /path/to/thingsboard/.mytbpe-data:/source -v tb-pe-license-data:/destination alpine sh -c "cp -a /source/license.data /destination/ && chown 799:799 /destination/license.data"

    Your /var/log/thingsboard (or .mytbpe-logs) bind mount doesn’t need this treatment — leave it as a local folder mount, or drop it entirely and rely on the Docker logging driver, as the reference compose file does.

  4. Update your docker-compose.yml: reference the named volumes on the services (postgres-data:/var/lib/postgresql/data on postgres, license-data:/data on the ThingsBoard node), and declare them at the top level with pinned names so Compose reuses the volumes created above instead of creating new project-prefixed ones:

    volumes:
    postgres-data:
    name: tb-postgres-data
    driver: local
    license-data:
    name: tb-pe-license-data
    driver: local
  5. Restart:

    Terminal window
    docker compose up -d