A tool for migrating Camunda 7 process instances and related data to Camunda 8. This migrator helps organizations seamlessly transition their process instances while preserving execution state and variables ensuring minimal disruption to ongoing business processes.
Warning
The C7 Data Migrator is currently in active development and not yet ready for production use. However, we encourage users to try it out in development/testing environments and provide feedback to help us improve the tool.
- Overview
- Key Features
- Prerequisites
- Installation & Setup
- Quick Start
- Supported Databases
- Configuration
- Variable transformation
- Migration Limitations
- Troubleshooting
- Migration Process
- Development
- Contributing
- License
The C7 Data Migrator is designed to help organizations migrate from Camunda 7 to Camunda 8 while preserving the state of running process instances. Unlike starting fresh with new process instances, this tool maintains variable states, and current positions within process flows.
What this tool migrates:
- Running process instances with their current state
- Process variables and their values
What this tool does NOT migrate:
- BPMN process models (use the Migration Analyzer for this)
- Custom code or integrations
- Users, groups and authorizations
- Task assignments and states
- Execution history (we are working on this currently)
- State-preserving migration: Maintains exact execution state of running process instances
- Variable data migration: Converts and migrates process variables with proper type handling
- Validation and verification: Pre-migration validation to ensure successful migration
- Skip and retry capabilities: Handle problematic instances gracefully with retry options
- Detailed logging and reporting: Comprehensive logging for monitoring migration progress
- Database flexibility: Support for multiple database vendors (H2, PostgreSQL, Oracle)
Before using the C7 Data Migrator, ensure you have:
- Java 21 or higher - Required for running the migrator
- Maven 3.6+ - For building from source (if not using pre-built releases)
- Running Camunda 8 instance - Target platform for migration
- Access to Camunda 7 database - Source database with process instances to migrate
- Migrated BPMN models - Process definitions already converted from C7 to C8 format
- Network connectivity - Between migrator, C7 database, and C8 platform
- Download the latest release from the releases page
- Extract the archive to your preferred directory
- Navigate to the extracted directory
# Clone the repository
git clone https://github.com/camunda/c7-data-migrator.git
cd c7-data-migrator
# Build the project
mvn clean install -DskipTests
# Navigate to the distribution
cd assembly/target
# Extract the generated archive (tar.gz or zip)
-
Make sure Camunda 8 is up and running and all process models that shall be migrated are deployed.
To be used with the Runtime Data Migrator, every process model requires:
- A blank start event (you need to add one if the process model doesn't have one)
- An execution listener at the end of your blank start event with the job type
migrator
(you have to add it manually, or let it be added by the Migration Analyzer & Diagram Converter).
<bpmn:startEvent id="StartEvent_1"> <bpmn:extensionElements> <zeebe:executionListeners> <zeebe:executionListener eventType="end" type="migrator" /> </zeebe:executionListeners> </bpmn:extensionElements> </bpmn:startEvent>
-
Prepare your configuration file (
application.yml
):camunda.client: mode: self-managed grpc-address: http://localhost:26500 rest-address: http://localhost:8088 camunda.migrator.c7.data-source: jdbc-url: jdbc:postgresql://localhost:5432/camunda7 username: your-username password: your-password
-
Run the migrator:
# On Linux/macOS ./start.sh --runtime # On Windows start.bat --runtime
-
Monitor the migration progress in the console output and log files.
# Migrate running process instances
./start.sh --runtime
# List skipped instances
./start.sh --runtime --list-skipped
# Retry previously skipped instances
./start.sh --runtime --retry-skipped
# Migrate historical data
./start.sh --history
# Use custom configuration file
./start.sh --spring.config.location=file:./my-config.yml
The migrator supports the following SQL databases:
Database | Version | JDBC Driver | Notes |
---|---|---|---|
H2 | 2.3.232 | org.h2.Driver |
Default, good for testing |
PostgreSQL | 17 | org.postgresql.Driver |
Recommended for production |
Oracle | 23ai | oracle.jdbc.OracleDriver |
Enterprise option |
- Include the appropriate JDBC driver in your classpath
- Configure connection details in
application.yml
- Set table prefixes if your existing installation uses them
- Verify connectivity before starting migration
- Ensure sufficient disk space for migration data
Note: The database vendor is automatically detected but can be overridden using the
database-vendor
property.
The migrator can be configured using the application.yml
file. Here are the available configuration options:
camunda.client:
mode: self-managed # Operation mode: 'self-managed' or 'cloud'
grpc-address: http://localhost:26500 # The gRPC API endpoint
rest-address: http://localhost:8088 # The REST API endpoint
camunda.migrator:
batch-size: 500 # Number of records to process in each batch
auto-ddl: true # Automatically create/update database schema
table-prefix: MY_PREFIX_ # Optional table prefix for migrator schema
data-source: C7 # Choose if the migrator schema is created on the data source of 'C7' or 'C8'
interceptors:
- class-name: com.example.MyCustomInterceptor # Custom interceptor class
- class-name: com.example.AnotherInterceptor # Another custom interceptor class
camunda.migrator.c7.data-source:
table-prefix: MY_PREFIX_ # Optional prefix for C7 database tables
auto-ddl: true # Automatically create/update C7 database schema
jdbc-url: jdbc:h2:./h2/data-migrator-source.db
username: sa # Database username
password: sa # Database password
driver-class-name: org.h2.Driver
camunda.migrator.c8.data-source:
table-prefix: MY_PREFIX_ # Optional prefix for C8 RDBMS database tables
auto-ddl: true # Automatically create/update C8 RDBMS database schema
jdbc-url: jdbc:h2:./h2/data-migrator-target.db
username: sa # Database username
password: sa # Database password
driver-class-name: org.h2.Driver
logging:
level:
root: INFO # Root logger level
io.camunda.migrator: INFO # Migrator logging
io.camunda.migrator.RuntimeMigrator: DEBUG # Runtime migration logging
io.camunda.migrator.persistence.IdKeyMapper: DEBUG # ID mapping logging
Prefix | Property | Type | Description |
---|---|---|---|
camunda.client |
Read more about Camunda Client configuration options. | ||
.mode |
string |
Operation mode of the Camunda 8 client. Options: self-managed or cloud . Default: self-managed |
|
.grpc-address |
string |
The gRPC API endpoint for Camunda 8 Platform. Default: http://localhost:26500 |
|
.rest-address |
string |
The REST API endpoint for Camunda 8 Platform. Default: http://localhost:8088 |
|
camunda.migrator |
|||
.batch-size |
number |
Number of records to process in each migration batch. Default: 500 |
|
.auto-ddl |
boolean |
Automatically create/update migrator database schema. Default: false |
|
.table-prefix |
string |
Optional prefix for migrator database tables. Default: (empty) | |
.data-source |
string |
Choose if the migrator schema is created in the C7 or C8 data source. Default: C7 |
|
.database-vendor |
string |
Database vendor for migrator schema. Options: h2 , postgresql , oracle . Default: Automatically detected. |
|
.interceptors |
array |
List of custom variable interceptors to apply during migration. Each interceptor must implement the VariableInterceptor interface. |
|
camunda.migrator.c7.data-source |
|||
.table-prefix |
string |
Optional prefix for Camunda 7 database tables. Default: (empty) | |
.auto-ddl |
boolean |
Automatically create/update Camunda 7 database schema. Default: false |
|
.database-vendor |
string |
The database vendor is automatically detected and can currently not be overridden. | |
.* |
You can apply all HikariConfig properties. For example: |
||
.jdbc-url |
string |
JDBC connection URL for the source Camunda 7 database. Default: jdbc:h2:mem:migrator |
|
.username |
string |
Username for Camunda 7 database connection. Default: sa |
|
.password |
string |
Password for Camunda 7 database connection. Default: sa |
|
.driver-class-name |
string |
JDBC driver class for Camunda 7 database. Default: org.h2.Driver |
|
camunda.migrator.c8 |
|||
.deployment-dir |
string |
Define directory which resources like BPMN processes are automatically deployed to C8. | |
camunda.migrator.c8.data-source |
If the c8.data-source configuration is absent, the RDBMS history data migrator is disabled. |
||
.table-prefix |
string |
Optional prefix for Camunda 8 RDBMS database tables. Default: (empty) | |
.auto-ddl |
boolean |
Automatically create/update Camunda 8 RDBMS database schema. Default: false |
|
.database-vendor |
string |
Database vendor for C8 schema. Options: h2 , postgresql , oracle . Default: Automatically detected. |
|
.* |
You can apply all HikariConfig properties. For example: |
||
.jdbc-url |
string |
JDBC connection URL for the target Camunda 8 RDBMS database. Default: jdbc:h2:mem:migrator |
|
.username |
string |
Username for Camunda 8 database connection. Default: sa |
|
.password |
string |
Password for Camunda 8 database connection. Default: sa |
|
.driver-class-name |
string |
JDBC driver class for Camunda 8 database. Default: org.h2.Driver |
|
logging |
|||
.level.root |
string |
Root logger level. Default: INFO |
|
.level.io.camunda.migrator |
string |
Migrator logging level. Default: INFO |
|
.file.name |
string |
Log file location. Set to: logs/c7-data-migrator.log . If not specified, logs are output to the console. |
The VariableInterceptor
interface allows you to define custom logic that executes whenever a variable is accessed or modified during migration. This is useful for auditing, transforming, or validating variable values.
- Create a new Maven project with the provided
pom.xml
structure - Add a dependency on
c7-data-migrator-core
(scope: provided) - Implement the
VariableInterceptor
interface - Add setter methods for any configurable properties
- Package as JAR and deploy to the
userlib
folder - Configure in
application.yml
Variable interceptor plugins configuration
These plugins can be packaged in JARs and dropped in the userlib folder
migrator.interceptors:
- class-name: com.example.MyCustomVariableInterceptor
- class-name: com.example.AnotherVariableInterceptor
Example of a custom variable interceptor can be find in the ./examples/variable-interceptor directory.
- If multiple interceptors are present, their execution order is determined by the
@Order
annotation (lower values run first). - When the interceptor in not a Spring bean, the default order is used and added to last in the list.
Already Implemented Interceptors
The following interceptors are already implemented in the project:
- DefaultVariableInterceptor
- Handles formatting and migration of primitive and object variables.
- The interceptor is ordered with priority 0 to ensure it runs first.
- DateVariableInterceptor
- Converts Camunda 7 Date variables to Camunda 8 compatible format
yyyy-MM-dd'T'HH:mm:ss.SSSZ
. - Uses by default the timezone of the JVM settings.
- The interceptor is ordered with priority 10 to ensure it runs after the default interceptor.
- To migrate running process instances, the historic process instance must exist.
- You cannot migrate running instances when you have configured history level to
NONE
or a custom history level that doesn't create historic process instances. - The minimum supported history level is
ACTIVITY
.
- You cannot migrate running instances when you have configured history level to
- You need to add an execution listener of type
migrator
to all your start events.
The migrator validates each process instance before migration and will skip instances that fail validation for the following reasons:
-
Missing C8 Process Definition
- If no corresponding C8 process definition is found for the C7 process ID
-
Multi-Instance Activities
- If the process instance has active multi-instance activities
-
Missing Flow Node Elements
- If a C7 process instance is currently at a flow node that doesn't exist in the deployed C8 model
-
Missing Non Start Event
- If a C8 process definition does not have a process level None Start Event, the migrator will skip the instance.
-
Missing
migrator
Execution Listener on Non Start Event- If a C8 process definition does not have an execution listener of type
migrator
on the None Start Event, the migrator will skip the instance.
- If a C8 process definition does not have an execution listener of type
When a process instance is skipped:
- The skipped process instance is logged
- The instance is marked as skipped in the migration database
- You can list skipped instances
- You can retry migration of skipped instances after fixing the underlying issues
-
List Skipped Instances
./start.sh --runtime --list-skipped
-
Retry Skipped Instances
./start.sh --runtime --retry-skipped
-
Common Resolution Steps
- Deploy the missing C8 process definition
- Wait for multi-instance activities to complete
- Ensure all active flow nodes in the C7 process have corresponding elements in the C8 process
- Modify process instance to a supported state
- Async before/after wait states
- C8 does not support asynchronous continuation before or after any kind of wait state. Service-task-like activities are executed asynchronously by default in Camunda 8 - so for example a service task waiting for asynchronous continuation before will be correctly migrated. But if you need to migrate an instance currently waiting asynchronously at other elements in a C7 model, like for example a Gateway, this instance would just continue without waiting in the equivalent C8 model. You might need to adjust your model's logic accordingly prior to migration
- Data changed via user operations
- Data set via user operations like setting a due date to a user task cannot be migrated currently. We plan to address this limitation with [this ticket.
- Message events
- only message catch and throw events are supported for migration
- depending on your implementation, you may need to add a correlation variable to the instance pre migration
- Message and Signal start events
- If your process starts with a message/signal start event, no token exists until the message/signal is received and hence no migration is possible until that moment
- Once the message/signal is received, the token is created and moved down the execution flow and may be waiting at a migratable element inside the process. However, due to how the migration logic is implemented, at the moment the data migrator only supports processes that start with a normal start event. This is to be addressed with this ticket
- Triggered Boundary events
- C7 boundary events do not have a natural wait state
- If the process instance to be migrated is currently at a triggered boundary event in Camunda 7, there may still be a job associated with that event, either waiting to be executed or currently running. In this state, the token is considered to be at the element where the job is created: typically the first activity of the boundary event’s handler flow, or technically the point after the boundary event if asyncAfter is used.
- During migration to Camunda 8, the token will be mapped to the corresponding target element. However, if that element expects input data that is normally produced by the boundary event’s job (e.g. setting variables), this data may be missing in the migrated instance.
- Recommendation: To ensure a consistent migration, allow boundary event executions to complete before initiating the migration.
- There are elements that are supported in C7 but not supported in C8. Please refer to the documentation for more details on element support in C8 and adjust your models accordingly before migration.
- Call Activity
- To migrate a subprocess that is started from a call activity, the migrator must set the
legacyId
variable for the subprocess. This requires propagating the parent variables. This can be achieved by updating the C8 call activity in one of the following ways:- Set
propagateAllParentVariables
totrue
(this is the default) in thezeebe:calledElement
extension element. - Or, if
propagateAllParentVariables
is set tofalse
, provide an explicit input mapping:
- Set
<zeebe:ioMapping> <zeebe:input source="=legacyId" target="legacyId" /> </zeebe:ioMapping>
- To migrate a subprocess that is started from a call activity, the migrator must set the
- Multi-instance:
- Processes with active multi-instance elements can currently not be migrated. We recommend to finish the execution of any multi-instance elements prior to migration.
- Timer events:
- Timer start events: prior to migration, you must ensure that your process has at least one none start event. Processes that only have a timer start event cannot be migrated.
- If your model contains timer events (start and other), you must ensure that no timers fire during the migration process.
- timers with date: ensure the date lies outside the migration time frame
- timers with durations: ensure the duration is significantly longer than the migration time frame
- timers with cycles: ensure the cycle is significantly longer than the migration time frame and/or use a start time that lies outside the migration time frame
- Note that during deployment and/or migration, the timers may be restarted. If business logic requires you to avoid resetting timer cycles/duration, you need to apply a workaround:
- timers with cycles:
- add a start time to your cycle definition that is equal to the moment in time when the currently running C7 timer is next due
- you must still ensure that the start time lies outside the migration time frame
- timers with durations:
- non interrupting timer boundary events:
- switch to cycle definition with a start time that is equal to the moment in time when the currently running C7 timer is next due and add a "repeat once" configuration
- this way, for the first post migration run, the timer will trigger at the start time
- for all subsequent runs, the defined cycle duration will trigger the timer. The "repeat once" instruction ensures it only fires once, similar to a duration timer
- you must still ensure that the start time lies outside the migration time frame
- interrupting boundary and intermediate catching events
- add a variable to your C7 instance that contains the leftover duration until the next timer is due
- in your C8 model, adjust the timer duration definition to use an expression: if the variable is set, the value of this variable should be used for the duration. If the variable is not set or does not exist, you may configure a default duration
- this way, for the first post migration run the variable will exist and the timer will set its duration accordingly
- for all subsequent runs, the variable will not exist and the default duration will be used
- again, you must ensure the leftover duration for the first post migration run lies outside the migration time frame
- non interrupting timer boundary events:
- timers with cycles:
- Variables
- Camunda 8 supported types: documentation
- Camunda 8 variable name restrictions: documentation.
- Variables that do not follow the restrictions will cause issues in FEEL expression evaluation.
- A Date variable (2025-06-20T11:32:06.868) is migrated to C8 in
ms
format (1750419126868). ticket - Variables are serialized. (to be changed)
- XML variable is migrated to JSON string variable. ticket
- Spin XML variable is migrated to XML string variable.
- Variables set into the scope of embedded sub-processes are not supported yet and will be ignored. Will be implemented in this ticket.
- Event subprocess:
- Important limitation during migration: Event subprocesses with interrupting start events can cause unexpected behavior during migration if triggered at the wrong moment. This includes timer, message, and signal start events.
- What can go wrong:
- A task that already ran in Camunda 7 might run again in Camunda 8.
- The process might end up in the wrong state after migration — for example, being one step behind what you see in C7.
- When could it happen:
- This can occur when a process instance is already inside an event subprocess in C7, and the start event of that same subprocess is accidentally triggered again in C8 during migration.
- How to prevent it:
- Don't correlate messages or send signals during migration
- Temporarily adjust timer start events in event subprocesses to ensure they do not trigger during migration:
- See the section on timer events for more details
- If above suggestions are not feasible in your use case make sure service tasks are idempotent — so repeating them does not cause issues.
- Start events
- It is required that a process instance contains a single process level None Start Event to run the data migrator.
- If a process definition only has event-based start events (eg. Message, Timer), it is required to add a temporary None Start Event. This change needs to be reverted after the data migration is completed.
- Example adding a None Start Event:
<bpmn:process id="Process_1fcbsv3" isExecutable="true">
<bpmn:startEvent id="StartEvent_1">
<bpmn:outgoing>Flow_FromEventStartEvent</bpmn:outgoing>
<bpmn:messageEventDefinition id="MessageEventDefinition_1yknqqn" />
</bpmn:startEvent>
<bpmn:sequenceFlow id="Flow_FromEventStartEvent" sourceRef="StartEvent_1" targetRef="ActivityId" />
+ <bpmn:startEvent id="NoneStartEvent">
+ <bpmn:outgoing>Flow_FromNoneStartEvent</bpmn:outgoing>
+ </bpmn:startEvent>
+ <bpmn:sequenceFlow id="Flow_FromNoneStartEvent" sourceRef="NoneStartEvent" targetRef="ActivityId" />
<bpmn:task id="ActivityId">
<bpmn:incoming>Flow_FromEventStartEvent</bpmn:incoming>
<bpmn:incoming>Flow_FromNoneStartEvent</bpmn:incoming>
<bpmn:outgoing>Flow_1o2i34a</bpmn:outgoing>
</bpmn:task>
<bpmn:endEvent id="EndEvent">
<bpmn:incoming>Flow_1o2i34a</bpmn:incoming>
</bpmn:endEvent>
<bpmn:sequenceFlow id="Flow_1o2i34a" sourceRef="ActivityId" targetRef="EndEvent" />
</bpmn:process>
Symptoms: Migrator exits immediately or fails to connect Solutions:
- Verify Java 21+ is installed:
java -version
- Check database connectivity and credentials
- Ensure Camunda 8 is running and accessible
- Review
application.yml
configuration
Symptoms: Instances appear in skipped list Solutions:
- Check that C8 process definitions are deployed
- Verify
migrator
execution listeners are added to start events - Ensure flow nodes exist in both C7 and C8 models
- Review the skipped instances log for specific reasons
Symptoms: Slow migration speed Solutions:
- Tweak
batch-size
in your configuration - Ensure database has sufficient resources
- Check network latency between components
- Monitor system resources (CPU, memory, disk)
Symptoms: Variables not migrated correctly Solutions:
- Check variable name restrictions for C8
- Verify variable types are supported
- Implement your customer variable interceptor if needed
If you encounter issues not covered here:
- Check the logs - Enable
DEBUG
logging for detailed information - Review limitations - Ensure your use case is supported
- Search existing issues - Check GitHub issues for similar problems
- Create an issue - Provide logs, configuration, and steps to reproduce
Enable detailed logging for troubleshooting:
logging:
level:
root: INFO
io.camunda.migrator: DEBUG
io.camunda.migrator.RuntimeMigrator: TRACE
file:
name: logs/c7-data-migrator.log
The migration process consists of three main phases to ensure a successful transition from Camunda 7 to Camunda 8:
- Stop C7 process execution - Prevent new instances from starting during migration
- Migrate your BPMN models using the Migration Analyzer
- Add required
migrator
execution listeners to normal flow start events of C8 models - Adjust C8 models to ensure compatibility with migration limitations
- Test migrated models in C8 environment thoroughly
- Create backup of your C7 database before starting migration
- Deploy C8 process models and resources to the target environment
- Configure the migrator with proper database connections and settings
- Start the migrator and monitor progress through logs
- Verify results in Camunda 8 Operate
- Handle skipped instances by reviewing and addressing validation failures
- Redeploy C8 models if necessary:
- Remove
migrator
execution listeners from C8 models after successful migration - Revert temporary changes in C8 models if necessary
- Migrate process instances to latest version of C8 models
- Remove
- Check migrated instances in Camunda 8 Operate
- Verify variable data has been transferred correctly
- Test process continuation by completing some migrated instances
- Monitor system performance and resource usage
- Validate business logic continues to work as expected
-
Clone the repository:
git clone https://github.com/camunda/c7-data-migrator.git cd c7-data-migrator
-
Build the project:
mvn clean install
-
Find distribution in
assembly/target/
directory
Execute the full test suite:
mvn verify
Run specific test categories:
# Unit tests only
mvn test
# Integration tests only
mvn integration-test
-
Install prerequisites:
- Java 21+
- Maven 3.6+
- Docker (for testing with different databases)
-
Set up IDE (IntelliJ IDEA/Eclipse):
- Import as Maven project
- Configure Java 21 as project SDK
- Install Spring Boot plugin (recommended)
-
Local development database:
# Start PostgreSQL with Docker docker run --name postgres-dev \ -e POSTGRES_DB=camunda7 \ -e POSTGRES_USER=camunda \ -e POSTGRES_PASSWORD=camunda \ -p 5432:5432 -d postgres:17
We welcome contributions to the C7 Data Migrator! Here's how you can help:
- Report bugs - Create detailed issue reports
- Suggest features - Propose new functionality
- Submit code - Fix bugs or implement features
- Improve documentation - Help others understand the tool
- Test and provide feedback - Try the tool and share your experience
See our issue tracker.
- Read the Contributions Guide
- Check existing issues to avoid duplicates
- Discuss major changes in an issue before implementing
- Follow Java coding standards and existing code style
- Write tests for new functionality
- Update documentation when adding features
- Use meaningful commit messages
- Keep changes focused - one feature/fix per pull request
Every source file must contain the license header. See license header template for the exact format required.
- Fork the repository
- Create a feature branch from
main
- Make your changes with tests
- Ensure all tests pass
- Update documentation if needed
- Submit a pull request with a clear description
The source files in this repository are made available under the Camunda License Version 1.0.
- Camunda 8 Documentation - Official Camunda 8 documentation
- Migration Guide - General migration guidance
- Migration Analyzer - Tool for migrating BPMN models
- Community Forum - Get help from the community
- GitHub Issues - Report bugs and request features
Last updated: July 2025