Resource Migration Pipeline
Use this workflow when a structure change is risky because content already exists.
This is one of the few ldev workflows that has no equivalent in Liferay itself. Out of the box, Liferay has no native pipeline for migrating articles when a journal structure changes — typical fixes involve manual SQL, redoing content, or accepting data loss. ldev provides the missing pipeline.
Examples it supports:
- remove a field
- rename a field
- split a structure into a safer schema
- update related templates while preserving existing content
How it differs from a normal import
A normal import (see Export and Import Resources) replaces the structure definition. It does not move data inside articles that already use that structure.
The migration pipeline reads a migration descriptor that defines:
- old structure → new structure mapping
- per-field actions (copy, rename, drop, default)
- whether old fields should be cleaned up after mapping
- which templates should be applied alongside the structure change
The descriptor is the source of truth for the migration plan.
1. Discover the current structure
ldev portal inventory structures --site /global --with-templates --jsonNote the structure key you want to migrate.
2. Export the current resource state
ldev resource export-structure --site /global --structure MY_STRUCTURE
ldev resource export-templates --site /globalCommit these so the before-state is in Git history.
3. Generate the migration descriptor
ldev resource migration-init \
--site /global \
--structure MY_STRUCTURE \
--templatesThis creates a descriptor file under the configured migrations path. Edit it: define the field mapping, keep cleanup disabled for the first proof, and list the templates to apply.
4. Validate the pipeline without mutating content
ldev resource migration-pipeline \
--migration-file liferay/resources/journal/migrations/global/MY_STRUCTURE.migration.json \
--check-only \
--migration-dry-runUse this to review the plan and catch obvious problems early. The output includes a reasonBreakdown per migrated structure with counters such as copiedToNewField, alreadyHadTargetValue, sourceEmpty, noEffectiveChange, and sourceCleaned.
5. Run the real pipeline deliberately
ldev resource migration-pipeline \
--migration-file liferay/resources/journal/migrations/global/MY_STRUCTURE.migration.jsonThe first real proof should keep the legacy fields readable in the structure, use mappings with cleanup disabled, and verify the copied values in the new shape. If the slow Liferay write path causes client timeouts, increase the request timeout:
ldev resource migration-pipeline \
--liferay-timeout-seconds 300 \
--migration-file liferay/resources/journal/migrations/global/MY_STRUCTURE.migration.jsonThe same timeout can be configured with LIFERAY_CLI_HTTP_TIMEOUT_SECONDS=300 or liferay.oauth2.timeoutSeconds. Longer timeouts reduce ambiguous recoverable timeouts, but they do not replace read-after-write verification.
If you intend to execute cleanup after that proof, get explicit approval and run it deliberately:
ldev resource migration-pipeline \
--migration-file liferay/resources/journal/migrations/global/MY_STRUCTURE.migration.json \
--run-cleanupDo not read this as a recommendation to run the pipeline twice in sequence. After validation, choose one approved real execution plan and run it deliberately with the flags that match. Do not remove legacy fields, fallback template reads, or source values before the copied target values are verified.
6. Verify
Read the migrated structure back, and check a page that uses it:
ldev resource structure --site /global --structure MY_STRUCTURE
ldev portal inventory page --url /home --json
ldev logs diagnose --since 10m --jsonRecommended way to test
Test the migration against production-like data in an isolated branch environment. This is exactly what ldev worktree is for:
ldev worktree setup --name migration-test --with-env
cd .worktrees/migration-test
ldev startIf migration-test already exists as a linked git worktree outside .worktrees/, run ldev worktree setup --with-env from inside that checkout instead of creating a second worktree.
Then run the full migration there before applying it elsewhere. See Worktrees for the full model.
Why this workflow exists
Liferay treats structures as immutable from the data side: articles are locked to the structure version they were created against. Without a pipeline like this, structural change either means writing custom SQL, losing content, or migrating manually article by article.
ldev resource migration-pipeline is the workflow you would otherwise have to build yourself.