Contents

Deploying JxBrowser applications with Docker

This tutorial demonstrates how to deploy a JxBrowser application using Docker.

Why Docker 

Docker allows you to package a JxBrowser application together with all required system dependencies and run it consistently across environments, including on older Linux distributions that are not officially supported.

In this tutorial, Docker is used to:

  • Run JxBrowser in a headless Linux environment using a virtual X server.
  • Run JxBrowser as a desktop application by connecting the container to the host X server.

Prerequisites 

Getting the code 

You can find a complete Dockerfile and a sample application in our examples repository:

$ git clone https://github.com/TeamDev-IP/JxBrowser-Examples
$ cd JxBrowser-Examples/tutorials/docker

JxBrowser application 

Creating an application 

Use the example project from the repository above, or create your own project with Gradle.

The example uses the following directory structure:

tutorials/docker/
├── Dockerfile      # Builds the Docker image with all required system dependencies
├── startup.sh      # Script to start the application
└── project/        # Gradle project containing the application
    └── build.gradle

Adding the license 

Add the license key to the application. See Adding the license to a project.

Creating a Dockerfile 

Create a Dockerfile that packages the application together with all required system dependencies.

Basic environment configuration 

Use Ubuntu LTS as the base image:

FROM ubuntu:24.04

Chromium requires glibc to work. Therefore, we can’t use Alpine or any other Linux distribution that uses musl or another standard C library.

Configure a non-interactive environment for package installation:

ENV DEBIAN_FRONTEND=noninteractive

Update the package list:

RUN apt update

Installing Java and Chromium dependencies 

Install OpenJDK 17. You can use any other supported JDK if required:

RUN apt install -y openjdk-17-jdk

Install the native system libraries required by Chromium:

RUN apt install -y \
    ca-certificates \
    fonts-liberation \
    libasound2t64 \
    libatk-bridge2.0-0 \
    libatk1.0-0 \
    libatspi2.0-0 \
    libc6 \
    libcairo2 \
    libcups2 \
    libdbus-1-3 \
    libdrm2 \
    libexpat1 \
    libgbm1 \
    libglib2.0-0 \
    libgtk-3-0 \
    libnspr4 \
    libnss3 \
    libpango-1.0-0 \
    libu2f-udev \
    libvulkan1 \
    libx11-6 \
    libxcb1 \
    libxcomposite1 \
    libxdamage1 \
    libxext6 \
    libxfixes3 \
    libxkbcommon0 \
    libxrandr2

If you choose a different base image, install the dependencies required for that distribution as listed in System requirements.

Installing an X11 server 

Chromium requires an X11 server to run. Install Xvfb, a lightweight virtual X server that can be used when no graphical display is available:

RUN apt install -y xvfb

Copying and building the project 

With the base image and dependencies in place, define how the application should be started and then copy the project into the image.

  1. Create a startup.sh script for running the application:
#!/bin/sh

if [ -z "${DISPLAY:-}" ]; then
  Xvfb :0 -screen 0 1920x1080x24+32 &
  export DISPLAY=:0
fi

cd project
./gradlew run

The script checks whether an X server is available via the DISPLAY environment variable:

  • If DISPLAY is not set, it starts a virtual X server (Xvfb) and runs in headless mode.
  • If DISPLAY is set, it starts the application using the X server specified by DISPLAY and runs in desktop mode.

For simplicity, this example runs the application using Gradle. In production, you would typically build a JAR file and run it directly.

  1. Copy the startup script into the image and make it executable:
COPY startup.sh .
RUN chmod +x startup.sh
  1. Copy the Gradle project and build it inside the image:
COPY project/ project
RUN cd project && ./gradlew build
  1. Configure the container entry point:
WORKDIR /
ENTRYPOINT ["sh", "-c", "/startup.sh"]

Building and launching a Docker container 

Build the Docker image and name it jxbrowser:

docker build -t jxbrowser .

Once the Docker image is ready, you can run the application in different ways depending on your use case.

This tutorial demonstrates two modes:

  • Headless mode — suitable for automation, testing, and server-side execution, where no UI is required.
  • Desktop mode — suitable for distributing and running the application as a desktop application with a visible UI.

Headless mode 

In headless mode, the application runs using a virtual X server (Xvfb).

Run the container:

docker run --shm-size=1gb -t jxbrowser

The --shm-size=1gb extends shared memory to 1 GB. By default, Docker limits it to 64 MB, which is not enough for Chromium.

In the sample application, we load Google and print the page title. You should see this in the console output:

Title: Google

Desktop mode 

In desktop mode, the application connects to the X server of the host system, which allows it to behave like a native desktop application. This setup is specific to Linux systems using X11.

In this mode, the application can display browser content in a native window. To do this, add a simple Swing UI to the application:

SwingUtilities.invokeLater(() -> {
    var view = BrowserView.newInstance(browser);
    var frame = new JFrame("Demo App");
    frame.addWindowListener(new WindowAdapter() {
        @Override
        public void windowClosing(WindowEvent e) {
            engine.close();
        }
    });
    frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
    frame.add(view, BorderLayout.CENTER);
    frame.setSize(800, 500);
    frame.setLocationRelativeTo(null);
    frame.setVisible(true);

    browser.navigation().loadUrl("https://google.com");
});

Don’t forget to rebuild the Docker image after modifying the application.

Before running the container, run this command to allow local connections to the X server:

xhost +local:root

Run the container with access to the host X server:

docker run --shm-size=1gb -t \
  -e DISPLAY=$DISPLAY \
  -v /tmp/.X11-unix:/tmp/.X11-unix \
  jxbrowser

This command passes the host DISPLAY value and mounts the X11 socket into the container.

You should see the application window appear on the host desktop: Docker app window

After you finish debugging, restore X server access control:

xhost -local:root

Troubleshooting 

Accessing DevTools via the remote debugging port 

For development and debugging, you can inspect the browser using Chrome DevTools. To do this, enable the remote debugging port in the application:

var engine = Engine.newInstance(
        EngineOptions.newBuilder(HARDWARE_ACCELERATED)
                .addSwitch("--remote-allow-origins=http://localhost:9222")
                .remoteDebuggingPort(9222)
                .build()
);

In Docker, Chromium typically accepts DevTools connections only from localhost inside the container. To access DevTools from the host machine, you can use SSH local port forwarding.

On the host, start the container and publish the SSH port:

docker run -p 2222:22 --shm-size=1gb -t jxbrowser

Open a shell inside the running container:

docker exec -it <container_id> /bin/bash

Replace <container_id> with the ID of the running container. You can find it by running docker ps.

Install and start the SSH server inside the container:

apt install -y openssh-server
service ssh start

Inside the container, create a user for SSH access:

useradd --create-home --shell /bin/bash jxbrowser
passwd jxbrowser

On the host, forward the remote debugging port:

ssh -L 9222:localhost:9222 -p 2222 jxbrowser@localhost

This command creates a tunnel from localhost:9222 on the host to localhost:9222 inside the container. Keep this SSH session open while using DevTools.

On the host, open Google Chrome and load chrome://inspect to access the DevTools.

Kubernetes 

Kubernetes does not support a direct --shm-size option like Docker, so you need to create a memory-backed volume for /dev/shm manually:

spec:
  volumes:
  - name: chromium_shm
    emptyDir:
      sizeLimit: "1Gi"
      medium: Memory
  containers:
  - image: jxbrowser
    volumeMounts:
      - mountPath: /dev/shm
        name: chromium_shm

Summary 

In this tutorial, you learned how to:

  • Build and run a JxBrowser application in Docker.
  • Run in a virtual X server, and use the X server of the host machine.
  • Enable the remote debugging port and access DevTools via SSH port forwarding.