Skip to content

Document migration from legacy all-in-one tb-pe Docker image - #616

Open
mykhailokornieiev wants to merge 2 commits into
thingsboard:mainfrom
mykhailokornieiev:pe_docker_legacy_image_migration
Open

Document migration from legacy all-in-one tb-pe Docker image#616
mykhailokornieiev wants to merge 2 commits into
thingsboard:mainfrom
mykhailokornieiev:pe_docker_legacy_image_migration

Conversation

@mykhailokornieiev

Copy link
Copy Markdown
Contributor

Summary

  • Add a new PE-only page documenting how to migrate a ThingsBoard PE Docker deployment off the legacy all-in-one thingsboard/tb-pe image onto the current tb-pe-node + tb-pe-web-report layout, including an optional section for moving from local folder bind mounts to Docker named volumes.
  • Add a "Still running the legacy all-in-one tb-pe image?" reminder banner to the per-family Docker upgrade instruction pages (v3.0.x–v4.2.x only; hidden from v4.3.x onward, since it's no longer relevant there), linking to the new migration page.

v4.3:
image

<= v4.2:
image

New page:

image image image image

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds PE documentation to help users migrate off the legacy all-in-one thingsboard/tb-pe Docker image and surfaces a contextual warning on older Docker upgrade-instruction pages so users don’t follow incompatible upgrade steps.

Changes:

  • Added a new PE-only migration page describing how to switch from thingsboard/tb-pe to the split tb-pe-node + tb-pe-web-report layout (plus an optional bind-mount → named-volume migration).
  • Added a version-gated caution banner on PE Docker per-family upgrade instruction pages (shown for v3.0.x–v4.2.x only) linking to the new migration page.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
src/pages/docs/pe/installation/upgrade-instructions/[platform]/[familySlug].astro Adds a Docker-only, family-gated caution <Aside> linking to the legacy-image migration page.
src/content/docs/docs/pe/installation/upgrade-instructions/docker-legacy-image-migration.mdx New PE documentation page describing the legacy-image → split-services migration workflow.
Suppressed comments (3)

src/content/docs/docs/pe/installation/upgrade-instructions/docker-legacy-image-migration.mdx:20

  • The steps currently suggest a <current-version>PE tag, but step 1 says to reuse the tag after thingsboard/tb-pe: (which already includes the PE suffix). As written, this would lead readers to produce tags like 4.2.0PEPE.
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-version>PE` to `thingsboard/tb-pe-node:<current-version>PE`.
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.

src/content/docs/docs/pe/installation/upgrade-instructions/docker-legacy-image-migration.mdx:40

  • These pull commands repeat the same <current-version>PE tag issue described above; it should reference the exact existing tag the user is currently running (e.g. 4.2.2.3PE).
<Code code={`docker pull thingsboard/tb-pe-node:<current-version>PE
docker pull thingsboard/tb-pe-web-report:<current-version>PE

src/content/docs/docs/pe/installation/upgrade-instructions/docker-legacy-image-migration.mdx:25

  • This compose snippet repeats the same <current-version>PE issue as the numbered steps: if <current-version> already includes the PE suffix, appending PE again produces an invalid tag.
  image: "thingsboard/tb-pe-web-report:<current-version>PE"

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.


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. That image also predates arm64 support. 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.

If your `docker-compose.yml` still references `thingsboard/tb-pe:<version>` as a single service (no separate report container), 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.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed

@vvlladd28 vvlladd28 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review summary

Reviewed 2 changed files in Document migration from legacy all-in-one tb-pe Docker image at commit 29ee31b. Left 13 inline comments.

The fix commit only renamed the version placeholder (<current-version><current-tag>), so everything below is still open — and that rename introduced a new inconsistency of its own (commented at step 2).

The main points:

  1. The gate behaves correctly, but the number in it isn't the fact it encodes. tb-pe and tb-pe-node diverged at 4.2.1PE (2025-10-14): the all-in-one image stopped there, while tb-pe-node carried on with 4.2.1.1PE, 4.2.2PE, 4.2.2.3PE and all of 4.3.x. So the real cut-off is 4.2.1 — a patch-level version inside the 4.2 family — while the code writes 4.3. The two agree only because rounding a mid-family cut-off up to family granularity happens to land on the same set (a side effect being that the 4.2 page shows the banner to readers on 4.2.2.x, who could never have run the all-in-one image — harmless, since the notice is conditional prose). But none of this is recorded anywhere: no comment, and no marker on UPGRADE_VERSIONS, which is where the codebase already keeps facts of exactly this shape (lts, windowsZip, patchableFrom). A named constant (LAST_ALL_IN_ONE_TAG = '4.2.1') plus one line of explanation would make it checkable.
  2. The optional named-volumes section has two ways to lose the database. The volume mount target doesn't line up with where PGDATA actually is for a reader who — correctly, per the caution immediately above — keeps their existing Postgres image. And a named volume referenced without a top-level volumes: entry either fails outright or gets project-prefixed, in which case Postgres quietly starts on an empty directory and the data copied in step 2 is orphaned. Nothing tells the reader to stop the stack before copying either.

The rest are maintainability and consistency points: the notice never appears on the aggregate /upgrade-instructions/docker/ page that renders the same steps; the new page is reachable only through the banner and never states the version range in which the image existed; and the page diverges from the conventions established in its sibling files (<Steps>, fenced code blocks, frontmatter).


This review was auto-generated. Findings may contain errors — please verify before applying changes.

'docker-compose': 'Docker Compose',
};

function isLegacyImageEligible(family: string): boolean {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The gate behaves correctly, but the number in it isn't the fact it's standing in for.

thingsboard/tb-pe and thingsboard/tb-pe-node diverged at 4.2.1PE — both got that tag on 2025-10-14, then the all-in-one image stopped while tb-pe-node carried on (4.2.1.1PE Dec 2025, 4.2.2PE Mar 2026, 4.2.2.3PE Jun 2026, plus all of 4.3.x). So the real cut-off is 4.2.1, a patch-level version inside the 4.2 family, and the code writes 4.3. Those agree today only because rounding a mid-family cut-off up to family granularity happens to land on the same set — which is fine in effect (the banner is conditional prose, so the 4.2 page over-including readers on 4.2.2.x costs nothing), but it means the literal actively obscures what's being encoded.

Nothing in the repo records any of this: no comment here, and no marker on UPGRADE_VERSIONS, which is where the codebase already keeps facts of exactly this shape (lts, upgrade, windowsZip, patchableFrom). Whoever adds 4.4 has no way to check whether the cut-off still holds, and since 4.3 is also the newest entry in UPGRADE_FAMILIES today, the predicate is indistinguishable from "every family except the current one". A named constant — const LAST_ALL_IN_ONE_TAG = '4.2.1'; — with a one-line comment, or a flag on the version entries, would make it checkable.

Naming. "Eligible" for what? Read cold this sounds like a capability check; what it actually decides is whether to render a notice. showsLegacyImageNotice(family) or mayRunAllInOneImage(family) would say which.

Third copy of the same comparator. DockerUpgradeSteps.astro already has isNewerVersionScheme (>= 4.2) and needsCompatibilityCheck (>= 4.3) with this identical split('.') + two-branch shape, and this one inverts it. A single compareFamilies / familyAtLeast(family, '4.3') in src/models/upgrade-shared.ts (which already has a private compareVersions) would collapse all three to one-liners against a named threshold and keep the parsing edge cases in one place.

<StarlightPage frontmatter={{ title, description, tableOfContents: true }} headings={headings}>
<p><a href={backUrl}>&larr; Back to all versions</a></p>

{platform === 'docker' && isLegacyImageEligible(family) && (

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the per-family route the right host for this? Two things point away from it.

The condition has to re-check platform === 'docker' even though DockerUpgradeSteps right below already owns every Docker-specific concern, including its own version thresholds and its own inline asides.

More importantly, /docs/pe/installation/upgrade-instructions/docker/ renders the exact same 3.0–4.3 steps through _includes/docs/installation/upgrade-instructions/docker.mdxDockerUpgradeSteps with no family filter, and gets no warning at all. Whether the reader sees the notice depends on which of two published routes they landed on. Moving the aside into DockerUpgradeSteps.astro — guarded on isPE and the family it's already iterating — would cover both entry points, drop the platform check here, and put the notice next to the version it applies to.

<p><a href={backUrl}>&larr; Back to all versions</a></p>

{platform === 'docker' && isLegacyImageEligible(family) && (
<Aside type="caution" title="Still running the legacy all-in-one tb-pe image?">

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This page will now stack two different aside implementations: the real Starlight <Aside> from here, then the hand-rolled <aside class="starlight-aside starlight-aside--caution"> markup with locally scoped <style> that DockerUpgradeSteps.astro emits. They're meant to look the same, but nothing keeps them in sync — the component's copy has no icon and approximates the spacing tokens, so the two caution boxes will likely already read as slightly different, and will drift further if Starlight's aside markup changes. Using the same mechanism as the component below (or better, having the component adopt <Aside>) would leave one aside in the codebase.

If your compose file uses a single <code>thingsboard/tb-pe</code> service instead of
separate <code>thingsboard/tb-pe-node</code> and <code>thingsboard/tb-pe-web-report</code> services,
the steps below don't apply as-is. See{' '}
<DocLink product={product} path="installation/upgrade-instructions/docker-legacy-image-migration">

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This banner is currently the only route to the new page — it isn't in astro.sidebar.ts, isn't linked from the PE installation/upgrade-instructions index (which does link upgrade-from-ce in prose), and isn't linked from the aggregate docker page. Compare docs/trendz/installation/old-docker-migrate, the closest precedent in the repo, which is a sidebar entry (astro.sidebar.ts:4219). A pointer on the upgrade-instructions index would make it findable independently of the gating, and would keep the page from being orphaned if the gating later changes.

import WindowsUpgradeSteps from '~/components/upgrade-instructions/WindowsUpgradeSteps.astro';
import DockerUpgradeSteps from '~/components/upgrade-instructions/DockerUpgradeSteps.astro';
import DockerComposeUpgradeSteps from '~/components/upgrade-instructions/DockerComposeUpgradeSteps.astro';
import { Aside } from '@astrojs/starlight/components';

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not created by this PR, but worth flagging while we're here: this route and src/pages/docs/installation/upgrade-instructions/[platform]/[familySlug].astro are near-identical twins that previously differed only by Products.CE/Products.PE and a few strings. This adds ~15 lines to only one of them, widening the divergence. If a shared component (or a createUpgradeFamilyPage(product) helper exporting getStaticPaths plus the body) is out of scope here, even a short comment on both files noting they're intentional twins would warn the next editor.


<Code code={`tb-web-report:
restart: always
image: "thingsboard/tb-pe-web-report:<current-tag>PE"

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a hand-trimmed copy of the canonical tb-web-report service in _includes/docs/installation/docker.mdx:226, dropping ports, the LOGGER_* keys, DEFAULT_PAGE_NAVIGATION_TIMEOUT, DASHBOARD_IDLE_WAIT_TIME and USE_NEW_PAGE_FOR_REPORT. A minimal example is defensible, but is USE_NEW_PAGE_FOR_REPORT: true in particular safe to omit? The migrated deployment ends up with different report-generation behaviour from a fresh install that follows the reference compose, which is a subtle way for "same data, same dashboards" (step 6) to not quite hold.

Even if the omissions are all fine, this is now a second definition of that service that nobody will think to update when the reference gains or renames a key. Either link the Docker installation guide for the full definition ("keeping your current version tag"), or add a note saying it's a deliberate minimal subset.

Port 8383 is called only by the ThingsBoard node container over the Docker Compose network — it does not need to be published to a host port or reachable from a public IP.
</Aside>

5. Pull the new images and restart:

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The procedure is a flat markdown ordered list with code blocks and asides at column zero between the items, so steps 1–4 and 5–6 (and 1–2 / 3 in the optional section) compile to separate <ol>s rather than one continuous list, and the commands aren't visually attached to the step that owns them. <Steps> with the <Code>/aside indented inside the list item is the established pattern — _includes/docs/installation/docker.mdx and the closest precedent for this whole page, docs/trendz/installation/old-docker-migrate.mdx, both use it.


2. Copy your existing bind-mounted data into the new volumes (adjust the source paths to match your current `docker-compose.yml`):

<Code code={`docker run --rm -v /path/to/thingsboard/.mytbpe-data/db:/source -v tb-postgres-data:/destination alpine sh -c "cp -rp /source/* /destination/"

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two problems with the copy step.

Nothing tells the reader to stop the stack first. Copying a live PostgreSQL data directory yields a torn snapshot — the files are being written while cp walks them — so the new volume can hold a corrupt cluster that either refuses to start or, worse, starts with inconsistent data. This needs a docker compose stop (or at least stopping the postgres service) before the copy, and it's the one instruction on the page whose absence can lose data.

cp -rp /source/* also relies on shell globbing, which skips dotfiles — anything hidden at the top of the data directory is silently left behind, and the destination looks plausibly populated. cp -a /source/. /destination/ copies everything including hidden entries and preserves symlinks/ownership properly. (The license copy on the next line already uses -a.)

<Code code={`docker run --rm -v /path/to/thingsboard/.mytbpe-data/db:/source -v tb-postgres-data:/destination alpine sh -c "cp -rp /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/"`} lang="bash" />

3. Update your `docker-compose.yml` volume entries to use the named volumes (`tb-postgres-data:/var/lib/postgresql`, `tb-pe-license-data:/data`) instead of the local folder paths, then restart. No newline at end of file

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this step doesn't work as written, in two independent ways.

The mount target. Step 2 copies the contents of the old PGDATA (.mytbpe-data/db, which the legacy compose bind-mounted at /var/lib/postgresql/data), and this step mounts the resulting volume at /var/lib/postgresql — one level above where those files have to be. /var/lib/postgresql only works for the reference compose because it runs postgres:18, whose PGDATA lives in a versioned subdirectory. A reader who follows the caution immediately above and keeps their current Postgres image still needs the volume at /var/lib/postgresql/data. The Trendz equivalent gets this pairing right: trendz-postgres-data:/var/lib/postgresql/data, matching where the copy put the files (docs/trendz/installation/docker.mdx:57).

The volume has to be declared, with a pinned name. A legacy bind-mount compose file has no top-level volumes: block. Adding - tb-postgres-data:/… to a service without one fails outright (refers to undefined volume); adding a bare volumes: { tb-postgres-data: } is worse, because Compose then creates a project-prefixed volume (<project>_tb-postgres-data) rather than the one created in step 1 — so Postgres initialises an empty cluster and the data copied in step 2 is silently orphaned. The reference compose avoids this with name: tb-postgres-data under the volume key (_includes/docs/installation/docker.mdx:245); this step should show that top-level block (or external: true).

Minor, same line: the file is missing its trailing newline — Prettier will want to add it, and the five siblings in this directory all have one.

@@ -0,0 +1,67 @@
---
title: "Migrating from the legacy all-in-one tb-pe image"

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Frontmatter drifts slightly from the five siblings in this directory: they all set tableOfContents: false and use an unquoted title. With the TOC left on, this page renders a one-entry "On this page" labelled Optional: moving from local folder bind mounts to Docker named volumes. Either match the siblings with tableOfContents: false, or shorten the heading to something TOC-sized (Optional: switch to Docker named volumes) and let the prose carry the detail.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants