Building A Docker Image

This document introduces Docker images in general and shows how to build a Docker image for FNZ Studio from scratch. While it is possible to follow the instructions in this document, we recommend using the standard FNZ Studio Docker images, which already implement best-practices, and add additional logic on top

Introduction

When setting up the Platform in a containerized environment, there are three main areas that need to be considered:

This article focuses on the "How to deploy" area, as building a Docker image and running FNZ Studio using the image require Platform-specific instructions. The goal is to enable you to deploy and manage instances in a containerized environment.

Traditional Approach vs Containers

In comparison to a traditional deployment on virtual machines, containers are faster and consume fewer resources:

In a traditional setup, multiple virtual machines run on the same infrastructure. This provides isolation between different virtual machines. Each virtual machine runs its own operating system, which then runs a Platform instance. The downside is that virtual machines use a lot of resources, and are difficult to manage.

With an approach based on containers, your FNZ Studio instances are contained in images. These images can then be started directly on top of a container service like Docker. These running images, called containers, provide the same isolation as an approach based on virtual machines, but without the downsides.

Build a Docker Image

A Docker image is a template that can be used to create one or more containers.

Note: This article assumes you are using the Tomcat application server, but the instructions are similar and can also be used for other application servers.

Resources

Before building the Docker image, ensure that you have all the software artifacts available that you want to include in the image; e.g. the WAR file, the extensions, and Packages that have to be deployed at startup.

Directory Structure

The following snippets assume you have the following directory structure which will be used to build the actual Docker image:

Copy
├── Dockerfile
├── build.sh                 # script to build the Appway Docker image
└── resources
    ├── appway-configuration # e.g. installation.properties, jaas.conf
    ├── appway-core          # unzipped Appway ROOT.war file in a subdir called ROOT
    ├── appway-extensions    # mandatory and additional Appway extensions
    ├── appway-packages      # mandatory and additional Packages that have to be deployed during Appway startup
    ├── appway-scripts       # script to start Appway
    ├── embedded-libs        # libraries that are used to customize the application server
    ├── java-configuration   # e.g. JVM options files for Java 21
    └── tomcat-configuration # configuration files for the application server

Discovery Mechanisms

By default, when you run FNZ Studio, there is no mechanism enabled that allows multiple nodes to connect to each other.

The discovery mechanism you should configure depends on how the containers will be managed.

For example, if the containers are managed using Kubernetes, then the KubernetesHazelcastDiscovery extension has to be placed into the /resources/appway-extensions folder so that it is included in the final Docker image. The configuration.xml file has to be placed in the same folder and configured so that the extensions are automatically started. The following example contains the mandatory extensions for FNZ Studio 2026.2.1:

Copy
<?xml version="1.0" encoding="UTF-8"?>
<AdapterConfig xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="adapters_configuration.xsd">
   <!--
   Extensions that are not listed in this file will not be initialized at startup.
   Extension tag attributes:
   - name: the name of the Extension
   - state: 0 stopped (has never been started manually), 1 stopped (has been running before), 2 started
   - startup: 0 preserve state, 1 do not run at startup, 2 run at startup
   - priority: indicates the startup order of the adapter. For example priority="1" is started before priority="4"
   -->
   
   <Adapter name="AppTask" state="2" startup="2" priority="0"/>
   <Adapter name="AppTaskAPI" state="2" startup="2" priority="0"/>
   <Adapter name="ClusterTools" state="2" startup="2" priority="0"/>
   <Adapter name="ComponentExtension" state="2" startup="2" priority="0"/>
   <Adapter name="DefaultIcons" state="2" startup="2" priority="0"/>
   <Adapter name="Functions" state="2" startup="2" priority="0"/>
   <Adapter name="ProcessUsageTracker" state="2" startup="2" priority="0"/>
   <Adapter name="StatisticsCollector" state="2" startup="2" priority="0"/>
   <Adapter name="SystemHealthSensors" state="2" startup="2" priority="0"/>
   <Adapter name="WorkitemFinder" state="2" startup="2" priority="0"/>
</AdapterConfig>

Start Script

Instead of simply running catalina.sh when starting the Docker image, we prefer to run a small script which allows us to customize Tomcat startup. Save this file under resources/appway-scripts/start-appway.sh:

Copy
#!/bin/bash

# set required command line options for Java 17 and greater
export JDK_JAVA_OPTIONS=@/appway/scripts/jvm-options.txt

# start Tomcat
# note: exec is used to replace the current shell so that the process that runs
#       Appway has PID 1 and it can receive the SIGTERM signal when the container is stopped
exec catalina.sh run

JVM Options File

See System Installation: Required command-line options for Java 21 to find the JVM Options file required to run FNZ Studio.

Dockerfile

The Dockerfile is a text file that contains instructions that are used to build a Docker image. The following example can be customized as needed:

Copy
ARG APP_SERVER_IMG
FROM ${APP_SERVER_IMG}

ARG JVM_OPTIONS_FILE

ENV APP_SERVER_HOME=/usr/local/tomcat
ENV NM_DATA_HOME=/appway/data-home

# Set the default locale
ENV LANG=en_US.UTF-8 LANGUAGE=en_US:en

# set application server directory as working directory
WORKDIR ${APP_SERVER_HOME}

# clean application server directories and
# create common webapps dir
RUN rm -rf webapps && \
    mkdir -p /appway/webapps/ROOT

# create data home dir
RUN mkdir -p ${NM_DATA_HOME}

# create appway user
# note: starting with Ubuntu Noble (24.04) there is a new 'ubuntu' user with ID 1000.
#       we have to remove it such that the 'appway' user can take this ID.
RUN userdel -r ubuntu
RUN groupadd appway && \
    useradd -s /sbin/nologin -g appway appway -d /appway

# set file permissions
RUN chown -R appway:appway ${APP_SERVER_HOME}/conf && \
    chown -R appway:appway ${APP_SERVER_HOME}/lib && \
    chown -R appway:appway ${APP_SERVER_HOME}/logs && \
    chown -R appway:appway ${APP_SERVER_HOME}/work && \
    chown -R appway:appway ${APP_SERVER_HOME}/temp && \
    chown -R root:root     /appway/webapps && \
    chown -R appway:appway ${NM_DATA_HOME}

USER appway

# copy Tomcat config (e.g. server.xml, context.xml)
COPY --chown=appway:appway ./resources/tomcat-configuration conf/

# add embedded libraries
COPY --chown=appway:appway ./resources/embedded-libs lib/

# add Appway WAR (exploded)
COPY --chown=root:root     ./resources/appway-core/ROOT /appway/webapps/ROOT/

# add Appway extensions
COPY --chown=appway:appway ./resources/appway-extensions ${NM_DATA_HOME}/extensions/

# add Appway packages
COPY --chown=appway:appway ./resources/appway-packages ${NM_DATA_HOME}/packages/

# add Appway configuration
COPY --chown=appway:appway ./resources/appway-configuration ${NM_DATA_HOME}/conf/

# add Appway scripts
COPY --chown=appway:appway ./resources/appway-scripts /appway/scripts/
COPY --chown=appway:appway ./resources/java-configuration/${JVM_OPTIONS_FILE} /appway/scripts/jvm-options.txt

# start Appway
CMD ["/appway/scripts/start-appway.sh"]

Build the Image

Add the following content to the build.sh file and run it to build the Docker image:

Copy
#!/bin/bash
 
tomcatVersion="11.0.21-jdk21-temurin-noble"
jvmOptionsFile="jvm-options-java-21.txt"
docker build --pull \
            --build-arg APP_SERVER_IMG="tomcat:${tomcatVersion}" \
            --build-arg JVM_OPTIONS_FILE="${jvmOptionsFile}" \
            -t appway \
            .

After the image is built, tag and push it in a private Docker registry that can be accessed to run the Platform, e.g. a repository like Artifactory.

Copy
docker tag appway [private_repository_url]/appway
docker push [private_repository_url]/appway

Running the Platform

There are two alternative ways of running the Platform in a containerized environment. See the dedicated articles for details on each variant: