Setting Up for Production

Enabling production mode for a Vaadin application is described in Deploying to Production. If your application uses Collaboration Engine, it requires some extra steps when you take your application to production. These steps are described here.

Before configuring your Vaadin application for production mode, you need to:

  1. Get a license file with an end user quota.

  2. Configure where Collaboration Engine files are stored.

  3. Store the license file in the right location.

  4. Implement a LicenseEventHandler to get notified when the license needs to be updated.

Obtaining a Free "Universal" License File

All Vaadin applications receive a free 10 end user per month quota with a universal license file. If you don’t have a commercial Vaadin subscription (Pro, Prime, or Enterprise), you can download the universal license file.

The name of the license file is ce-license.json. It informs Collaboration Engine that you are entitled to receive a quota of 10 end users per month.

You can upgrade to a commercial license to increase your monthly end user quota. However, a commercial license also requires an active Vaadin commercial subscription (Pro, Prime, or Enterprise).

Obtaining a Commercial License File

If you need to use Collaboration Engine powered features for more than 10 end users / month, please request a commercial license file.

First, you need to have a commercial Vaadin subscription (Pro, Prime, or Enterprise) to request this license. Each Vaadin subscription offers a bundled quota of end users per month, based on the number of development seats. Visit, the official feature page to learn more about the bundled quotas.

To obtain your license, contact our team. You can also request and purchase a larger quota of end users than provided by your commercial Vaadin subscription. End users above your bundled quota are billed at 1 USD (or 1 EUR) per month.

The name of the license file is ce-license.json. It defines how many unique end users can use collaborative features per month and when the license expires.

Configuring File Storage Location

Follow the steps in this section after you receive your license file.

You need to configure which directory should be used by Collaboration Engine to store files by setting the vaadin.ce.dataDir property. In version 2, the only file that Collaboration Engine uses is the ce-license.json, but in upcoming versions can use the directory to store automatic usage statistics, topic data, and so forth.

You can configure the directory path by either:

  1. configuring the data directory in project files or

  2. passing it in as a parameter on the server startup.

If both are present, the server startup parameter is used.

Configuring the Data Directory in Project Files

Storing the path into the project files is a good way if you always deploy your application into the same environment and the folder does not change. You can set the path as a system property in a VaadinServiceInitListener, which runs once on server startup.

Spring Applications

If you have a Spring application, you can register a VaadinServiceInitListener by implementing the interface and annotating the class as a @SpringComponent. In the serviceInit method, you can then set vaadin.ce.dataDir as a system property.

public class MyVaadinInitListener implements VaadinServiceInitListener {

    public void serviceInit(ServiceInitEvent serviceEvent) {


Other Applications

If you have a non-Spring application, you can implement the VaadinServiceInitListener in the same way as seen above, except that you can’t use the @SpringComponent annotation. Instead, you need to register it through the Java Service Provider Interface (SPI) loading facility. You can do that by providing a file, src/main/resources/META-INF/services/com.vaadin.flow.server.VaadinServiceInitListener with a fully qualified class name to your listener as content, for example,

The location of the configuration file

Configuring the Data Directory on Server Startup

For another way, you can pass the data directory as a parameter on server startup. This way assumes that you have already a production-ready build available and want to run the package on the server. Read Deploying to Production to learn more about building your application for production.

Spring Boot Applications

You can set the data directory with the vaadin.ce.dataDir system property for Java, for example as follows on the command line:

java -Dvaadin.ce.dataDir=/Users/steve/.vaadin/collaboration-engine -jar my-app-1.0-SNAPSHOT.jar

Note that the system property should be before the -jar parameter.

Other Servlet Containers

You can pass the data directory parameter as a context parameter to the servlet container. You should refer to your servlet container’s documentation on how to provide it. You need to set the vaadin.ce.dataDir context parameter. The value should be the directory path.

For example, you can pass the context parameter to Jetty as follows:

mvn jetty:run -Dvaadin.ce.dataDir=/Users/steve/vaadin/collaboration-engine/

See Runtime Configuration for more information.

The directory should be both readable and writable by the user running the Vaadin application.

Placing the License File

After obtaining the license file and the data directory, you need to put the license file in that directory on the server. For example, if you configured the folder to be /Users/steve/vaadin/collaboration-engine/, you should place the license file so that the application can read it as /Users/steve/vaadin/collaboration-engine/ce-license.json.

Collaboration Engine uses the file to verify that you have a proper license. The application does not require an internet connection to Vaadin servers to verify the license.

Providing a Data Directory for a Docker Container

It is recommended to provide the data directory to a Docker container on runtime by either using a volume or a bind mount. It is not recommended to copy the license file into the container image, as the data directory is erased every time you deploy a new version of your application.

If you are deploying to a cloud provider, you may not have access to the host file system to be able to make use of bind mounts. Consult the documentation for your cloud provider to get instructions on how to set up and provide a volume to your application.

While a volume is preferred, if you have access to the host’s file system or want to test the Docker image locally, you can do it with a bind mount with the following steps:

  1. Set up a data directory on the host’s file system. For example: /Users/steve/.vaadin/collaboration-engine.

  2. Copy the ce-license.json file into the folder above.

  3. Pick a suitable folder within your Docker image where the container mounts the host folder. For example: /usr/app/ce.

  4. Configure your Dockerfile to start up the server with the vaadin.ce.dataDir parameter pointing to the internal folder. For example CMD java -Dvaadin.ce.dataDir=/usr/app/ce -jar /usr/app/app.jar

  5. Build the Docker image, for example $ docker build --tag my-app . in the project directory.

  6. Start up the Docker container by giving the -v parameter mapping the host folder to the image folder. For example $ docker run --name=myapp -dp 8080:8080 -v /Users/steve/.vaadin/collaboration-engine:/usr/app/ce myapp

When using volumes, you would replace the absolute path to the directory with the name of the volume, for example:

$ docker run --name=myapp -dp 8080:8080 -v myapp-volume:/usr/app/ce myapp

Notifications for Updating the License

The licensing model may cause collaborative features to be disabled for some of your application’s users. To avoid this situation, you need to get a new license if your old license is about to expire, or if your user base increases and the number of end-users exceeds the quota for one month.

To know when to update the license, you need to implement a license event handler for Collaboration Engine. Collaboration Engine can fire the following types of license events, each at most once during the license’s lifecycle:

  1. the first time when exceeding the end-user quota and entering the grace period (more details in Going Over the Quota),

  2. when the grace period ends,

  3. 30 days before the license expires, and

  4. when the license expires.

If you take care of updating the license when events 1 and 3 are fired, the other two events shouldn’t happen at all.

One potential way to handle the event is to send a message to any existing application monitoring system you might have. Another option is to send an email to the relevant people, for example, those who maintain the deployment and those who are responsible of the Collaboration Engine license. You need to ensure that your application notices and handles the events.

The listener can be configured in a VaadinServiceInitListener in the same way as the vaadin.ce.dataDir property, if you’re setting that property in Java code, as described earlier.

The following example is a Spring project, so the VaadinServiceInitListener is registered by adding the @SpringComponent annotation. If you are not using Spring, you can register the service init listener in the same way as described in Configuring the Data Directory in Project Files.

public class MyVaadinInitListener implements VaadinServiceInitListener {
    private static final Logger LOGGER = LoggerFactory

    public void serviceInit(ServiceInitEvent serviceEvent) {
        VaadinService service = serviceEvent.getSource();

        LicenseEventHandler licenseEventHandler = licenseEvent -> {
            switch (licenseEvent.getType()) {
            case GRACE_PERIOD_STARTED:
            case LICENSE_EXPIRES_SOON:
            case GRACE_PERIOD_ENDED:
            case LICENSE_EXPIRED:
            sendEmail("Vaadin Collaboration Engine license needs to be updated",

        CollaborationEngineConfiguration configuration = new CollaborationEngineConfiguration(
        CollaborationEngine.configure(service, configuration);


In the above example, the license event handler logs the event messages using the SLF4J logging API, and sends an email. When it is time to update the license, the message is logged as a warning. If the license is not updated in time, the message is logged as an error. The default event messages provide information of what has happened, how it affects the application, and what is the recommended action to take.

Below is an example implementation of the sendEmail() method. It requires the javax.mail.mail package as a dependency.

private void sendEmail(String subject, String content) {
    // Replace the following information:
    String from = "";
    String password = "*****"; // Read, for example, from encrypted config
                               // file.
    String to = "";
    String host = "";

    Properties properties = System.getProperties();
    properties.put("", host);
    properties.put("mail.smtp.port", "465");
    properties.put("mail.smtp.ssl.enable", "true");
    properties.put("mail.smtp.auth", "true");

    Session session = Session.getInstance(properties, new Authenticator() {
        protected PasswordAuthentication getPasswordAuthentication() {
            return new PasswordAuthentication(from, password);
    try {
        MimeMessage message = new MimeMessage(session);
        message.setFrom(new InternetAddress(from));
                new InternetAddress(to));
    } catch (MessagingException e) {
        LOGGER.error(e.getMessage(), e);
Exception thrown if events are not handled
Collaboration Engine throws an exception in production mode if a configuration has not been defined. The purpose is to make sure that your application handles the events, and to avoid situation where the license expires by accident.

End-User Quota

Your license includes a quota for how many unique end users are supported within a month, for example, a limit of 1.000 end users. Collaboration Engine counts how many end users use its features during each calendar month. The count of end users starts over on the first day of each month.

Definition of an End User

When you use any Collaboration Engine features, you have to provide a UserInfo object with a unique ID.

String userId = "";
String name = "Steve";
UserInfo userInfo = new UserInfo(userId, name);
CollaborationAvatarGroup avatarGroup = new CollaborationAvatarGroup(
        userInfo, "app");

Collaboration Engine records the ID of each user that accesses collaborative features in the ongoing month and counts towards your quota for the current month. Each user ID is counted only once per month.

Going Over the Quota

When you exceed the limit the first time, nothing changes from the end user’s perspective. When that happens, Collaboration Engine starts a 30-day grace period, during which time the quota is ten times bigger. The grace period gives you time to react to exceeding your limit without impacting your application in any way. For example, if you have obtained a license for a 500 end-user quota, your effective quota is 5.000 end-users during the grace period. After 30 days, your effective quota goes back to 500, and you won’t get another grace period until the next billing period.

If the number of users in a month exceeds the 10x quota during the grace period, or the normal quota after the grace period is over, the collaborative features are disabled for the exceeding users. Your application stills continues to work, but CollaborationAvatarGroup only show the user’s own avatar, and forms built with CollaborationBinder do not show edits made by other end-users. The end-users who are registered within the allowed quota have collaborative features available throughout the month.

Consider, for example, a situation where you have a quota for 500 end-users, you have used your grace period, and 520 end-users have used collaborative features this month. The first 500 end users can collaborate throughout the month. Users from 501 through 520 can use the application, but it works as if Collaboration Engine was not in use. They can only see their own presence and edits. When the calendar month changes, counting starts over, and the first 500 end-users again get full access for the whole month.

Entering the Grace Period

The engine enters the grace period when you have a higher demand than expected when obtaining the license. It is recommended that you get a new license with a higher quota to have collaborative features available for all your users before the grace period expires. Contact Vaadin to get a new license file with a higher quota. You can change your quota at any time. When you replace the license file with the new one, Collaboration Engine resets your grace period. If you exceed your new quota in the future, you again receive a 30 day grace period.

Checking for End User Access

You can determine whether a user has access or not by passing a callback to the requestAccess() method in the CollaborationEngine instance. The callback gets an AccessResponse instance as parameter. You can call its AccessResponse::hasAccess() method to find out access. You can use the status to adapt the UI according to whether the end-user can use collaborative features.

For example:

UserInfo userInfo = new UserInfo("", "Steve");
CollaborationEngine.getInstance().requestAccess(userInfo, response -> {
Cache the results
To avoid calling this method multiple times per user, it is suggested to cache the result during the login process, for example, in the session.

Limiting Collaborative Features to Some End Users

Collaboration Engine only counts those end users towards the quota whose UserInfo objects are passed to collaborative features.

You can limit usage to a subset of your users in two different ways:

  1. Only use Collaboration Engine in views that you have restricted with user access. For example, if you only use collaborative features in the admin views, only those users who access the admin views are counted.

  2. Check the details of the user before initializing the collaboration features. For example, by checking the role or privileges or the user, you can decide in code if the users should have collaborative features enabled or not.

An example of how to enable collaboration by checking user permissions:

User userEntity = userService.getCurrentUser();
if (userEntity.getRoles().contains(Role.ADMIN)) {
    UserInfo userInfo = new UserInfo(userEntity.getId(),
            userEntity.getName(), userEntity.getImageUrl());

    CollaborationAvatarGroup avatarGroup = new CollaborationAvatarGroup(
            userInfo, "avatars");