Clustered Mendix Runtime

Last modified: October 30, 2026

Introduction

This page describes the behavior and impact of running Mendix Runtime as a cluster. Using the cluster functionality, you can set up your Mendix application to run behind a load balancer to enable a failover and/or high availability architecture.

The main feature enabling clustering is Mendix's stateless runtime architecture. This means that the dirty state (the non-persistable entity instances and not-yet-persisted changes) are stored on the client and not on the server. This enables much easier scaling of the Mendix Runtime, as each cluster node can handle any request from the client. The stateless runtime architecture also allows for better dirty state maintainability and better insight in application state.

Clustering Support

Clustering support is built natively into our Cloud Foundry buildpack implementation. This means that you can simply scale up using Cloud Foundry. The buildpack ensures that your system automatically starts behaving as a cluster.

Clustering is also supported on Kubernetes, but you will have to use a StatefulSet.

Cluster Infrastructure

The Mendix Runtime cluster requires the following infrastructure:

This means that a Mendix cluster requires a load balancer to distribute the load of the clients over the available Runtime cluster nodes. It also means that all the nodes need to connect to the same Mendix database, and the files need to be stored on S3 (for details, see the File Storage section below). The number of nodes in your cluster depends on the application, the high availability requirements, and its usage.

Cluster Leader and Cluster Followers

Database synchronization is coordinated across cluster nodes depending on how Mendix Runtime is started:

  • Leader-based startup – This is used when the Runtime is started through the administrative API. This is how most managed deployments start the app: for example, the Cloud Foundry buildpack, SAP BTP, or an on-premises installation managed with the Mendix Service Console. With this mechanism, exactly one node is the cluster leader and the other nodes are cluster followers. The cluster leader initially synchronizes the database with any Domain Model changes and clears persistent sessions after a new deploy; see Cluster Startup below. The cluster leader node is controlled by the com.mendix.core.isClusterSlave custom setting (see Runtime Customization).

  • Leaderless startup – This is used when the Runtime is started directly from a configuration file instead of through the administrative API. Mendix Portable Runtime (previously called Portable App Distribution, or PAD) always runs this way: its start script always launches the Runtime with a configuration file. In this model, there is no cluster leader. Database synchronization is coordinated using a database lock: whichever node acquires the lock performs the synchronization while the other nodes wait. The com.mendix.core.isClusterSlave setting has no effect when using this mechanism and is ignored. Persistent sessions are not explicitly cleared as a separate deploy step in this model; they are left to expire normally, as described in Sessions Are Always Persistent below.

Other Cluster Management Activities

Besides the startup activities described above, Mendix Runtime nodes also perform the following recurring cluster management activities. These run independently of the leader-based or leaderless startup model, and are not tied to a single designated node:

  • Session cleanup handling – removing expired sessions from the database is handled by an arbitrary cluster node
  • Cluster node expiration handling – every node independently removes other cluster nodes that have expired (meaning, not giving a heartbeat for a configured timespan)
  • Background job expiration handling – removing data about background jobs after the information has expired (meaning, older than a specific timespan) is handled by an arbitrary cluster node
  • Unblocking blocked users is handled by an arbitrary cluster node
  • Cleanup of unreferenced files – each node removes its own file documents that were deleted, replaced, or never committed; cleaning up files left behind by a crashed node is handled by an arbitrary cluster node
  • Executing Scheduled Events – scheduled events are executed by an arbitrary cluster node; for details, see Task Queue

Cluster Startup

Individual nodes in a cluster can be started and stopped with no impact on the uptime of the app. However, when you deploy a new version of the app the whole cluster should be restarted, and the database may need to be synchronized with the updated Domain Model, as described in Cluster Leader and Cluster Followers, above.

Once database synchronization has finished, all the cluster nodes become fully functional. If no database synchronization is required, all the cluster nodes become fully functional directly after startup.

File Storage

Uploaded files should be stored in a shared file storage facility, as every Mendix Runtime node should access the same files. Either the local storage facility is shared or the files are stored in a central storage facility such as an Amazon S3 file storage, Microsoft Azure Blob storage, or IBM Bluemix Object Storage.

For more information about configuring the Mendix Runtime to store files on these storage facilities, see Runtime Customization.

After-Startup and Before-Shutdown Microflows

It is possible to configure After-Startup and Before-Shutdown microflows in Mendix. In a Mendix cluster, this means that those microflows are called per node. This lets you register request handlers and other activities. However, doing data changes during these microflows is strongly discouraged, because it might impact other nodes of the same cluster. There is no possibility to run a microflow on cluster startup or shutdown.

Cluster Limitations

Microflow Debugging

While running a multi-node cluster, you cannot predict the node on which a microflow will be executed. Therefore, it is not possible to debug such a microflow execution in a cluster from Mendix Studio Pro. However, you can still debug a microflow while running a single instance of the Mendix Runtime.

Cluster-Wide Locking (Guaranteed Single Execution)

Some apps require a guaranteed single execution of a certain activity at a given point in time. In a single-node Mendix Runtime, this can be guaranteed with a lock which is local to a Java virtual machine (a JVM-local lock). In a cluster, you cannot set a JVM-local lock across the separate JVMs that make up the cluster.

If the activity can be modeled as a microflow or Java action, you can use a Task Queue with a cluster-wide scope and a single thread to guarantee it runs on only one node at a time (see Limitations in Task Queue). Keep in mind that this is an at-least-once guarantee rather than a strict exactly-once guarantee: if the node running the task fails, another node picks it up and reruns it, so the activity must be idempotent (safe to run multiple times with the same parameters). For details, see Behavior If App Stops Unexpectedly in Task Queue.

Mendix Runtime does not provide a cluster-wide locking mechanism for other cases, for example holding a lock for an arbitrary duration around work that cannot be modeled as a queued task. In these cases, you need to use an external distributed lock manager. Keep in mind that locking in a distributed system is complex and prone to failure (for example, through lock starvation or lock expiration).

Dirty State in a Cluster

When a user signs in to a Mendix application and starts going through a certain application flow, the system can temporarily retain some data while not persisting it yet in the database. The data is retained in the Mendix Client memory and communicated on behalf of the user to a Mendix Runtime node.

For example, imagine you are booking a vacation through a Mendix app with a flight, hotel, and rental car. In the first step, you select and configure the flight, in the second one your hotel, in the third your rental car, and in the final step, you confirm the booking and payment. Each of these steps could be in a different screen, but when you go from step one to step two, you would still like to remember your booked flight. This is called the "dirty state." The data is not finalized yet, but should be retained between different requests. Because it is necessary to reliably scale out and support failover scenarios, the state cannot be stored in the memory of one Mendix Runtime node between requests. Therefore, the state is returned to the caller (the Mendix Client) and added to subsequent requests, so that every node can work with that state for those requests.

The following image describes this behavior:

Reading objects and deleting (unchanged) objects from the Mendix database is still a "clean state." Changing an existing object or instantiating a new object will create "dirty state." Dirty state needs to be sent from the Mendix Client to the Mendix Runtime with every request. Committing objects or rolling back will remove them from the dirty state. The same will happen if an instantiated or changed object is deleted. Non-persistable entities are always part of the dirty state.

Only the dirty state for requests that originate from the Mendix Client (both synchronous and asynchronous calls) can be retained between requests. For all other requests—such as scheduled events, web services, or background executions—the state only lives for the current request. After that, the dirty state either has to be persisted or discarded. The reason for only allowing Mendix Client requests to retain their dirty state is that this is currently the only channel that works with actual user input. User input requires more interaction and flexibility with the data between requests. By only allowing these requests to retain their dirty state, the load on the Mendix Runtime and the external source is minimized, and performance is optimized.

The more objects that are part of the dirty state, the more data has to be transferred in the requests and responses between the Mendix Runtime and the Mendix Client. As such, this has an impact on performance. In clustered environments, it is advised to minimize the amount of dirty state to minimize the impact of the synchronization on performance.

The Mendix Client attempts to optimize the amount of state sent to the Mendix Runtime by only sending data that can potentially be read while processing the request. For example, if you call a microflow that gets Booking as a parameter and retrieves Flight over association, then the client will pass only Booking and the associated Flights from the dirty state along with the request, but not the Hotels. Note that this behavior is the best effort; if the microflow is too complex to analyze (for example, when a Java action is called with a state object as a parameter), the entire dirty state will be sent along. This optimization can be disabled via the Optimize network calls app setting.

To reduce the performance impact of large requests and responses, an app developer should be aware of the following scenarios that cause large requests and responses:

  • A microflow that creates a large number of non-persistable entities and shows them in a page
  • A microflow that calls a web service to retrieve external data and convert them to non-persistable entities
  • A page that has multiple microflow data source data views, each causing the state transferred to the Mendix Runtime to handle the microflow

Associating Entities with System.Session or System.User

The $currentSession Session object is available in microflows so that a reference to the current session can easily be obtained. When an object needs to be stored, its association can be set to $currentSession, and when the object needs to be retrieved again, $currentSession can be used as a starting point from which the desired object can be retrieved by association. The associated object can be designed so that it meets the desired needs. This same pattern applies to entities associated with System.User. In that case, you can use the $currentUser User object.

For example, you can add Key and Value members to a Data entity associated with System.Session (and have constants for key values).

The Value values can easily be obtained by performing a find on the Key values of a list of Data instances.

Sessions Are Always Persistent

To support seamless clustering, sessions are always persisted in the database. In previous versions, this was a known performance bottleneck. Mendix now contains optimizations to mitigate this performance hit.

Roundtrips to the database for this purpose are reduced by giving the persistent sessions a maximum caching time of thirty seconds (by default). This means that after signing out of a session, the session might still be accessible for thirty seconds on other nodes of the cluster, but only if that node has handled a previous request on that session just before the logout happened. This timeout can be configured. Lowering it makes the cluster more secure, because the chance that the session is still accessible within the configured time window is smaller. However, this also requires more frequent roundtrips to the database (which impacts performance). Increasing the timeout has the opposite effect. This can be configured by setting SessionValidationTimeout (value in milliseconds).

Persistent sessions also store a last-active date upon each request. To improve this particular aspect of the performance, the last-active date attribute of a session is no longer committed to the database immediately on each request. Instead, this information is queued for an action to run at a configurable interval to be stored in the Mendix database. This action verifies whether the session has not been logged out by another node and whether the last active date is more recent than the one in the database. The interval can be configured by setting ClusterManagerActionInterval (value in milliseconds).