Guide for Upgrading To Liferay 2026 Q1 Successfully

blog-banner

A step-by-step technical reference for upgrading from Liferay DXP 7.x to 2026 Q1

⚠️ Coming from Liferay 6.x?

Upgrading from Liferay 6.x is a multi-hop process and requires a different approach.

Please refer to our dedicated guide: "Upgrading to Liferay 7.x from 6.x" before continuing with this document.

Direct 6.x → 26 Q1 upgrade is not supported. You must first reach 7.4 GA, then proceed with this guide.

Analysis & Pre-Upgrade Planning

Before touching any code or running the upgrade tool, you need a thorough picture of your existing environment. This analysis phase defines the scope of work, flags blockers early, and prevents surprises mid-upgrade.

Planning your Liferay DXP Upgrade to 2026 Q1? Visit our Liferay Upgrade & Migration page to see how we can support your technical roadmap.

1.1 Environment Baseline

Document every component of your current installation against the table below. This becomes your upgrade checklist and your rollback reference.

Component Your Current Value Required for 26 Q1
Source version Liferay DXP 7.x (note exact SP/FP) Liferay DXP 2026 Q1 (26.0.x)
Application server e.g. Tomcat 9.x Tomcat 10.1 (minimum)
Java version e.g. JDK 11 or JDK 8 OpenJDK 21 LTS (Java 11 dropped)
Database e.g. MySQL 5.7 / 8.0 MySQL 8.0.x (5.7 unsupported)
Elasticsearch e.g. 7.x Elasticsearch 8.x
OS / Docker base image e.g. Ubuntu 20.04 Update to support Java 21

1.2 Custom Assets Inventory

Before upgrading, catalogue every custom asset in your instance. Each category has a different migration path and risk level.

  Asset Type What to Check Migration Path
Custom OSGi Modules Count bundles; check javax.* imports, bnd.bnd, BOM alignment Rebuild against 26 Q1 BOM + jakarta migration
Custom Non-OSGi Portlets WAR portlets, legacy plugins; check servlet API usage Migrate to OSGi portlet or Client Extension
Client Extensions List all LXC / remote apps; check compatibility with new DXP APIs Review API compatibility; re-deploy
Custom Themes Check base theme, Clay version, FreeMarker templates Rebuild as Theme Client Extension
Custom Objects List object definitions, fields, relationships, actions Re-export/import; verify picklists and validations
Fragments Count fragment collections; check for deprecated JS APIs Validate in new renderer; update JS if needed
Web Contents Check structures, templates, and any Velocity templates Migrate Velocity → FreeMarker (Velocity removed)
Blogs Images, categories, display pages Auto-migrated; verify display page templates
Document and Media Check custom metadata, DL file entry types, preview renderers Verify file entry type migration; check adaptive media

Tip: Export the table above into a spreadsheet and assign an owner and status column to each row before starting the upgrade.

1.3 Modernisation Mapping: What to Migrate to What

Liferay 26 Q1 shifts several legacy extension patterns to modern equivalents. Use this table to map your current customisations to their target architecture before writing any code.

Legacy Pattern Modern Replacement Notes
Frontend JS Portlet (WAR) Remote App / IFrame CX No server-side Java; pure frontend deployed independently
OSGi MVC Portlet (simple UI) Custom Element Client Extension React/Vue/Angular; communicates via REST/GraphQL
Custom Theme (.war) Theme Client Extension CSS + JS only; extends base theme tokens
Ext Plugin Configuration + Fragment CX Ext plugins fully removed in 7.4+; replace with config
Service Builder Entity Liferay Object Definition For simple domain models; use Service Builder for complex
Velocity Templates FreeMarker Templates Velocity engine removed; mandatory migration
Legacy Hook (portal.properties) OSGi Configuration Admin (.cfg) Deploy .config / .cfg files to osgi/configs/
WAR Portlet OSGi Bundle Portlet Convert to module using Blade CLI scaffolding

ℹ️ Service Builder vs Objects — When to choose which?

Use Liferay Objects if: your entity is simple, no complex business logic, no direct DB joins needed.

Keep Service Builder if: you need custom SQL finders, complex relationships, workflow integration, or performance-critical queries.

You can mix both in the same project — Objects for config-driven data, Service Builder for transactional core.

Known Challenges & What to Expect

Understanding the major breaking changes before you start will save significant debugging time. These are the most common blockers encountered during a 7.x to 26 Q1 upgrade.

2.1 Jakarta EE Namespace Migration

The single largest code change. Every import of javax.* in your custom Java code must become jakarta.* — this is a hard compile-time failure if missed.

⛔ Compile-time break

Any custom module still importing javax.* packages will fail to compile and resolve in the OSGi runtime.

This includes Java source files AND XML deployment descriptors (web.xml, portlet.xml, persistence.xml).

Old (javax.*) New (jakarta.*) Affects
javax.servlet.* jakarta.servlet.* Filters, Listeners, Servlets
javax.portlet.* jakarta.portlet.* All custom portlets
javax.persistence.* jakarta.persistence.* JPA entities, DynamicQuery
javax.inject.* jakarta.inject.* CDI @Inject points
javax.ws.rs.* jakarta.ws.rs.* JAX-RS REST endpoints
javax.validation.* jakarta.validation.* Bean validation annotations
javax.annotation.* jakarta.annotation.* @PostConstruct, @PreDestroy
javax.el.* jakarta.el.* EL in JSP / FreeMarker

XML descriptor namespace URI also changes:

<!-- BEFORE --> 
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" version="4.0" />
<!-- AFTER --> 
<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee" version="6.0" />

2.2 Java 21 & Tomcat 10.1 Requirement

  • Java 11 is end-of-life for Liferay 26 Q1 — you must upgrade the JDK on all nodes
  • Tomcat 9.x implements Servlet 4.0 (javax.servlet) — incompatible with Jakarta EE 10 Servlet 6.0
  • Update Docker base images: FROM eclipse-temurin:21-jdk-jammy
  • Review GC flags — G1GC is still the default; ZGC is available for low-latency workloads on Java 21

2.3 OSGi Bundle Resolution Failures

After deployment, bundles may show as INSTALLED rather than ACTIVE in the Gogo Shell. This is almost always caused by unresolved Import-Package constraints that still reference javax.* namespaces in bnd.bnd.

# Diagnose via Gogo Shell
g! lb -s | grep -v Active
g! diag <bundleId>
# Common failure output:
Unresolved requirement: Import-Package: javax.portlet;version="[3.0,4.0)"
# Fix: update bnd.bnd -> jakarta.portlet;version="[3.0,4.0)"
# Force refresh after fixing:
g! refresh <bundleId>

2.4 Service Builder & Hibernate 6

Liferay 26 Q1 bundles Hibernate 6 which removes the legacy Criteria API. Custom finders using DynamicQuery with RestrictionsFactoryUtil patterns may fail silently or throw runtime exceptions.

// PROBLEMATIC (Hibernate 6) — Criteria API removed 
DynamicQuery dq = dynamicQueryFactory.forClass(MyEntity.class); 
dq.add(RestrictionsFactoryUtil.like("field", "%value%")); 

// RECOMMENDED — Use ActionableDynamicQuery 
ActionableDynamicQuery adq = myEntityLocalService.getActionableDynamicQuery(); 
adq.setAddCriteriaMethod(dq -> { 
    dq.add(RestrictionsFactoryUtil.ilike("field", "%value%")); 
}); 
adq.performActions();

2.5 Theme & Clay 4 Breaking Changes

  • Classic theme and Unstyled theme are removed — do not extend them
  • clay:dropdown-menu taglib removed → use clay:dropdown
  • clay:navigation-bar taglib removed → use clay:vertical-nav
  • clay:management-toolbar API changed — review props
  • If you are migrating to a Theme Client Extension, the build pipeline is entirely different (no Liferay Theme Toolkit; pure CSS/JS bundle)

2.6 Velocity Templates Removed

If your Web Content Structures or Display Pages use Velocity (.vm) templates, they will not render in 26 Q1. All templates must be migrated to FreeMarker (.ftl) before upgrading.

ℹ️ How to find Velocity templates

Go to: Site Administration → Content & Data → Web Content → Templates

Filter by Language = Velocity.

Export and rewrite each one in FreeMarker before proceeding with the upgrade.

Upgrade Steps

Follow these steps in sequence. Each step should be validated before proceeding to the next. We strongly recommend completing the full process in a staging environment before touching production.

⚠️ Before you start

  1. Take a full database backup: mysqldump -u root -p liferay_db > backup_$(date +%Y%m%d).sql
  2. Back up $LIFERAY_HOME/osgi/, $LIFERAY_HOME/deploy/, and portal-ext.properties
  3. Document your current portal.properties customisations
  4. Ensure you have rollback procedures agreed with your team

Step 1 — Upgrade Java & Application Server

1. Install OpenJDK 21 on all application server nodes

2. Replace Tomcat 9.x with Tomcat 10.1

3. Update setenv.sh JVM flags for Java 21

# setenv.sh — update JVM settings for Java 21 

CATALINA_OPTS="$CATALINA_OPTS -Xms2g -Xmx4g" 

CATALINA_OPTS="$CATALINA_OPTS -Dfile.encoding=UTF-8" 

CATALINA_OPTS="$CATALINA_OPTS -Djava.locale.providers=JRE,COMPAT,CLDR" 

# Optional: enable ZGC for low-latency (Java 21+) 
# CATALINA_OPTS="$CATALINA_OPTS -XX:+UseZGC" 

Step 2 — Update the Liferay Workspace

4. Update liferay.workspace.product in gradle.properties

5. Remove all hardcoded com.liferay.* version overrides

6. Refresh dependencies

# gradle.properties 

liferay.workspace.product=dxp-2026.q1.0 

# Then refresh: 

blade gw initBundle 
blade gw dependencies --refresh-dependencies 

Step 3 — Migrate Velocity Templates to FreeMarker

7. Export all Web Content templates using Velocity

8. Rewrite each as a FreeMarker (.ftl) template

9. Update the template language in the Web Content admin

FreeMarker equivalent of common Velocity syntax is documented at: learn.liferay.com/w/freemarker-migration

Step 4 — Run Jakarta Namespace Migration on Custom Modules

10. Install Eclipse Transformer CLI

11. Run transformer across modules directory

12. Manually review string literals, bnd.bnd, and XML descriptors

# Run Eclipse Transformer on your modules 
java -jar org.eclipse.transformer.cli.jar \ 

     ./modules ./modules-jakarta \ 

     -tf org.eclipse.transformer.jakarta.JakartaTransformRelocation 

# Verify no javax imports remain 

grep -r 'import javax\.' ./modules --include='*.java' | wc -l 

# Expected output: 0 

# Also update bnd.bnd Import-Package directives manually 
# javax.portlet;version="[3.0,4.0)" -> jakarta.portlet;version="[3.0,4.0)" 
# javax.servlet;version="[4.0,5.0)" -> jakarta.servlet;version="[6.0,7.0)" 

Step 5 — Rebuild Service Builder Entities

13. Re-run buildService task against the updated workspace

14. Review generated *FinderImpl for Hibernate 6 incompatible patterns

15. Migrate any DynamicQuery Criteria usage to ActionableDynamicQuery

cd modules/your-service-module 
blade gw buildService 
blade gw compileJava 

# If using Objects instead of Service Builder for simple entities: 

# Export the object definition JSON from your 7.x instance and 
# import it into the 26 Q1 instance via: 
# Objects → Export / Import 

Step 6 — Migrate Themes

Classic theme is removed. You have two options:

  • Option A: Extend the _styled base theme (traditional theme WAR, still supported)
  • Option B: Rebuild as a Theme Client Extension (recommended for new architecture)

Option A — Traditional Theme (extend _styled)

cd themes/your-theme 
# Update liferay-look-and-feel.xml 

# <compatibility-version>2026.q1.0</compatibility-version> 

# <extends>_styled</extends>   <- change from 'classic' 

gulp upgrade   # auto-detects deprecated Clay taglibs 

# Manually replace removed taglibs in templates/: 
# clay:dropdown-menu  ->  clay:dropdown 
# clay:navigation-bar ->  clay:vertical-nav 

Option B — Theme Client Extension

cd modules/your-service-module 
blade gw buildService 
blade gw compileJava 

# If using Objects instead of Service Builder for simple entities: 
# Export the object definition JSON from your 7.x instance and 
# import it into the 26 Q1 instance via: 
# Objects → Export / Import 
# Deploy: 

blade gw deploy 
# Then assign in: Site Settings → Custom Apps → Theme CSS

Step 7 — Migrate Frontend JS Portlets to Client Extensions

If you have React, Angular, or Vue-based frontend portlets built as OSGi modules or WAR files, the recommended path is to convert them to Custom Element Client Extensions.

# Scaffold a Custom Element Client Extension 
blade create -t client-extension -c custom-element my-react-widget
# client-extension.yaml example: 
my-react-widget: 
    name: My React Widget 
    type: customElement 
    htmlElementName: my-react-widget 
    url: index.js 
    cssURLs: 
        - index.css 
    instanceable: true 
    portletCategoryName: category.client-extensions

Step 8 — Run the Database Upgrade Tool

⚠️ Critical: Liferay must NOT be running during this step

Stop all Liferay nodes before running the DB upgrade tool.

The upgrade is irreversible — ensure your backup from Step 0 is verified first.

cd $LIFERAY_HOME/tools/portal-tools-db-upgrade-client 

./db_upgrade.sh \ 
    -j "-Dfile.encoding=UTF-8 -Xmx4096m" \ 
    --log-level DEBUG 

# Monitor progress: 
tail -f $LIFERAY_HOME/logs/upgrade*.log

# After completion, check for errors or skipped steps: 
grep -i 'ERROR\|WARN\|SKIP' $LIFERAY_HOME/logs/upgrade*.log

Step 9 — Deploy Custom Modules & Validate Bundle Resolution

16. Start Liferay with the updated Tomcat 10.1 + Java 21

17. Deploy all custom OSGi bundles to deploy/ or osgi/modules/

18. Open the Gogo Shell and verify all bundles reach ACTIVE state

# Open Gogo Shell via telnet (if enabled) or via Control Panel > Gogo Shell 
g! lb -s | grep -v ' Active ' 
# If list is empty: all bundles resolved successfully 

# If bundles show as INSTALLED or RESOLVED but not ACTIVE: 
g! diag <bundleId> 
# Fix the reported unresolved requirement, redeploy, then: 
g! refresh <bundleId>

Step 10 — Verify Content, Objects & Fragments

16. Navigate to Web Content and verify all structures and templates render

17. Open Object Definitions admin — confirm all custom objects, fields, and picklists are present

18. Open Fragments admin — preview each fragment collection; fix any deprecated JS APIs

18. Open Blogs admin — verify display page template assignments

Open Documents and Media — check custom metadata sets and adaptive media renditions

Breaking Changes Reference

Use this section as a checklist. Tick off each item as you confirm it is resolved in your upgrade.

4.1 Platform-Level Changes

Change Impact Action Required
Java 11 dropped High Upgrade to OpenJDK 21 LTS
Tomcat 9 incompatible High Install Tomcat 10.1
javax.* → jakarta.* High Full namespace migration on all custom code
Hibernate 6 (Criteria removed) High Migrate DynamicQuery patterns
MySQL 5.7 unsupported Medium Upgrade to MySQL 8.0.x
Elasticsearch 7 → 8 Medium Upgrade Elasticsearch cluster
Velocity templates removed Medium Migrate all .vm to .ftl
Classic & Unstyled themes removed Medium Extend _styled or use Theme CX

4.2 Jakarta Package Quick Reference

Old (javax.*) New (jakarta.*) Affects
javax.servlet.* jakarta.servlet.* Filters, Listeners, Servlets
javax.portlet.* jakarta.portlet.* All custom portlets
javax.persistence.* jakarta.persistence.* JPA entities, JPQL
javax.inject.* jakarta.inject.* CDI @Inject
javax.ws.rs.* jakarta.ws.rs.* JAX-RS REST
javax.validation.* jakarta.validation.* Bean validation
javax.annotation.* jakarta.annotation.* @PostConstruct, @PreDestroy
javax.el.* jakarta.el.* EL in JSP/FTL

4.3 Removed APIs

  • Hibernate Criteria API — use ActionableDynamicQuery or native JPQL
  • clay:dropdown-menu taglib — use clay:dropdown
  • clay:navigation-bar taglib — use clay:vertical-nav
  • com.liferay.portal.kernel.servlet.taglib.ui.* (several UI tag classes removed)
  • Legacy Ehcache XML config — migrate to portal.cache.multi.jvm OSGi config
  • WAR hook plugins — replace with OSGi fragments or configuration admin

Fixes & Code Patterns

Copy-paste-ready fixes for the most common upgrade issues.

Fix 1 — Bulk javax → jakarta Rename

# Automated rename across all Java source files 

find ./modules -name '*.java' | xargs sed -i \ 
  -e 's/import javax\.servlet/import jakarta.servlet/g' \ 
  -e 's/import javax\.portlet/import jakarta.portlet/g' \ 
  -e 's/import javax\.persistence/import jakarta.persistence/g' \ 
  -e 's/import javax\.inject/import jakarta.inject/g' \ 
  -e 's/import javax\.ws\.rs/import jakarta.ws.rs/g' \ 
  -e 's/import javax\.validation/import jakarta.validation/g' \ 
  -e 's/import javax\.annotation/import jakarta.annotation/g' 

# Verify nothing remains: 
grep -r 'import javax\.' ./modules --include='*.java'

Fix 2 — bnd.bnd Import-Package Update

# BEFORE 
Import-Package: javax.portlet;version="[3.0,4.0)",\ 
                javax.servlet;version="[4.0,5.0)" 

# AFTER (note: servlet version jumps to 6.0 under Jakarta EE 10) 
Import-Package: jakarta.portlet;version="[3.0,4.0)",\ 
                jakarta.servlet;version="[6.0,7.0)"

Fix 3 — DynamicQuery Migration

// BEFORE — Hibernate Criteria (fails on Hibernate 6) 

DynamicQuery dq = dynamicQueryFactory.forClass(Article.class); 
dq.add(RestrictionsFactoryUtil.like("title", "%" + keyword + "%")); 
return articlePersistence.findWithDynamicQuery(dq); 

// AFTER — ActionableDynamicQuery 

List<Article> results = new ArrayList<>(); 
ActionableDynamicQuery adq = articleLocalService.getActionableDynamicQuery(); 
adq.setAddCriteriaMethod(dq -> { 
    dq.add(RestrictionsFactoryUtil.ilike("title", "%" + keyword + "%")); 
}); 
adq.setPerformActionMethod((Article a) -> results.add(a)); 
adq.performActions(); 
return results;

Fix 4 — XML Descriptor Namespace

<!-- web.xml BEFORE --> 

<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" 
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee 
             http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" 
         version="4.0">

<!-- web.xml AFTER --> 

<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee" 
         xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee 
             https://jakarta.ee/xml/ns/jakartaee/web-app_6_0.xsd" 
         version="6.0">

Fix 5 — Cache Configuration (OSGi Config)

# Create file in: $LIFERAY_HOME/osgi/configs/ 
# Filename: com.liferay.portal.cache.multiple.internal.module.configuration 

#           .MultiplePortalCacheManagerConfiguration.config 

clusterLinkEnabled=B"true" 
replicatedPortalCacheEnabled=B"true" 

# Remove any legacy ehcache-*.xml files from WEB-INF/ 
# They are silently ignored in 26 Q1 

QA & Validation

After completing all upgrade steps, run through this validation checklist before sign-off.

6.1 OSGi Health Check

# All custom bundles must be ACTIVE 
g! lb -s | grep -v Active 
# Expected: empty output 

# Check for any ERROR-level log entries at startup 
grep -i 'ERROR' $LIFERAY_HOME/logs/liferay*.log | grep -v 'expected'

6.2 Functional Regression Areas

Area to Test What to Validate
Custom portlets Render correctly, no 500 errors, all actions work
Client extensions Remote apps load, custom elements initialise
Themes All pages render with correct styling and Clay components
Web Content Structures render, FreeMarker templates output correct HTML
Custom Objects All object entries visible, forms submit, relationships intact
Fragments Fragment collections visible, JS interactions work
Blogs Display pages assigned, images render, categories preserved
Documents and Media Files accessible, metadata sets present, previews render
Search Site search returns results, facets work
Workflow Existing workflow definitions load, approvals route correctly
REST APIs All custom Headless API endpoints return 200
User / Roles / Permissions Existing roles intact, permission inheritance working

6.3 Performance Sanity Checks

  • Homepage load time within acceptable range (compare with pre-upgrade baseline)
  • REST API p95 response time not regressed
  • Heap memory stabilises under load (no continuous growth indicating leak)
  • Startup time measured and documented for baseline comparison on next upgrade

Support & Reference Resources

7.1 Official Liferay Documentation

  • Release Notes 2026 Q1: learn.liferay.com/w/liferay-release-2026q1
  • Breaking Changes Reference: learn.liferay.com/w/breaking-changes-2026q1
  • Database Upgrade Tool: learn.liferay.com/w/database-upgrade-tool
  • Client Extension Overview: learn.liferay.com/w/client-extensions
  • Objects Documentation: learn.liferay.com/w/liferay-objects
  • Fragments Documentation: learn.liferay.com/w/page-fragments-and-layouts

7.2 Jakarta EE Resources

  • Jakarta EE 10 Specification: jakarta.ee/specifications
  • Eclipse Transformer CLI: github.com/eclipse/transformer
  • Jakarta Migration Guide: jakarta.ee/resources/migration

7.3 Tooling

  • Blade CLI — workspace management, scaffolding, deploy: blade.liferay.com
  • Liferay Upgrade Planner (IntelliJ Plugin) — step-by-step guided upgrade
  • Gogo Shell (lb, diag, refresh) — OSGi runtime diagnostics
  • Eclipse Transformer — automated javax → jakarta migration
  • gulp upgrade — detect deprecated Clay taglibs in theme projects

7.4 Key Lessons & Tips

✅ Top tips for a successful upgrade

  1. Inventory first, code second — complete Section 1 fully before writing any migration code.
  2. Commit the Eclipse Transformer run as a single atomic commit — makes it easy to diff and review the bulk rename.
  3. Always update bnd.bnd Import-Package after running the transformer — it does not always catch these.
  4. Run the DB upgrade on a full production data clone, not just a schema dump — data volume exposes timeout issues.
  5. Migrate Velocity templates early — they are often forgotten and block content rendering post-upgrade.
  6. Budget 2x your estimated time. Jakarta migration + Service Builder regeneration + theme rebuild always surfaces surprises.
  7. Keep the Gogo Shell open on a second screen during first startup — bundle errors surface there first.
Contact us

For Your Business Requirements

Contact us