When migrating applications from traditional WebSphere Application Server (tWAS) to WebSphere Liberty, most transactions behavior stays similar — but a few important differences are worth understanding before you begin.
Both tWAS and Liberty follow the same transaction specification. Liberty provides many of the same transaction services as WebSphere Application Server traditional, and its transaction manager coordinates updates across multiple resource managers under a single global transaction. So features like XA (two-phase commit) transactions across databases and messaging systems continue to work.
The main differences show up in three areas:
- How transactions are enabled. In tWAS, the transaction service is always present and managed through the administrative console. In Liberty, transaction services are activated implicitly when you enable features that need them, such as jdbc, jpa, or the messaging features. Configuration moves into a simple, readable server.xml.
- Transaction scope across JVMs. This is the change most teams need to plan for. In Liberty, the transaction boundary cannot span more than a single JVM process. The global transaction can still involve multiple XA resources outside the JVM (like Db2, Oracle, or IBM MQ), and can include another EJB, as long as it resides in the same JVM and is called over its local interface rather than RMI/IIOP. tWAS, by contrast, supports cross-JVM transaction propagation.
- Recovery in a clustered setup. tWAS relies on the High Availability Manager for transaction peer recovery. Liberty takes a lighter approach. The recoveryGroup and recoveryIdentity attributes of the peer Liberty servers takes care of recovering the in-doubt/pending transactions of a failed Liberty server.
WebSphere Liberty activates its transaction service when an enabled feature requires transactional support. Most applications therefore do not need to enable a transaction feature explicitly. A reliable Liberty transaction design depends on four elements: a suitable transaction boundary, realistic timeout values, durable recovery logs, and a stable recovery identity. In containerized environments, those elements must be designed together because a replacement pod might not return with the same local storage or identity.
1. Transaction service activation
The transaction service is activated implicitly when Liberty features that depend on transactions are enabled, such as Jakarta Persistence, JDBC, enterprise beans, messaging, or Jakarta Transactions. Feature names depend on the Liberty and Jakarta EE level in use, so select features that match the application rather than copying a fixed feature list from another server.
<featureManager>
<feature>transaction-1.2</feature> <!-- Core JTA 1.2 -->
<feature>jdbc-4.2</feature> <!-- JDBC + XA -->
<feature>jndi-1.0</feature> <!-- JNDI lookups -->
<feature>ejb-3.2</feature> <!-- CMT (Container-Managed Transactions) -->
</featureManager>
Applications can define transaction boundaries through container-managed transactions, the Jakarta Transactions @Transactional interceptor, or the UserTransaction API where programmatic control is allowed. The runtime enlists transactional resources as they are used.
Administering the transaction service on Liberty
Configuring the startup of the transaction service
Java EE 7 feature ==> transaction-1.2
2. Transaction configuration
The <transaction> element in server.xml controls timeout behavior, transaction-log placement, startup recovery, peer recovery, and selected heuristic handling. Configure only the settings required by the workload and document why each non-default value exists.
<transaction
totalTranLifetimeTimeout="120s"
propogatedOrBMTTranLifetimeTimeout="300s"
transactionLogDirectory="${server.output.dir}/tranlog"/
clientInactivityTimeout="60s"
heuristicRetryLimit="3"
heuristicRetryWait="10s"
transactionLogDirectory=" ${server.output.dir}/tranlog/tranlog"
recoverOnStartup="true"
waitForRecovery="true"
recoverOnStartup="true"
waitForRecovery="false" >


Configuration properties for the Transaction Manager service
3. Transaction recovery fundamentals in Liberty
3.1 Startup recovery
By default, Liberty starts the transaction recovery when the transaction service is first used. Setting recoverOnStartup="true" performs recovery during server startup. Setting waitForRecovery="true" additionally prevents new transactional work from client applications until recovery completes.
<transaction
recoverOnStartup="true"
waitForRecovery="true"/>
Above combination is useful when the service must not accept transactional traffic while prepared work remains unresolved. Account for the additional startup time in orchestration startup probes and deployment timeouts.
3.2 Recovery credentials and resource identity
During database recovery, Liberty locates the current data source by its id or JNDI name and determines which credentials to use. Do not rename/change the id or jndiName while recovery is pending for transactions in which that data source participated.
<authData id="recoveryAuth"
user="${env.RECOVERY_DB_USER}"
password="${env.RECOVERY_DB_PASSWORD}"/>
<dataSource id="testDS"
jndiName="jdbc/testDS"
jdbcDriverRef="my_dbDriver"
recoveryAuthDataRef="recoveryAuth">
……
</dataSource>
For datasources and connection factories (including jmsConnectionFactorys) XA recovery uses the authentication data configured via the recoveryAuthDataRef attribute. The recovery account must be able to connect and perform the database/JMS XA recovery operations. Verify privileges with the database or messaging administrator.
In terms of whether this is required or not - that depends on how the resource manager is configured. By default, MQ doesn't require a user/password for connection, including for when connecting to perform XA recovery, however that is configurable within MQ. Other RMs will require authentication data, indeed when using an Oracle databasem, specific permissions must be granted to any userid that performs XA recovery (if they are not given then the call to Oracle's XAResource.recover fails with XAER_RMERR).
4. Choosing and configuring the transactions recovery logs
4.1 File system for transaction logs:
Liberty stores recovery logs in operating-system files by default. File system logs are appropriate when the server identity and durable storage survive restart, and when the storage provides the locking and consistency semantics required by Liberty.
<transaction
transactionLogDirectory="/var/lib/liberty/tranlog"
recoverOnStartup="true"/>
- When using file-based transaction logs, place the log directory on durable storage. A container's writable layer is ephemeral and disappears when the container is removed, so it should never be treated as durable recovery storage.
- Keep the ownership, permissions, mount path, and server identity consistent across your Liberty container restarts and replacements. Recovery depends on the server being able to find and claim its own logs, so these details must remain stable.
- Never let two active server instances own the same transaction log. Concurrent access to a single file log leads to corruption and unpredictable recovery behavior.
- Finally, back up your configuration and storage metadata, but be careful during restores: don't overwrite newer recovery state with stale log files. Restoring an outdated log over a more current one can undo in-progress recovery and reintroduce transactions that were already resolved.
4.2 Database for transaction logs:
A relational database can provide a shared, durable repository when reliable shared file storage is not suitable for the architecture. Configure a dedicated non-transactional data source for the transaction log. Each server must use its own transaction-log tables, typically through a unique transactionLogDBTableSuffix.
<transaction
dataSourceRef="tranlogDataSource"
recoverOnStartup="false"
recoveryGroup="peer-group-name"
recoveryIdentity="${HOSTNAME}${wlp.server.name}"
transactionLogDBTableSuffix="${HOSTNAME}${wlp.server.name}"/>
<dataSource id="tranlogDataSource" transactional="false">
<jdbcDriver libraryRef="DB2JCC4LIB"/>
<properties.db2.jcc databaseName="TXLOG"
serverName="dbhost"
portNumber="50000"
user="dbuser"
password="{xor}Oz1tPjsyNjE="/>
</dataSource>
5. Peer recovery for Liberty ND containers
If the pod is gone for good for any reason, the transaction and any locks it holds in the resource managers can stay stuck. Peer recovery solves this by letting a surviving member of the group complete the failed server's transactions instead. Peer recovery allows one Liberty server to recover the transactions of a failed member. Servers in the same recovery group monitor their peers and can access a failed member’s logs when that member disappears.
Every server in the group is given the same recoveryGroup and its own unique recoveryIdentity. Each member then watches the others: a server configured this way continually monitors its peers, the other instances sharing the same recoveryGroup value. Membership and ownership are tracked through leases. A healthy server periodically renews its lease; when a peer's lease expires and stays expired, another member concludes that server has failed, claims its logs, and drives recovery to completion on its behalf. This is where the lease-tuning settings come in — leaseLength, leaseExpiryThreshold, and leaseRenewalThreshold control how quickly a dead peer is detected versus how tolerant the group is of a slow or briefly-paused server.
Peer recovery configuration in Liberty server.xml.
<transaction
recoveryGroup="recovery-group1"
recoveryIdentity="${HOSTNAME}-${wlp.server.name}"
recoverOnStartup="false"
waitForRecovery="true"
dataSourceRef="tranlogDS"
transactionLogDBTableSuffix="${HOSTNAME}"/>
DDL structures show how to create the database tables
All Liberty app servers should be in the same recoveryGroup and recoveryIdentity is a unique ID for each Liberty server in that group.
|
Requirement
|
Why it matters
|
|
Unique recoveryIdentity
|
Two active instances must never present the same recovery identity or compete for the same recovery state.
|
|
Durable, peer-accessible logs
|
A surviving peer cannot recover a failed member if the log disappeared with the pod or is mounted privately.
|
|
Access to the same XA resources
|
The peer needs compatible data-source or connection-factory configuration, drivers, network paths, TLS material, credentials, and recovery privileges.
|
|
Stable naming while recovery is pending
|
Changing a resource id or JNDI name can prevent Liberty from locating the resource required to complete recovery.
|
|
Supported locking semantics
|
Shared filesystems must provide the required consistency and locking. Cross-data-center RWX shared filesystems are not supported for this purpose; use RDBMS logs in that topology.
|
In Kubernetes, ${HOSTNAME} commonly maps to the pod name. Whether that value provides the right balance of uniqueness and recoverability depends on the workload controller, persistence model, and log-store design. Validate pod replacement, scaling, and failover behavior under controlled failure tests.
If 2 servers are part of recoveryGroup server.xml looks like below:
example in server.xml for server1:
<transaction
recoverOnStartup="true"
waitForRecovery="false"
transactionLogDirectory="c:/X_shared_storage/server1"
recoveryGroup="RG1"
recoveryIdentity="UUID1"
backendURL="http://localhost:9081"/>
example in server.xml for server2:
<transaction
recoverOnStartup="true"
waitForRecovery="false"
transactionLogDirectory=" c:/X_shared_storage/server2"
recoveryGroup="RG1"
recoveryIdentity="UUID2"
backendURL="http://localhost:9082"
/>
Note: uniqueID should only contain alphanumeric characters [A-Z][a-z][0-9] ie not symbols like '-'.
backendURL is the protocol://host:port for the app server that a different Liberty app server in the same Recovery Group. Typically this would be the default http endpoint for the app server.
6. Liberty-specific troubleshooting patterns
6.1 WTRN0000E, WTRN0005W, WTRN0025E, and WTRN0045W — XAResource Cannot Be Re-created, or an Invalid Xid or recoveryId in the Liberty Log/FFDC’s
The following errors share the same root cause. When an Invalid Xid or recoveryId error is logged, you might see one or more of these messages.
WTRN0000E: An internal error occurred in method reconstruct in class com.ibm.ws.tx.jta.RegisteredResources; the exception stack trace follows: java.lang.Exception: Invalid Xid/recoveryId in transaction log
WTRN0025E: Recovery of Transaction failed javax.transaction.SystemException: java.lang.Exception: Invalid Xid/recoveryId in transaction log
WTRN0005W: The XAResource for a transaction participant could not be recreated and transaction recovery may not be able to complete properly. The resource was <XYZ>
FFDC1015I: An FFDC Incident has been created: "java.lang.Exception: Invalid Xid/recoveryId in transaction log com.ibm.tx.jta.embeddable.impl.EmbeddableRegisteredResources.reconstruct 843" at ffdc_***.log
com.ibm.tx.jta.embeddable.impl.EmbeddableRegisteredResources E WTRN0045W: The transaction service cannot recover resource 0000000000000000000000020001. The exception stack trace follows: java.lang.Exception: Invalid Xid/recoveryId in transaction log
This error occurs when the transaction manager finds an entry in the partner log but can no longer reconstruct the corresponding XAResource. It typically occurs after a data source or a messaging engine has been re-created, or after queues or other resources have changed since the entry was written to the logs.
In this situation, the transaction service typically generates an FFDC record, which contains essential information about the recovery ID that cannot be recovered.
Key information in FFDC:
Stack Dump = java.lang.Exception: Invalid Xid/recoveryId in transaction log
=======================
_recoverableUnitIdentity = 4
_logIdentifierString = "TRAN_LOG"
_serverName = "_RI_POD0"
_recoveryTableName = "WAS_"
_recoveryIndexName = "XYZ"
_recoveryTableNameSuffix = "_RI_POD3"
========================
The FFDC record contains the table names and the associated suffix details.
If an FFDC record is not available, review the transaction traces for recoveryID=* and recovered=false.
If an FFDC record is available, you do not need to enable tracing to identify the recovery ID in question. For Liberty, run the following statement against the transaction log database tables.
To fix the error, delete the RUID from the transaction logs using the following query:
recoveryTableName = "WAS_"+_logIdentifierString ( "TRAN_LOG")+_recoveryTableNameSuffix ("_RI_POD3")
So the tranlog table name is :WAS_TRAN_LOG_RI_POD3
Syntax: DELETE FROM <tranlogtable> WHERE RU_ID=XXX
DELETE FROM WAS_TRAN_LOG_RI_POD3 WHERE RU_ID=4
After this record has been deleted from the tranlog database table, the server should start as expected.
DONT delete RU_ID 1 and -1 which indicates metadata.
6.2 Recovery audit messages
Some Liberty environments do not emit the full recovery-progression commentary expected in traditional WebSphere Application Server. WTRN0105I would be produced on Liberty but messages WTRN0132-134 and WTRN0135-151 are not produced by default on Liberty. The JVM system property below is used to investigate the recovery issues.
-Dcom.ibm.tx.auditRecovery=true
Treat com.ibm.tx.auditRecovery as a diagnostic aid rather than a general server.xml setting. Validate it for the exact Liberty level and use it when directed by IBM Support or when a controlled test confirms the expected output.
6.3 Recovery logs disappeared with the container
Symptom: A replacement pod starts normally, but prepared transactions remain visible in a database or messaging provider; the expected log directory is empty.
Likely cause: The file log was stored on an ephemeral layer or local to that pod.
Corrective action: Restore the correct durable storage and recovery identity. For future deployments, use a supported persistent file systemor database logs for transaction recovery logs.
6.4 Two instances compete for one recovery log
Symptom: Exclusive-access, file-lock, lease, or repeated peer-recovery errors; an instance may stop after an unrecoverable log failure.
Likely cause: Duplicate recoveryIdentity, duplicate table suffix, or concurrent ownership of one file-log directory.
Corrective action: Stop the duplicate owner, correct identity and log namespaces, and verify orchestration does not run two owners for the same state.
6.5 Liberty can read the log but cannot recover an XA resource
Symptom: Repeated XA recover failures, authentication errors, missing schema/object errors, TLS failures, or connection timeouts.
Likely cause: Missing recovery privileges, incorrect recovery credentials, changed resource identity, unavailable driver/provider configuration, or network failure.
Corrective action: Validate recoveryAuthDataRef, XA privileges, resource id/JNDI name, drivers, DNS, firewall, TLS, and provider availability.
6.6 Server accepts work before recovery completes
Symptom: Startup completes, but early traffic experiences lock contention, recovery load, or a slow first transactional request.
Likely cause: Recovery is deferred to first use, or waitForRecovery is false.
Corrective action: Use recoverOnStartup="true" and, when required by service policy, waitForRecovery="true". Align readiness checks with recovery.
6.7 Database transaction-log store is unavailable
Symptom: Transaction service startup/recovery fails with connection, table, schema, or SQL errors.
Likely cause: Database outage, bad credentials, missing create/read/write privileges, incorrect suffix, driver issue, or a transactional log data source.
Corrective action: Restore connectivity and permissions; ensure transactional="false"; verify unique tables and the database before accepting traffic.
7. Frequently asked questions
Should recoverOnStartup always be true?
Not necessarily. The default is false, which defers recovery until first transactional use. Production services that need predictable recovery before traffic often set it to true; pair it with waitForRecovery when new transactional work must not begin until recovery finishes.
Can a larger totalTranLifetimeTimeout prevent in-doubt transactions?
No. It changes how long active transactions may run. In-doubt work is associated with two-phase completion interrupted by failure and must be resolved through recovery.
Must defaultMaxShutdownDelay be equal to the transaction timeout?
No. They serve different purposes. The shutdown delay gives normal in-flight work time to finish during an orderly stop. the transaction timeout limits active transaction lifetime.
The defaultMaxShutdownDelay provides a delay in order to attempt to complete existing work, including completing existing transactions. In theory the greater this value the more chance there is of avoiding having transactions that have indoubt transaction branches once the app server is stopped - in reality a value of 120s is normally plenty - if a transaction branch is indoubt when the app server stops that's more likely to be caused by a not particularly transient problem connecting to the resource (network issue, backend resource manager down etc).
Can a new pod start with an empty tranlog?
It can start, but an empty log cannot resolve prepared work recorded by the failed instance. Durable log access and the correct identity are essential.
Can the transaction log be deleted after recovery appears stuck?
Do not delete it as routine troubleshooting. Preserve the evidence, restore resource access, and engage IBM Support before any destructive action.
Is there a Liberty recovery-only command?
There is no general documented command that directly mirrors a recovery-only mode. Operational isolation can be used carefully, but it must prevent concurrent log ownership and normal workload. If pod /Liberty server is terminated or crashed peer Liberty server from the same recovery group can resolve the pending transactions as mentioned above.
Configuring transaction recovery for managed Liberty servers
How database transactions are recovered
Storing transaction logs in a relational database
What is new in this release of Liberty
Troubleshooting Transaction Timeout Issues (WTRN0006W / WTRN0124I) in WebSphere Application Server and WebSphere Liberty
Troubleshooting Transaction Recovery issues in WebSphere Application Server and WebSphere Liberty
Conclusion
WebSphere Liberty provides a compact transaction runtime, but reliable recovery still depends on explicit operational design. Keep transaction boundaries short, choose timeouts from evidence, preserve durable recovery records, keep identities unique and stable, and ensure that every recovery participant remains reachable with the required credentials and privileges. In cloud deployments, test failure and replacement scenarios before production. Successful startup alone is not proof that prepared work can be recovered.