Introduction

jDeploy allows Java developers to deploy desktop applications as native bundles on Macintosh, Windows, and Linux. Unlike other deployment solutions, jDeploy doesn’t require any third party tools (other than OpenJDK), and can build native installers for Mac, Windows, and Linux on any platform. For example, you can build a native Windows installer on Linux or Mac, and vice versa. Applications deployed using jDeploy can also receive updates automatically as they become available, so you can be assured that your users will always be working with the latest version of your application.

jDeploy 6.0 extends beyond GUI desktop applications to support multi-modal deployments. A single package can now include GUI apps, CLI commands, background services, system tray helpers, and MCP servers for AI tool integration. jDeploy also ships with its own MCP server, allowing AI coding agents like Claude Code to create, configure, and publish jDeploy projects directly. See AI Integrations for details.

jDeploy 6.1 adds a manifest-driven uninstaller, local development mode for testing apps without publishing, Java remote debugging via JDWP, and improved platform hardening. All new features are opt-in.

Note
Apps distributed with jDeploy can also be installed as command-line apps using npm, or include CLI commands that are installed to the user’s PATH. See Command-line Distribution and CLI Commands for more information.

GUI

jDeploy provides a graphical user interface that makes it easy to configure your app’s deployment settings such as icons, splash screens, file associations, etc…​ After you’re satisfied with the settings, press "Publish", and it will publish your app so that users can download the latest version.

details tab
Figure 1. The jDeploy GUI allows you to configure and publish your app.

Download Page

When you publish your application using jDeploy, your users will instantly be able to download your app at https://www.jdeploy.com/~YOUR-APP-NAME

swingset download page
Figure 2. Example download page the Java SwingSet demo app.

The download page will present the correct installer for the user’s platform. E.g. if they are on Windows, it will show the Windows installer; if they are on Mac, it will show the Mac installer; and if they are on Linux, it will show the Linux installers.

The user can click "Other platforms…​" to see the installers for other platforms.

swingset download page other platforms
Figure 3. Download page after selecting "Other platforms…​"

The download page includes links for Windows, Mac, and Linux. At time of writing, Linux includes two different download types: .deb, and .tar.gz, but more may be added as there is demand.

Note
You can also use the Github Action to add your installers as artifacts on your app’s release page.

The Installer

The installer that is downloaded from the download page will prompt the user to select relevant installation options, such as whether to add a link in the Dock (Mac only), or the Start Menu (Windows Only), and whether to enable auto-update (default "On").

swingset installer mac
Figure 4. SwingSet2 Installer on Mac

When an app is already installed, the installer shows Update and Uninstall buttons instead of just "Install", making it clear what action will be taken.

If the app includes AI integrations (such as MCP servers, skills, or agents) and at least one supported AI tool is detected on the system, the installer shows an Install AI Integrations checkbox. Checking it opens a dialog where the user can select which AI tools to configure. See AI Integrations for more information about AI tool integration.

After selecting the desired options, the user can press the Install button, which will trigger the installation of your app. This will only take a second, and, when complete, the user will be prompted with a dialog as follows:

swingset success mac
Figure 5. Dialog shown after successful installation.

In this example, clicking on the "Open SwingSet2", will open the app as shown below:

swingset mac
Figure 6. The SwingSet2 Demo application, running on Mac OS

Mac Codesigning and Notarization

When you deploy your app with jDeploy, you don’t need to sign or notarize it as you normally would when deploying a Mac application. jDeploy handles all of this so you don’t need to worry about it. You don’t require any Apple developer certificates; nor do you require an Apple developer account. Mac users running the latest versions of MacOS will be able to download and install your app, by simply downloading it from the jDeploy download page, and running the installer.

If, however, you do have an Apple Developer account, and want to sign and notarize your app, you can do this as well, via a convenient Github action.

Java Versions

jDeploy currently uses Azul’s OpenJDK REST API to download an appropriate JRE, or JDK on demand. You can specify which version of Java your app requires in your jDeploy config file. You can also specify whether your app requires a full JDK, or JavaFX. Deploying JavaFX apps with jDeploy is actually quite painless - as you don’t need to bundle the JavaFX jars or modules with your distribution.

All versions of Java should work. If a new version comes out that isn’t yet in the "drop-down" list of the GUI, you can just enter the version manually in the package.json file.

GitHub Action

You can automate your deployments using the jDeploy Github action. Publish your app as a GitHub release, or using npm’s repository.

Publish to npm and/or GitHub

jDeploy supports hosting your releases on both GitHub (as release artifacts) or npm. When you publish a new version, your app will automatically detect this the next time it is launched, so that you can be sure that your users are always working with your latest version.

Important
Publishing the app to npm requires an NPM account. This takes a few seconds to set up. You can simply type npm adduser and follow the prompts. full instructions on the NPM site. If you already have an account but haven’t logged in on the current machine you would use npm login to login and store the credentials locally.

Git Branch Synchronization

The jDeploy GitHub action will allow you to publish native apps that stay in-sync with changes in a particular branch of your repository. When you commit changes to that branch, the app will automatically receive the update the next time it is launched. This all happens through the magic of Github actions.

Getting Started

Installation

Download and install the jDeploy desktop application for your platform. macOS, Windows, and Linux are supported.

jdeploy installer
Tip
Setup with AI Coding Agents

If you use an AI coding agent like Claude Code, Codex CLI, or Gemini CLI, you can set up jDeploy by simply asking:

Set up jDeploy for this project

The jDeploy installer detects supported AI tools and offers to configure them during installation. Once configured, your coding agent can create, configure, and publish jDeploy projects directly. See AI Integrations for more details.

Main Menu

The jDeploy main menu, shown below, provides 3 main options:

  1. Open Project - Use this if you have already set up your application with jDeploy.

  2. Import Project - Use this if you have an jar file that you would like to deploy.

  3. Create New Project - Use this if you would like to create a new project from scratch.

main menu

Import Project: Convert your existing jar file to a native application

If you already have a jar file (or a project that produces an executable jar file), you should use the "Import Project" option in the main menu to configure jDeploy to deploy your jar file as a native application.

image 2025 03 01 15 39 43 280

Either enter the path to your project’s directory in the provided text field, or press "Select…​" to select it from a file chooser dialog.

Tip
Generally, it is best to select the directory of your project, which is the one containing your pom.xml file, or your build.gradle file.

If you intend to use GitHub actions to automatically deploy your application in response to commit and tag events, then you can check the "Generate GitHub Workflow" checkbox. This will generate a basic workflow in the .github/workflows directory.

Once you have selected your project directory, press "Import"

Note
Creating a jdeploy project will generate a file named package.json in your project’s directory. This file will be used to store the configuration for deploying your application.

This should open the project editor dialog, as shown below:

project editor swingset after import

This dialog allows you to configure your desktop application. You can configure such things as:

  1. App icon

  2. Installer splash screen

  3. Java version

  4. File and URL associations

  5. Display name

  6. Publish targets

Tip
For the smoothest setup experience, you can just open your project in Claude Code and say "Set up jDeploy for this project". See Claude Code Integration for more information.

See [project-editor] for more information about the fields and options available in the project editor.

jDeploy does its best to find your app’s main jar file. You should see the path (relative to project root) in the JAR File field. If this is incorrect, or empty, the first thing you’ll want to do is to correct this. Press the "Select…​" button to select the correct jar file.

Tip
Make sure that you have built your project so that there is a jar file to find!

The name and title fields will also be populated with default values, based on the name of your Jar file. Inspect these and change as desired. The critical fields, for publishing your application, are the Name, Version, Title, and Author fields.

The Name field should be your globally unique project name. If you are publishing to npm, for example, this will be the name of your npm package.

The Title field is the human-readable title of your application. This is what will be displayed in the application menu, and in the installer.

Note
Importing a project will create a package.json file in your project’s directory. This file will be used to store the configuration for deploying your application. You’ll also notice a .jdeploy directory, which is used to store metadata used by the jDeploy desktop application. Additionally, if you selected the "Generate GitHub Workflow" option, you’ll see a .github/workflows directory with a workflow file named jdeploy.yml that will be used to deploy your application to GitHub releases.

Publishing your application

Currently (as of jDeploy 5.0.0), jDeploy supports publishing to the following platforms:

  1. GitHub Releases

  2. npm

To publish your application, you’ll need to configure the Publish Targets section of the project editor dialog. By default, you’ll find that npm is selected as a publish target. If you don’t want to publish to npm, you can uncheck this option.

Generating an npm token

For the purposes of this "Getting started" tutorial, we are going to publish to npm. To do this, you’ll need to have an npm account, and you’ll need to generate a token that you can use to authenticate with npm. You can generate a token by visiting the following URL:

Replace your-username with your npm username.

Click on the "Generate Token" button, to create a new token. You can either choose a granular access token or a class token. Either will work, as long as it has publish permissions.

Copy this token into a safe place. You’ll need it in the next step.

Publishing to npm

Now, back in the jDeploy project editor for your application, press the "Publish" button in the lower right of the dialog.

Important
Before publishing, you should save any changes you’ve made to the configuration by selecting "File" > "Save" from the menu, or simply pressing Ctrl+S (or Cmd+S on macOS).
publish button

You will be prompted to confirm that you want to publish your application, as shown below:

publish confirm

Click "Yes" to proceed.

Next, you’ll be asked to select the npm account you wish to use to publish your application. If you haven’t already configured an npm account, you can do so by pressing the "Add Account" button.

select npm account

Since this is our first time using jDeploy, you’ll need to add an account.

add npm account

The account name field can be any identifier you choose. It doesn’t need to match your npm username. It is just for you to identify the account.

The Token field is where you’ll paste the token that you generated in the previous step.

Note
The token will be stored in your system’s keychain, so you won’t need to enter it again in the future.

Press "Save", to return to the account selection dialog. Then press "Continue" to proceed with publishing your application.

publishing progress dialog

Depending on your npm account settings, you might be prompted to enter a one-time password (OTP) to authenticate. If so, you’ll see a dialog like the one shown below:

one time password prompt

If you have set up your npm account for 2-factor authentication, then you can simply enter the code from your authenticator app. If you haven’t set up 2-factor authentication, then you have two options:

  1. Set up 2-factor authentication for your npm account.

  2. Adjust your account settings to allow publishing without 2-factor authentication.

See the npm documentation for more information on how to set up 2-factor authentication for your npm account.

Once you have entered the OTP, or if you don’t need to enter one, the publishing process will continue.

If all goes well, you should see a dialog like the one shown below:

publish success

You can click on "View Download Page" to download your application. The URL for this download page will remain constant for all future releases of your application, so you can link to it from your website, or share it with your users.

download page
Note
The download page is hosted on jdeploy.com, and provides downloads for your app’s installer, however, it doesn’t actually host your app. The installer that users download from this page will download your app from npm’s repository at install time.

Installing your application

To install your application, simply download the installer for your platform from the download page, and run it. The installer will guide you through the installation process.

E.g. On windows, it will download the installer as an exe file. You can run this directly from your browser’s downloads:

windows installer downloads

On macOS, it will download a .tar.gz file with an .app file inside. You can extract this file and run the .app file to install your application.

Tip
jDeploy also supports DMG file distribution for macOS, but this requires some configuration, as well as set up for code signing. This is not covered in this "Getting Started" tutorial.

Linux provides 3 different options for installation:

  1. .deb package

  2. .bin installer

  3. Command-line installation snippet that you can run in your terminal.

Choose the option that is most appropriate for your system.

The vanilla installer UI is shown below. We’ll cover later, how you can customize this to match your app’s branding.

installer vanilla

On Windows it provides options to add to the Start menu, as well as adding a desktop shortcut. On macOS it will provide an option to add to the Dock instead of the start menu.

Users can select the auto-update settings that they prefer. Recommended to leave it "On" so that the application will automatically download updates for your application when they are available.

Click the "Install" button to proceed with the installation.

This will prompt you with a confirmation dialog as shown below:

install confirmation dialog

Click "Yes" to proceed with the installation.

Tip
By default this says that the application’s home page is at npmjs.com. If you published to GitHub releases, it would, instead, show the URL to your GitHub repository. Later we’ll learn how to change this to point to your own website.
Important
This confirmation step is important to prevent malware, and provide the user with a chance to verify that the software is from a trusted source. The jDeploy installer is signed using the jDeploy’s code signing certificate, so it should be trusted by most systems so that you don’t need to jump through the hoops of code-signing yourself. So the installer is considered trusted, but the user must still confirm that they trust the software that the installer is installing.

Once you have confirmed, the installation will proceed.

If all goes well, you should see a dialog like the one shown below:

install success dialog

Click "Open <Your application name>" to launch your application.

swingset2 main menu

Notice, that the application should also be available via the start menu.

swingset2 start menu

Publishing to GitHub Releases

If you would like to publish your application to GitHub releases, instead of, or in addition to, npm, you can do so by configuring the Publish Settings tab of the project editor dialog.

publish settings tab

Select the "Publish releases to GitHub" checkbox to enable this option. You should also provide the URL to the GitHub repository where you would like to publish your releases.

Important
This GitHub repository needs to be public so that the jDeploy installer is able to download updates from it. You may use a different repository than where your application sources are stored, if you want to keep your sources private. This repository need just be used to publish releases.

Generating a GitHub Personal Access Token

To publish to GitHub releases, you’ll need to generate a personal access token. See the GitHub documentation for details on creating your token. You can use a classic token or a fine-grained token, as long as it has permission to publish releases.

Publishing to GitHub

Once you have configured your GitHub repository and generated your personal access token, you can press the "Publish" button in the project editor dialog to publish your application.

In the project editor, click on the "Publish" button in the lower right of the dialog.

publish button

This will prompt you to confirm that you want to publish your application.

publish github confirm

Click "Yes" to proceed.

Next, you’ll be asked to select the GitHub account you wish to use to publish your application. If you haven’t already configured a GitHub account, you can do so by pressing the "Add Account" button.

publish select github account

If this is your first time publishing, you’ll need to add an account, by pressing "Add a new account". This will open a dialog where you can enter your GitHub username and personal access token.

github add account dialog

You can enter anything you like into the "Account Name" field. This is just for you to identify the account. The "Token" field is where you’ll paste the personal access token that you generated in the previous step.

Press "Save" to save the account, and then press "Continue" to proceed with publishing your application.

While publishing, you’ll see a progress dialog like the one shown below:

github publish progress dialog

If all goes well, you should see a dialog like the one shown below:

github publish success

You can click on "View Download Page" to view the download page for your application. This page will provide a link to download the installer for your application.

Create New Project

If you are starting from scratch, you can use the "Create New Project" option in the main menu to create a new project based on a selection of templates. Some of the templates available include:

  1. swing - A Swing application

  2. javafx - A JavaFX application

  3. codenameone - A Codename One application

  4. fxgl- An FXGL application

All templates currently use Maven as the build system.

create project dialog

Most of the "Create New Project" dialog should be self-explanatory. You can select the template you want to use, and enter the name of your project. The "Group ID" and "Artifact ID" fields are used to generate the Maven coordinates for your project.

You can select either GitHub or npm as a publish target. If you select GitHub, you’ll need to provide the URL to your GitHub repository. If you select npm, you’ll need to specify the name of your npm package. These options can be configured also later in the project editor.

Press "Create Project" to create your new project. Upon completion, the project editor dialog open, allowing you to configure your application, further.

Tip
You can open the project’s directory in Finder/File explorer by selecting "File" > "Open Project Directory" from the menu. open project directory

You can open the project in your preferred IDE.

Deploy as a Web App (CheerpJ)

jDeploy includes support for deploying Java Swing/AWT-based applications as progressive web apps. From the CheerpJ website:

CheerpJ is a WebAssembly-based Java Virtual Machine for the browser. It has extensive compatibility with Java 8 and provides a full runtime environment1 for running Java applications, applets, libraries, and Java Web Start / JNLP applications in the browser without plugins.

This is currently experimental. Limitations include:

  1. Only Swing/AWT UIs are supported currently. No JavaFX.

  2. Some 3rd-party java libraries may not be supported. You can try to build and find out.

Important
Please read the CheerpJ licensing options before deploying your application, to ensure that you are fully compliant.

To preview your app running as a web app, you can press the "Web Preview" button in the project editor dialog. This will build your application as a web app, then open a dialog for you to preview it in your browser, as shown below:

web preview dialog

You can open the web preview in your browser by pressing the "Open in Browser" button.

The following screenshot shows the SwingSet2 demo running as a web app in the browser:

swingset2 in browser

Command-line Usage

If you eschew GUIs and prefer to work on the command-line, the following instructions are for you.

In terminal, navigate to a directory containing an executable .jar file that you would like to publish.

$ jdeploy init

This will generate a package.json file with settings to allow you to publish the app to npm.

The package.json file

After this command completes, you should open the package.json in your text editor to adjust the settings to your requirements. The following is the package.json file for the SwingSet2 app.

{
  "bin": {"swingset2": "jdeploy-bundle/jdeploy.js"},
  "author": "Oracle",
  "description": "The Swing Sampler demo, packaged by jDeploy",
  "main": "index.js",
  "preferGlobal": true,
  "repository": "https://github.com/shannah/swingset2",
  "version": "1.0.5",
  "jdeploy": {
    "jar": "target/swingset2-1.0-SNAPSHOT.jar",
    "javaVersion" : "11",
    "title" : "SwingSet2",
    "javafx" : false,
    "jdk" : false
  },
  "dependencies": {"shelljs": "^0.8.4", "njre": "^0.2.0"},
  "license": "GPLv2+Classpath Exception",
  "name": "jdeploy-demo-swingset2",
  "files": ["jdeploy-bundle"],
  "scripts": {"test": "echo \"Error: no test specified\" && exit 1"}
}

The package.json specification can be found here. The jdeploy object includes additional properties that you can use to customize how your native desktop bundle. It includes the following keys:

jar

The path (relative to the package.json file) to your executable jar file. It the jdeploy init command worked correctly, it should already point to the correct jar file. If it found the wrong file, or the name of the file changes later, you may need to adjust this value.

Type: String

javaVersion

The Java runtime version required to run your app. At time of writing the default is "11". You can also use "8", or "17" here. In theory any version that Azul's API supports can be used here.

Important
This value should not include only the major version. E.g. "11", NOT "11.0". Also, this value should be a string, not an int. I.e. "javaVersion" : "11", NOT "javaVersion" : 11.

Type: String

title

Your app’s title. This is the human-readable name of your app. It will be used on the download page, and also as the app’s file name when downloaded.

Type: String

jdk

Whether the app requires a full JDK to run. If true, then the app will be run with a JDK - not just the JRE. Unless your app actually needs a JDK (i.e. access to javac, etc…​), you should keep this false as the JDK is substantially larger than the JRE.

Type: boolean

javafx

Whether your app requires JavaFX. If true, then the app will run with a JRE/JDK that includes JavaFX.

Type: boolean

Icons and Splash Images

There are three specially-named images that, if placed in the same directory as your package.json, will be used by jDeploy. They are:

icon.png

An icon for your app. If you don’t include this file, it will use a generic jDeploy icon for your app.

Recommended specifications: 512x512 pixels, with transparency.

Note
If you are using JavaFX you should also add your icon to your app’s Stage using stage.getIcons().add(…​) as there is a bug in Ubuntu that will cause the app’s icon to appear blank in the taskbar otherwise.
splash.png/splash.jpg/splash.gif

A splash image to be displayed while your app is loading.

Note
jDeploy uses the -splash:splash.gif CLI argument at runtime to add the splash screen. This apparently causes some issues with JavaFX. Currently, the recommendation for adding splash screens to JavaFX apps is to use a Preloader. Example here.
installsplash.png

A splash/info screen to be displayed in the installer for your app.

launcher-splash.html

A custom HTML splash screen displayed during installation and updates. Allows full customization of the splash screen appearance using HTML and inline CSS. This file should be placed in the same directory as your package.json.

Example:

<!DOCTYPE html>
<html>
<head>
    <style>
        body {
            display: flex;
            justify-content: center;
            align-items: center;
            height: 100vh;
            margin: 0;
            font-family: Arial, sans-serif;
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            color: white;
        }
        .content {
            text-align: center;
        }
        h1 {
            font-size: 3em;
            margin-bottom: 20px;
        }
        .spinner {
            border: 4px solid rgba(255,255,255,0.3);
            border-top: 4px solid white;
            border-radius: 50%;
            width: 50px;
            height: 50px;
            animation: spin 1s linear infinite;
            margin: 20px auto;
        }
        @keyframes spin {
            0% { transform: rotate(0deg); }
            100% { transform: rotate(360deg); }
        }
    </style>
</head>
<body>
    <div class="content">
        <h1>My Awesome App</h1>
        <div class="spinner"></div>
        <p>Installing and updating...</p>
    </div>
</body>
</html>
Note
Since version 5.2.3, the jDeploy installer uses your custom icon.png and launcher-splash.html files during the initial installation process, providing a fully branded experience from the first installation through all updates.

Publish App to NPM

Tip
This section describes how to publish to npm, but you can alternatively publish to Github releases using the jDeploy github action.
Important
In order to publish your app with jDeploy, you need to have an npm account. For instructions on creating your free npm account see this tutorial.

You must be logged into your npm account in the command-line in order for the jdeploy publish command to work. If you already have an npm account you can login with the npm login command. See this tutorial for more details.

Once you have logged into your npm account and configured your package.json file to your liking, you can publish your app to npm with the following command:

$ jdeploy publish

If it completes successfully, you’ll be able to download your app bundles at https://www.jdeploy.com/~YOUR-APP-NAME where YOUR-APP-NAME is the name specified in the name property of your package.json file.

Publishing New Versions

If you make changes to your app, and want to publish a new version, you can increment the version property of your package.json file, and run jdeploy publish again.

Customizing the Download Page

Starting with jDeploy v5.0, you can configure which platforms you want to provide installers for on your app’s download page. This can be configured either in the GUI, on the "Download Page" tab of the project editor, or manually via the jdeploy.downloadPage.platforms property in the package.json file.

There are three different options for platforms:

  1. All - This will provide installers for all supported platforms and architectures: Windows, Mac, and Linux (.deb and .bin). As jDeploy adds support for more architectures, they will automatically be included.

  2. Default - This will provide the default set of installers. jDeploy maintains a set of "default" platforms and architectures. When we release support for a new platform, typically, it won’t be included by default during a preview period. For example, v5.0 adds support for ARM64 builds on Linux and Windows. These builds are not included yet by default. If you want to include these on your download page, you’ll need to either select the "All" option for your download page, or choose your platforms explicitly.

  3. Custom - This allows you to select which platforms and architectures you want to provide installers for.

download page tab
Figure 7. The download page tab allows you to customize which platforms are shown on the download page.

Example package.json for Customizing Download Page

A custom list of platforms can be specified in package.json
{
  ...
  "jdeploy" : {
    ...
    "downloadPage" : {
      "platforms" : [ "windows-x64", "windows-arm64", "mac-x64", "mac-arm64", "linux-x64" ]
    }
  }
  ...
}
All platforms
{
  ...
  "jdeploy" : {
    ...
    "downloadPage" : {
      "platforms" : [ "all" ]
    }
  }
  ...
}
Default platforms
{
  ...
  "jdeploy" : {
    ...
    "downloadPage" : {
      "platforms" : [ "default" ]
    }
  }
  ...
}
Note
If you don’t specify any options for jdeploy.downloadPage or jdeploy.downloadPage.platforms, then it will default to "default".

Available Platforms

The following platforms are currently supported: . windows-arm64 - Windows on ARM64 (e.g. Surface Pro X) . windows-x64 - Windows on x64 (Intel/AMD) . mac-arm64 - Mac on ARM64 (Apple Silicon) . mac-x64 - Mac on x64 (Intel) . mac-high-sierra - A legacy build compatible with MacOS 10.10 to 10.13 . linux-arm64 - Linux on ARM64 (e.g. Raspberry Pi) . linux-x64 - Linux on x64 (Intel/AMD) . debian-arm64 - Debian-based Linux on ARM64 (e.g. Raspberry Pi) . debian-x64 - Debian-based Linux on x64 (Intel/AMD)

Special Platforms

There are two special platform options: . all - This will include all supported platforms and architectures. . default - This will include the default set of platforms and architectures. This is subject to change as jDeploy adds support for new platforms and architectures.

Platform-Specific Bundles

jDeploy 5.1 adds support for platform-specific bundles. This allows you to filter out certain files from being included in the bundle for a specific platform. For example, you might want to include a .dll file in the Windows bundle, but not in the Mac or Linux bundles. In Compose Multiplatform apps, for example, there are sizable platform-specific native libraries that, if included in all bundles, would make the overall download size much larger than necessary. The People in Space demo, bundled for all platforms is 100 MB, but if you exclude the platform-specific native libraries from the non-relevant bundles, the overall download size drops to 40 MB.

This feature has been applied to the jDeploy installer itself, to reduce the size from ~35 MB down to ~4 MB.

Configuring Platform-Specific Bundles

To configure your platform-specific bundles, open the project editor for your project, and click on the "Platform-Specific Bundles" tab.

platform specific bundles tab

Check the "Enable Platform-specific Bundles" checkbox to enable this feature.

If you are publishing to npm, then you’ll need to specify alternate npm package names for each platform-specific bundle.

Ignore Patterns

Notice that there are sub-tabs for each platform, as well as one named "Global", which applies to all platforms. These tabs allow you to specify which files should be included in each platform’s bundle, in a form very similar to .gitignore file syntax. For example, if you want to exclude all files in the com.myapp.native.windows package from your Mac bundle, then you could add the following line to the mac-x64 and mac-arm64 tabs:

com.myapp.native.windows

You can also use "path" notation, if you prefer, using "/" instead of ".", as follows:

/com/myapp/native/windows

A leading slash is helpful to make it explicit that you’re using path notation. Especially when referring to resources in the default package.

Keep File Patterns

You can also provide "keep" patterns, which will override any ignore patterns, by prefixing the line with a !. E.g.

!com.myapp.resources.important

A common way to combine ignore and keep patterns is to ignore a whole namespace, but then keep one or more sub-namespaces.

For example, for your mac bundle, you might want to exclude resources in the com.myapp.native package, but then keep resources in the com.myapp.native.mac package. You could do this as follows:

com.myaapp.native
!com.myapp.native.mac

"Keep" patterns always take precedence over "ignore" patterns. It doesn’t matter if they come before or after the ignore pattern. Platform-specific patterns always take precedence over global patterns.

Wildcards

You can also use wildcards in your patterns. The * wildcard matches any sequence of characters, except for / or . depending on whether you’re using path or package notation.

e.g.

/mylib-*.dll

This would match any file in the default package that starts with mylib- and ends with .dll.

Comments

Lines starting with # are treated as comments and ignored.

# This is a comment

.jdpignore Files

Under the hood, these patterns are stored in files whose names start with .jdpignore in the same directory as your package.json file.

  1. .jdpignore - Global patterns that apply to all platforms.

  2. .jdpignore.win-x64 - Patterns that apply to the windows-x64 platform.

  3. .jdpignore.win-arm64 - Patterns that apply to the windows-arm64 platform.

  4. .jdpignore.mac-x64 - Patterns that apply to the mac-x64 platform.

  5. .jdpignore.mac-arm64 - Patterns that apply to the mac-arm64 platform.

  6. .jdpignore.linux-x64 - Patterns that apply to the linux-x64 platform.

  7. .jdpignore.linux-arm64 - Patterns that apply to the linux-arm64 platform.

The GUI provides a convenient way to edit these files, but you can also edit them directly in your text editor, if you prefer.

Platform-Specific Bundles in package.json

See Platform-Specific Bundle Properties for a description of how to configure platform-specific bundles directly in the package.json file.

File Associations

jDeploy allows you to associate your application with file mimetypes and extensions. For example, you can associate your application with .txt files so that it is listed as one of the options when users right click on a .txt file and select "Open with". Additionally, your application will respond when text files are dropped onto your app icon.

You can add file associations in the "Filetypes" tab of the GUI. Alternatively see File Associations In package.json for a description of how to add the associations directly in the package.json file.

Tip
Consider enabling singleton mode ("singleton": true) when using file associations. This ensures that double-clicking a file activates your existing application window and forwards the file to it, rather than launching a new instance. Use jdeploy-desktop-lib to receive forwarded files. See the Swing or JavaFX deep linking tutorials for complete implementation examples.
filetypes tab

To add a new document type, press the add button button. To remove a document type, press the delete icon button in the corresponding row.

Each file type row includes the following fields:

Extension

The file extension. E.g. txt, html, mp4. Do not include the "dot".

Mimetype

The corresponding mimetype of the extension. You should provide both the extension and mimetype in each row, as some operating systems rely more heavily on one than the other.

Editor

Check this box if your app can edit files of this type. Leave it unchecked if it can only view files of this type.

Custom

Check this box if this is a custom mimetype. This is used on Linux as an indicator that the installer needs to register the mimetype in the system’s mimetype database.

File Associations In package.json

Such file associations can registered by adding the documentTypes property to the jdeploy object. E.g.

...
"jdeploy" : {

    ...
    "documentTypes" : [
      {
        "extension" : "txt",
        "mimetype" : "text/plain",
        "editor" : true
      }, {
        "extension" : "html",
        "mimetype" : "text/html"
      }, {
        "extension" : "jdtext",
        "mimetype" : "application/x-jdeploy-demo-texteditor-jdtext"
      }, ....
    ],
}
...

The documentTypes array may contain zero of more object entries, with the following properties:

extension

The extension of the file type. E.g. txt, html, etc…​

mimetype

The mimetype of the file type. E.g. text/plain, text/html, etc…​

editor

Boolean indicating whether the application can edit the file type. If omitted or false, the application will be regarded as a "viewer" for this file type.

custom

Boolean indicating whether this is a custom file type for your app. This is currently only used for .deb installers so that it knows whether it needs to register the mimetype with the system. Default is false

Directory Associations

Since version 5.3, jDeploy allows you to associate your application with directories and folders. This enables users to open entire directories with your application, making it ideal for project-based workflows, IDEs, build tools, and workspace management applications.

When directory associations are configured, users can:

  • Windows: Right-click on folders to see "Open with {Your App}" in the context menu, or right-click in empty folder space for "Open folder with…​"

  • macOS: Drag and drop folders onto your application icon, or use "Open With" from the right-click context menu

  • Linux: Use "Open With" from the right-click context menu in file managers (GNOME, KDE, etc.)

Configuring Directory Associations

Directory associations can be added in the "Filetypes" tab of the GUI alongside file associations. To add them directly to package.json, include a document type with "type": "directory":

{
  "jdeploy": {
    "documentTypes": [
      {
        "type": "directory",
        "role": "Editor",
        "description": "Open folder as project"
      }
    ]
  }
}

The directory association object supports the following properties:

type

Must be set to "directory" to indicate a directory association.

role (optional)

Either "Editor" or "Viewer". Default is "Viewer". Use "Editor" if your application can modify folder contents, "Viewer" if it only reads.

description (optional)

Human-readable text shown in context menus. Example: "Open as Project", "Open folder as workspace".

icon (optional)

Path to a custom icon file to use for directory associations.

Use Cases

Directory associations are particularly useful for:

  • IDEs and Code Editors: Open project directories

  • Build Tools: Execute builds on project folders

  • File Managers: Browse and manage directory structures

  • Archive Tools: Compress or extract folder contents

  • Project Management Tools: Open workspace directories

Example: Mixed File and Directory Associations

{
  "jdeploy": {
    "title": "My IDE",
    "documentTypes": [
      {
        "extension": "java",
        "mimetype": "text/x-java-source",
        "editor": true
      },
      {
        "extension": "xml",
        "mimetype": "text/xml",
        "editor": true
      },
      {
        "type": "directory",
        "role": "Editor",
        "description": "Open as Project",
        "icon": "resources/folder-icon.png"
      }
    ]
  }
}

Accessing Directories In Java

When a user opens a directory with your application, the directory path is passed just like file paths:

On Windows and Linux, the directory path is passed as an argument in your main() method:

public static void main(String[] args) {
    if (args.length > 0) {
        File path = new File(args[0]);
        if (path.isDirectory()) {
            System.out.println("Opened directory: " + path.getAbsolutePath());
            // Process the directory
        } else if (path.isFile()) {
            System.out.println("Opened file: " + path.getAbsolutePath());
            // Process the file
        }
    }
}

On macOS, use the java.awt.Desktop class with OpenFilesHandler, which handles both files and directories:

import java.awt.Desktop;
import java.awt.desktop.OpenFilesHandler;
import java.awt.desktop.OpenFilesEvent;
import java.io.File;

public class MyApplication {
    static {
        try {
            Desktop.getDesktop().setOpenFileHandler(new FileHandler());
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    public static class FileHandler implements OpenFilesHandler {
        @Override
        public void openFiles(OpenFilesEvent e) {
            for (File file : e.getFiles()) {
                if (file.isDirectory()) {
                    System.out.println("Opened directory: " + file.getAbsolutePath());
                    // Process the directory
                } else {
                    System.out.println("Opened file: " + file.getAbsolutePath());
                    // Process the file
                }
            }
        }
    }
}

Accessing Files In Java

When a user launches your app by opening an associated file type (e.g. by dragging the file on your app icon, or selecting your app in the "Open with" menu), you will likely want to know which file triggered the launch. On Windows and Linux, these file paths are passed as arguments in your main() method. E.g.

On Linux and Windows opened files can be accessed in your main args.
public static void main(String[] args) {

    if (args.length > 0) {
        System.out.println("Received "+args.length+" file arguments");
        for (int i=0; i<args.length; i++) {

            System.out.println("File: "+args[i]);
        }
    }
}

On Macintosh, you need to use the java.awt.Desktop class to be notified when the user opens a file with your app.

Using the FileHandler interface with the Desktop class to be notified when users open files with your app on Mac OS.
import java.awt.*;
import java.awt.desktop.OpenFilesHandler;
import java.awt.desktop.OpenFilesEvent;

public class HelloApplication {
    static {
        try {
            Desktop.getDesktop().setOpenFileHandler(new FileHandler());
        } catch (Exception ex){}
    }

    ...

    public static class FileHandler implements OpenFilesHandler {

        @Override
        public void openFiles(OpenFilesEvent e) {
            System.out.println("Received open files event "+e.toString());
        }
    }

    ...
}

URL Schemes

jDeploy allows you to register your application to open URLs with specific schemes so that, when a user clicks on a link with that URL scheme, your application will be launched.

You can add URL schemes in the URLs tab of the GUI. To add url schemes directly in the package.json file, see URL Schemes in package.json.

Tip
Consider enabling singleton mode ("singleton": true) when using URL schemes. This ensures that clicking a link activates your existing application window and forwards the URI to it, rather than launching a new instance. Use jdeploy-desktop-lib to receive forwarded URIs. See Singleton Apps for details.

For example, you if you specify the "myapp" URL scheme, then you could add a link on your website as follows:

<a href="myapp:some-data">Open My App</a>

When users who have your app installed click on this link, they will be prompted to launch your app. When you app launches, you’ll be able to detect the specific URL that was clicked, so that you can respond appropriately.

The URLs tab is shown here:

urls tab

In the above example, the app would be associated with URLs like "jdtext:xxxxxx".

Adding Multiple URL Schemes

To add multiple URL schemes, you can simply add multiple values separated by commas in the text field.

e.g.

"myapp,podcast,feed"

In this case the app would respond to URLs like "myapp:…​", "podcast:…​", and "feed:…​"

Standard URL Schemes

You can associate your app with both custom schemes, and standard schemes. For a list of standard URL schemes that you might want your app to be able to handle, see this wikipedia entry.

URL Schemes in package.json

Do this by adding a urlSchemes property of the jdeploy object with an array of string schemes.

E.g.:

"jdeploy" : {

  ...
  "urlSchemes" :  ["jdtext"]
  ...
}

With the above configuration in the package.json file, your app would be launched when the user clicks links like "jdtext:…​." in a webpage.

E.g.

Example HTML with link that will open in your app, assuming you have registered the "jdtext" URL scheme.
...
<p>Testing the custom jdtext url scheme.  <a href="jdtext:hello">Click here</a></p>
...

Accessing URLs In Java

When a user launches your app by clicking on a link, you will likely want to know the URL that triggered the launch.

Tip
The recommended approach is to enable singleton mode ("singleton": true) and use jdeploy-desktop-lib to receive URL events. This ensures that clicking a URL activates your existing application window and forwards the URL to it, rather than launching a new instance. See the Swing or JavaFX deep linking tutorials for complete implementation examples.

The following describes the legacy approach without singleton mode.

On Windows and Linux, these URLs paths are passed as arguments in your main() method. E.g.

On Linux and Windows opened files can be accessed in your main args.
public static void main(String[] args) {

    if (args.length > 0) {
        System.out.println("Received "+args.length+" URL arguments");
        for (int i=0; i<args.length; i++) {

            System.out.println("URL: "+args[i]);
        }
    }
}

On Macintosh, you need to use the java.awt.Desktop class to be notified when the user opens a URL with your app.

Using the URIHandler interface with the Desktop class to be notified when users open URLs with your app on Mac OS.
import java.awt.Desktop;
import java.awt.desktop.OpenURIEvent;
import java.awt.desktop.OpenURIHandler;

public class HelloApplication {
    static {
        try {
            Desktop.getDesktop().setOpenURIHandler(new URIHandler());
        } catch (Exception ex){}
    }

    ...

    public static class URIHandler implements OpenURIHandler {

        @Override
        public void openURI(OpenURIEvent e) {
            System.out.println("Received open uri event "+e.toString());
        }
    }

    ...
}

App Permissions

MacOS requires that your app explicitly declares if it needs access to certain system resources, such as the camera, microphone, or file system. If your app needs access to any of these resources, you can declare this in the Permissions tab of the project editor dialog. jDeploy v5.0 introduces a cross-platform abstraction for permissions that allows you to specify which permissions your app needs, and it will correctly configure each platform’s bundle to request these permissions.

Note
Currently only MacOS supports this feature, but support for Windows and Linux will be added in future releases.

You can configure your permissions either in the GUI, in the Permissions tab, or directly in the package.json file using the jdeploy.permissions property.

permissions tab

Example package.json for Permissions

A sample package.json file with permissions configured.
{
  ...
  "jdeploy" : {
    ...
    "permissions": [{
            "name": "microphone",
            "description": "Microphone permissions are required for recording music"
        }],
    ...
  },
  ...
}

The following table shows the permissions that are currently supported, along with their corresponding Info.plist key on MacOS.

Permission Name MacOS Info.plist Key Description

camera

NSCameraUsageDescription

Access to the camera

microphone

NSMicrophoneUsageDescription

Access to the microphone

location

NSLocationUsageDescription

Access to the user’s location

location_when_in_use

NSLocationWhenInUseUsageDescription

Access to the user’s location when the app is in use

location_always

NSLocationAlwaysUsageDescription

Access to the user’s location at all times

contacts

NSContactsUsageDescription

Access to the user’s contacts

calendars

NSCalendarsUsageDescription

Access to the user’s calendars

reminders

NSRemindersUsageDescription

Access to the user’s reminders

photos

NSPhotoLibraryUsageDescription

Access to the user’s photo library

photos_add

NSPhotoLibraryAddUsageDescription

Access to add photos to the user’s photo library

motion

NSMotionUsageDescription

Access to motion and fitness data

health_share

NSHealthShareUsageDescription

Access to share health data

health_update

NSHealthUpdateUsageDescription

Access to update health data

bluetooth

NSBluetoothPeripheralUsageDescription

Access to Bluetooth

bluetooth_always

NSBluetoothAlwaysUsageDescription

Access to Bluetooth at all times

speech_recognition

NSSpeechRecognitionUsageDescription

Access to speech recognition

homekit

NSHomeKitUsageDescription

Access to HomeKit

music

NSAppleMusicUsageDescription

Access to Apple Music

tv_provider

NSTVProviderUsageDescription

Access to TV provider information

video_subscriber

NSVideoSubscriberAccountUsageDescription

Access to video subscriber account information

siri

NSSiriUsageDescription

Access to Siri

face_id

NSFaceIDUsageDescription

Access to Face ID

desktop_folder

NSDesktopFolderUsageDescription

Access to the Desktop folder

documents_folder

NSDocumentsFolderUsageDescription

Access to the Documents folder

downloads_folder

NSDownloadsFolderUsageDescription

Access to the Downloads folder

network_volumes

NSNetworkVolumesUsageDescription

Access to network volumes

removable_volumes

NSRemovableVolumesUsageDescription

Access to removable volumes

file_provider_presence

NSFileProviderPresenceUsageDescription

Access to file provider presence

system_administration

NSSystemAdministrationUsageDescription

Access to system administration

apple_events

NSAppleEventsUsageDescription

Access to Apple events

local_network

NSLocalNetworkUsageDescription

Access to the local network

media_library

NSMediaLibraryUsageDescription

Access to the media library

calendars_write

NSCalendarsWriteUsageDescription

Access to write to the user’s calendars

reminders_write

NSRemindersWriteUsageDescription

Access to write to the user’s reminders

focus_status

NSFocusStatusUsageDescription

Access to the user’s focus status

user_tracking

NSUserTrackingUsageDescription

Access to user tracking

Tip
Not all of these permissions are required. Some pertain to sandboxed applications only, which jDeploy apps are not. (e.g. desktop_folder). They are included in the above list for completeness, should you ever determine that your app needs to request it.

Each permission entry may include the following properties:

name

The name of the permission. This must be one of the supported permission names listed in the table above.

Type: String

description

A description of why your app needs this permission. This description will be shown to the user when they are prompted to grant the permission.

Type: String

Run as Administrator

jDeploy 5.2 adds support for configuring applications to run with elevated privileges. This is configured using the jdeploy.runAsAdministrator property in package.json.

Configuration Modes

"disabled" (default)

Applications run with normal user privileges. No elevated launchers are generated.

"allowed"

Generates both standard and administrator launcher variants. Users can choose which launcher to use. This is the recommended setting when your application sometimes needs elevated access but can function normally without it.

"required"

All launchers are configured for elevation. No standard launchers are created. Use this only when your application requires administrator access to function.

Platform Implementations

Windows

Uses the native "Run as Administrator" option with User Account Control (UAC) prompt.

macOS

Creates a wrapper launcher app with elevated permissions. Users authenticate with their password when launching.

Linux

Uses pkexec for PolicyKit-based privilege escalation.

Example Configuration

{
  "jdeploy": {
    "runAsAdministrator": "allowed"
  }
}

Security Best Practices

  • Request elevation only when necessary

  • Use "allowed" rather than "required" when the application can function without elevation

  • Document clearly why elevation is needed

  • Validate input in elevated applications

Singleton Apps

jDeploy 6.0 introduces singleton mode, which ensures that only one instance of your GUI application runs at a time. When the user launches the app again (by double-clicking a file, clicking a custom URL, or running the launcher command), the existing window is activated and the new file or URI is forwarded to it via IPC.

Enabling Singleton Mode

To enable singleton mode, set "singleton": true in your jdeploy configuration:

{
  "jdeploy": {
    "singleton": true,
    "urlSchemes": ["myapp"]
  }
}

You can also enable this in the GUI on the Build tab by checking the "Singleton Application" checkbox.

jdeploy-desktop-lib

To receive forwarded files, URIs, and activation events in your running application, you need to add the jdeploy-desktop-lib dependency and register a JDeployOpenHandler.

Swing Applications

Add the dependency to your pom.xml:

<dependency>
    <groupId>ca.weblite</groupId>
    <artifactId>jdeploy-desktop-lib-swing</artifactId>
    <version>1.0.2</version>
</dependency>

Then register your handler:

import ca.weblite.jdeploy.app.swing.JDeploySwingApp;
import ca.weblite.jdeploy.app.JDeployOpenHandler;

public class MyApp {
    public static void main(String[] args) {
        JDeploySwingApp.setOpenHandler(new JDeployOpenHandler() {
            public void openFiles(List<File> files) {
                // Handle files opened by the user
                for (File file : files) {
                    openDocument(file);
                }
            }
            public void openURIs(List<URI> uris) {
                // Handle custom URL schemes (e.g., myapp://settings)
                for (URI uri : uris) {
                    handleDeepLink(uri);
                }
            }
            public void appActivated() {
                // Bring window to front when user tries to launch again
                mainWindow.toFront();
            }
        });

        // ... rest of your application startup
    }
}

JavaFX Applications

Add the dependency to your pom.xml:

<dependency>
    <groupId>ca.weblite</groupId>
    <artifactId>jdeploy-desktop-lib-javafx</artifactId>
    <version>1.0.2</version>
</dependency>

Then register your handler.

Important
On macOS, you must call JDeployFXApp.initialize() in your main() method before calling Application.launch(). This ensures the AWT Desktop handlers are registered while AWT is still the primary toolkit. Without this, deep links won’t be received on macOS.
import ca.weblite.jdeploy.app.javafx.JDeployFXApp;
import ca.weblite.jdeploy.app.JDeployOpenHandler;

public class MyApp extends Application {
    public static void main(String[] args) {
        JDeployFXApp.initialize();  // Required before launch() on macOS
        launch(args);
    }

    @Override
    public void start(Stage primaryStage) {
        JDeployFXApp.setOpenHandler(new JDeployOpenHandler() {
            public void openFiles(List<File> files) { /* handle files */ }
            public void openURIs(List<URI> uris) { /* handle URIs */ }
            public void appActivated() { /* bring window to front */ }
        });

        // ... rest of your application startup
    }
}
Important
Without jdeploy-desktop-lib, your app will not receive forwarded URIs or file-open events from subsequent launches.

Integration Scenarios

Singleton mode and jdeploy-desktop-lib enable several powerful integration patterns:

  • Service-to-GUI: A background service (see Services) hosts a web dashboard. Links on the dashboard use myapp:// URLs to open views in the native GUI app without spawning duplicates.

  • Tray helper shortcuts: Helper actions in the system tray use custom URL schemes to deep-link into specific screens of the running GUI app.

  • File associations: When the user double-clicks a file associated with your app, the file is routed to the existing instance rather than launching a new one.

  • Web-to-desktop handoff: A web application redirects to a myapp:// URL to hand off a workflow to the native desktop app.

Note
Singleton mode applies on Windows and Linux. macOS handles single-instance behavior natively through its Desktop API.

CLI Commands

jDeploy 6.0 allows you to define named command-line tools that are installed to the user’s PATH alongside your GUI application. When invoked, the launcher skips GUI initialization, connects stdin/stdout/stderr to the terminal, and forwards exit codes. Your Java main() receives only user-supplied arguments—all launcher internals are stripped.

Basic Configuration

Define CLI commands in the jdeploy.commands section of your package.json:

{
  "jdeploy": {
    "commands": {
      "myapp-cli": {
        "description": "Run My App in command-line mode"
      }
    }
  }
}

You can also configure commands in the GUI on the CLI Commands tab.

Detecting CLI Mode

Your application can detect that it’s running as a CLI command by checking the jdeploy.mode system property:

public class Main {
    public static void main(String[] args) {
        String mode = System.getProperty("jdeploy.mode", "gui");

        if ("command".equals(mode)) {
            // CLI mode: args contains only user-supplied arguments
            runCli(args);
        } else if ("npx".equals(mode)) {
            // Launched via npm/npx (Since 6.1)
            runNpxMode(args);
        } else {
            // GUI mode
            SwingUtilities.invokeLater(() -> new MainWindow().setVisible(true));
        }
    }
}

Since 6.1, applications launched via npm/npx receive jdeploy.mode=npx, allowing you to distinguish between native launcher execution and npm/npx execution. This is useful when certain features (like native launcher-specific capabilities) are not available in npx mode.

Command Arguments

Each command entry can include an args array of additional arguments that the launcher passes to your Java main() before any user-supplied arguments. This is useful when you have multiple commands that share the same JAR but need to distinguish which command was invoked:

{
  "jdeploy": {
    "commands": {
      "myapp-cli": {
        "description": "Run in CLI mode",
        "args": ["-Dapp.role=cli"]
      },
      "myapp-server": {
        "description": "Start the server",
        "args": ["-Dapp.role=server", "-Dserver.port=8080"]
      }
    }
  }
}
Note
The app.role property in this example is application-defined, not a jDeploy property. You can use any property names or argument patterns that suit your application.

Command Implementations

Commands can implement special behaviors by adding an implements array:

Implementation Description

service_controller

Adds service lifecycle commands (install, start, stop, status, uninstall). See Services.

updater

Adds an update subcommand for self-updating. The update logic runs entirely in the native launcher. The trigger argument can be customized with the updateTrigger property (default: "update"). (Since 6.1)

uninstaller

Adds an uninstall subcommand that performs a clean removal of the application. Reads the XML uninstall manifest written during installation and removes all installed files, services, PATH entries, registry keys (Windows), and shell profile exports (Linux/macOS). The trigger argument can be customized with uninstallTrigger (default: "uninstall"). (Since 6.1)

launcher

Creates a CLI shortcut that opens your GUI application (launches with jdeploy.mode=gui). Arguments are passed as files or URLs to open.

Implementations can be combined. For example:

{
  "jdeploy": {
    "commands": {
      "myapp-server": {
        "description": "Web server for My App",
        "implements": ["service_controller", "updater"]
      }
    }
  }
}

This gives the command both service management and self-update capabilities.

Uninstaller (Since 6.1)

The uninstaller implementation gives users a clean way to remove your application. When a user runs <command> uninstall, the launcher reads the XML uninstall manifest written during installation and removes all installed artifacts in a controlled sequence:

  1. Detects running GUI instances and warns the user

  2. Stops helper processes and background services

  3. Deletes all files tracked in the uninstall manifest

  4. Cleans up runtime directories (JRE caches, package caches, lock files)

  5. Removes PATH and shell profile entries (Unix) or registry PATH entries (Windows)

  6. Self-deletes the launcher binary

Path deletion is restricted to known safe directories to prevent accidental damage.

{
  "jdeploy": {
    "commands": {
      "myapp-cli": {
        "description": "My App CLI",
        "implements": ["uninstaller"]
      }
    }
  }
}

By default, the uninstaller prompts for interactive confirmation. Pass --jdeploy:interactive=false to skip the prompt for automation.

Custom Trigger Arguments (Since 6.1)

The updater and uninstaller implementations use "update" and "uninstall" as their default trigger arguments. You can customize these with the updateTrigger and uninstallTrigger properties:

{
  "jdeploy": {
    "commands": {
      "myapp-cli": {
        "description": "My App CLI",
        "implements": ["updater", "uninstaller"],
        "updateTrigger": "upgrade",
        "uninstallTrigger": "remove"
      }
    }
  }
}

With this configuration, myapp-cli upgrade triggers self-update and myapp-cli remove triggers uninstallation.

Launcher Mode

The launcher implementation is special—it launches your app in GUI mode rather than command mode:

{
  "jdeploy": {
    "commands": {
      "myapp": {
        "description": "Open files in My App",
        "implements": ["launcher"]
      }
    }
  }
}

Users can then run:

myapp document.txt           # Opens file in the GUI app
myapp https://example.com    # Opens URL in the GUI app
Note
The launcher implementation is mutually exclusive with other implementations since it launches in gui mode, not command mode.

Constraints

  • Branch installations do not support CLI commands. Only GUI mode works for branch installs.

  • Command names must match ^[A-Za-z0-9][A-Za-z0-9._-]*$ (max 255 characters).

  • Command args cannot contain shell metacharacters (;|&$()`).

Example Project

For a working example, see the jdeploy-hello-commands project, a bare-bones app with a CLI command alongside its GUI.

Services

jDeploy 6.0 allows you to deploy CLI commands as background services managed by the operating system’s native service manager (systemd on Linux, launchd on macOS, Windows Service Manager on Windows).

Service Controllers

To make a CLI command work as a service, add service_controller to its implements array:

{
  "jdeploy": {
    "commands": {
      "myapp-server": {
        "description": "Web server for My App",
        "implements": ["service_controller"]
      }
    }
  }
}

This adds service lifecycle commands that users can run:

myapp-server service install    # Register with system service manager
myapp-server service start      # Start the service
myapp-server service status     # Check if running
myapp-server service stop       # Stop the service
myapp-server service uninstall  # Unregister from service manager

myapp-server --port=9090        # Run directly as a CLI command (not as service)

Your Java application just needs to start its server logic and stay alive—the launcher handles all service registration and lifecycle management.

macOS SMAppService Integration (Since 6.1)

On macOS 13 (Ventura) and later, services can use Apple’s SMAppService API for Launch Agent management instead of manually placing plist files. During the build, jDeploy generates embedded LaunchAgent plist files inside the macOS app bundle for commands that have fully static arguments.

Enable this per command with embedPlist:

{
  "jdeploy": {
    "commands": {
      "myapp-server": {
        "description": "Background server",
        "implements": ["service_controller"],
        "embedPlist": true
      }
    }
  }
}

This provides a more native service management experience on modern macOS, where Login Items appear in System Settings and users have explicit visibility and control over background services.

Background Helper (System Tray)

When your application includes services, jDeploy can install a system tray application called the Background Helper. This provides a convenient UI for managing services and accessing quick actions.

Helper Actions

Configure helper actions in the jdeploy.helper.actions array:

{
  "jdeploy": {
    "commands": {
      "myapp-server": {
        "description": "Web server",
        "implements": ["service_controller"]
      }
    },
    "helper": {
      "actions": [
        {
          "label": "Dashboard",
          "description": "Open the web dashboard",
          "url": "http://localhost:8080/dashboard"
        },
        {
          "label": "Settings",
          "description": "Open app settings in the GUI",
          "url": "myapp://settings"
        }
      ]
    }
  }
}

You can also configure helper actions in the GUI on the Helper Actions tab.

System Tray Menu

The Background Helper displays a system tray icon with a context menu that includes:

  • Service status and controls (Start, Stop, Restart)

  • Your custom helper actions

  • View logs option

  • Open main application

  • Start on login checkbox

  • Quit

Helper actions can open:

  • HTTP/HTTPS URLs (e.g., http://localhost:8080/dashboard)

  • Custom URL schemes (e.g., myapp://settings)—useful for deep-linking into your GUI app

  • File paths

Tip
When using custom URL schemes with helper actions, enable "singleton": true in your jdeploy config so that clicking a link activates the existing GUI window instead of launching a new instance. See Singleton Apps.

Example Project

For a working example, see the jdeploy-service-example project, a Quarkus REST service deployed with jDeploy as a background service with a system tray helper.

AI Integrations

jDeploy 6.0 provides comprehensive support for AI tool integration. This includes:

  • jDeploy’s built-in MCP server: Allows AI coding agents to create, configure, and publish jDeploy projects

  • Deploy your app as an MCP server: Register your application with AI tools during installation

  • Skills and Agents: Install Claude Code skills and agents alongside your application

jDeploy’s Built-in MCP Server

jDeploy itself ships with an MCP server that AI coding agents can use directly. When you install jDeploy, the installer detects which AI tools you have installed (Claude Desktop, Claude Code, VS Code Copilot, Cursor, Codex CLI, Windsurf, Gemini CLI, and others) and lets you choose which ones to integrate with.

Once connected, your AI coding agent can:

  • Create new jDeploy projects from scratch, including generating the package.json configuration and GitHub Actions workflow

  • Set up distribution on existing projects—configure build settings, CLI commands, services, MCP servers, permissions, and download page options

  • Publish projects—build and publish releases to npm or GitHub directly from the agent

For example, in Claude Code you can say:

Set up jDeploy for this project with a CLI command and a background service

And the agent will analyze your project, configure the appropriate settings, and guide you through publishing.

Deploying Your App as an MCP Server

You can also register your own application as an MCP server that AI tools discover and use automatically. During installation, jDeploy configures the MCP server in the user’s AI tool configurations.

Configuration

The MCP server is backed by a CLI command defined in jdeploy.commands, so jdeploy.mode is command when running as an MCP server. The ai.mcp section references that command and controls installation behavior:

{
  "jdeploy": {
    "commands": {
      "myapp-mcp": {
        "description": "MCP server for AI integration",
        "args": ["--mcp-mode"]
      }
    },
    "ai": {
      "mcp": {
        "command": "myapp-mcp",
        "args": ["--verbose"],
        "defaultEnabled": true
      }
    }
  }
}
Property Description

command

The name of the CLI command (defined in jdeploy.commands) that runs the MCP server

args

Additional arguments passed when the AI tool invokes the MCP server

defaultEnabled

Whether the MCP server is auto-enabled during installation

You can also configure MCP settings in the GUI on the AI Integrations tab.

Java Detection

Your application can detect MCP mode by checking the arguments:

public static void main(String[] args) {
    String mode = System.getProperty("jdeploy.mode", "gui");

    if ("command".equals(mode)) {
        if (Arrays.asList(args).contains("--mcp-mode")) {
            // MCP server mode: communicate via stdin/stdout using MCP protocol
            startMcpServer();
        } else {
            runCli(Arrays.asList(args));
        }
    } else {
        SwingUtilities.invokeLater(() -> new MainWindow().setVisible(true));
    }
}

The MCP server communicates via stdin/stdout using the Model Context Protocol. The launcher injects the static args from both the command spec and the ai.mcp section before any user-supplied arguments.

Supported AI Tools

Tool MCP Skills Agents Auto-Install

Claude Desktop

Yes

No

No

Yes

Claude Code

Yes

Yes

Yes

Yes

Codex CLI

Yes

Yes

No

Yes

VS Code (Copilot)

Yes

No

No

Yes

Cursor

Yes

No

No

Yes

Windsurf

Yes

No

No

Yes

Gemini CLI

Yes

No

No

Yes

OpenCode

Yes

No

No

Yes

Warp

Yes

No

No

No

JetBrains IDEs

Yes

No

No

No

Skills and Agents

Beyond MCP servers, jDeploy can also install Claude Code skills and agents. Place them in your project’s jdeploy-bundle/ai/skills/ and jdeploy-bundle/ai/agents/ directories respectively. They are discovered during installation and configured in the user’s Claude Code settings.

Example Project

For a working example, see the jdeploy-mcp-example project, a Quarkus MCP server deployed with jDeploy.

Claude Code Integration

jDeploy integrates with Claude Code and other AI coding agents, making it easier than ever to configure your project to use jDeploy.

jDeploy’s Built-in MCP Server (6.0+)

jDeploy 6.0 ships with its own MCP server that AI coding agents can use directly. When you install jDeploy, the installer detects which AI tools you have installed and lets you choose which ones to integrate with.

Once connected, you can simply ask your AI coding agent to set up jDeploy:

Set up jDeploy for this project with a CLI command and a background service

The agent will analyze your project, configure the appropriate settings, set up the GitHub Actions workflow, and guide you through publishing—all without leaving your editor.

See AI Integrations for the full list of supported AI tools and detailed configuration options.

CLAUDE.md Integration

When you import your existing project into jDeploy, it will append instructions to your CLAUDE.md file to help Claude complete the setup. This provides context about your jDeploy configuration that Claude can use when assisting with your project.

The workflow is:

  1. Open jDeploy

  2. Select "Import Existing Project". This creates a default package.json file, a vanilla GitHub workflow, and adds jDeploy instructions to your CLAUDE.md file.

  3. Open Claude Code in your project and say "Setup jDeploy for me". Claude will analyze your project and make any necessary changes to your build files, prompting you for approval.

Adding Claude Instructions to Existing Projects

If you’ve already set up jDeploy with your project and want to add Claude integration, the project editor has a menu option for appending (or creating) jDeploy’s instructions to your CLAUDE.md file.

claude integration menu

Homepage Verification

When users install your app, they will be prompted with a warning dialog to remind them to only install software from trusted sources. They are encouraged to review the app’s homepage to ensure that they trust the developer before proceeding with the installation. The dialog will look something like:

trust prompt dialog

By default it will report the software’s verified homepage to be the npmjs.org package page for your app. If you want this to report your actual website or GitHub repository, you’ll need to enter your website URL into the "Homepage" field on the "Details" tab, and then verify ownership.

homepage field

To verify ownership of this page, you’ll need to copy and paste your app’s SHA256 hash code into your website. You can begin this process by pressing the "Verify" button beside the Homepage text field. It will display a dialog like the following:

verify homepage dialog

Click on the SHA256 hash code in this dialog to have it copied to the clipboard. Then paste it somewhere in your webpage. This can be hidden inside a meta tag, or just placed into the text of the page. As long as the jDeploy installer can make an HTTP request to your homepage and find the code amongst the page content.

Important
The sha256 hash code is generated based on the package name and the homepage URL, so if you change the homepage URL, the hash will change, and you’ll have to re-verify.

Once you have added the code to your homepage, press the "Verify" button so that jDeploy can check it. If everything looks good, you’ll see a success dialog like the following:

homepage verified
Important
This verification step is just a sanity check to ensure that you’ve pasted the code correctly. It doesn’t store this verification anywhere, and the jDeploy installer will perform its own check at install time whenever a user installs your app. This means that you shouldn’t remove the code after verification. You should leave it there permanently - or at least as long as you want your installers to be able to verify to your homepage.

Command-line Distribution

Apps distributed using jDeploy can also be installed in the command-line using npm. When distributing in this way, you’ll want to pay attention to the bin and name properties of your package.json file.

name

This is the name of our project as it will be listed in the NPM registry. This name must be unique. If you try to publish a project with a name that is already registered by someone else, it will fail. This name will be used by users who want to install your app. E.g. If "name"= "hello-world", then people would install my app by typing "npm install hello-world -g" at the command prompt.

bin

This specifies the name of the command that will be installed to activate your app. It actually maps an alias to the "jdeploy-bundle/jdeploy.js" script, which is thin bootstrap shell script for your java app. The entry will look like "bin" : {"hello-world" : "jdeploy-bundle/jdeploy.js"}. If you want to change your app’s command name to "my-hello-world", then you would just change "hello-world" to "my-hello-world".

You don’t need to do anything special to allow your app to be installed via the command-line. If you have published it using the jdeploy publish command, then, assuming your package name is "hello-world", users will be able to install your app by typing:

$ npm install hello-world -g

Some things to note:

  1. On Mac and Linux you’ll need to use sudo as it adds a symlink to /usr/local/bin which requires sudo permissions. On Windows it will work without sudo as long as you are using an Administrator account.

  2. If you omit the -g it will install the app locally (i.e. for use inside the current working directory). When installed in this way, you need to use the npx command to run your app, and it will only work when run inside the install directory.

They can then launch your app with:

$ hello-world

or, if they installed it locally, they would instead run it with:

$ npx hello-world

See Getting Started with jDeploy for Command-line apps for step-by-step instructions in deploying your command-line app.

Runtime Arguments

jDeploy allows you to configure JVM options, system properties, and program arguments for your application in the Runtime Args tab of the GUI, or the args property of the jdeploy object in the package.json file.

When working in the GUI, you should enter the arguments one per line in the provided text area. jDeploy supports a small set of placeholder variables that you can include in your arguments, that will be replaced at runtime. It also supports a minimal syntax for making platform-specific arguments, which are used on some platforms and not others.

runtimeargs tab
Figure 8. A sample Runtime Args tab demonstrating how to set a few JVM options, properties, and program arguments.

The above arguments would be saved in the package.json file as follows:

{
    ...
    "jdeploy": {
        ...
        "args": [
            "-Dmyapp.foo=bar",
            "-Xmx2G",
            "-D[mac]application.support.dir={{ user.home }}/Library/Application Support",
            "-D[mac]resources.dir={{ app.path }}/Contents/Resources",
            "gui"
        ],
        ...

JVM Options

JVM options are options that configure how the JVM works. They are always marked with the "-X" prefix. Some examples of commonly-used JVM options include:

-XmsXXX

Sets the minimum and initial size of the heap. E.g. -Xms2G. See the Java documentation for more information.

-XmxXXX

Sets the maximum size of the heap. E.g. -Xmx4g. See the Java documentation for more information.

System Properties

You can set system properties using the -D prefix. E.g. -Dmyapp.setting=foobar

Program Arguments

Arguments that are not prefixed with -X or -D are treated as program arguments. They will be passed to your program, and you can receive them in your main(String[] args) method.

Placeholder Variables

jDeploy supports placeholder variables and expressions that can be embedded in your run arguments and configuration file paths. These are replaced with the appropriate values at runtime. Placeholder variables are marked with {{ …​ }}.

Basic Placeholders
{{ user.home }}

The path to the user’s home directory.

{{ executable.path }}

or {{ exe.path }}:: The full path to the launcher executable.

{{ executable.dir }}

or {{ exe.dir }}:: The directory containing the launcher executable.

{{ app.path }}

The path to the .app bundle on macOS. On other platforms, same as {{ executable.path }}.

{{ app.dir }}

The directory containing the .app bundle on macOS. On other platforms, same as {{ executable.dir }}.

{{ pathsep }}

The platform-specific path separator (/ on Unix, \ on Windows).

Dynamic Placeholder Expressions (Since 5.3)

jDeploy 5.3 adds support for dynamic expressions within placeholders, enabling environment variable access, conditional logic, and cross-platform path construction.

Environment Variable Access

Access environment variables directly:

{{ env.APPDATA }}
{{ env("JAVA_HOME") }}
{{ env("CONFIG_DIR", "/default/path") }}  // With default value

Coalesce Operator

Provide fallback values using the ?? operator:

{{ env.APPDATA ?? user.home }}
{{ env.XDG_CONFIG_HOME ?? path(user.home, ".config") }}
{{ env.VAR1 ?? env.VAR2 ?? "fallback" }}

String Concatenation

Combine values using the + operator:

{{ user.home + "/Documents" }}
{{ "MyApp - User: " + env.USERNAME }}

Cross-Platform Path Function

Build platform-native paths automatically:

{{ path(user.home, "Documents", "MyApp") }}
{{ path(env.APPDATA ?? user.home, "MyApp", "config.json") }}

Results: - Unix: /Users/john/Documents/MyApp - Windows: C:\Users\john\Documents\MyApp

Path Normalization

Normalize paths with mixed separators:

{{ normalize("C:/mixed\\slashes/path") }}
{{ normalize(user.home + "/docs") }}

Conditional Blocks

Include content conditionally based on whether a value exists:

{{ if env.DEBUG }}--verbose{{ end }}
{{ if env.MODE }}{{ env.MODE }}{{ else }}production{{ end }}
{{ if env.CUSTOM_PATH }}{{ env.CUSTOM_PATH }}{{ else }}{{ user.home }}/default{{ end }}

Conditions are truthy if the value is non-empty, falsy if empty or undefined.

Example Usage

Platform-specific configuration file paths:

{
  "jdeploy": {
    "configFileMac": "{{ path(user.home, 'Library', 'Application Support', 'MyApp', 'config.json') }}",
    "configFileWin": "{{ path(env.APPDATA, 'MyApp', 'config.json') }}",
    "configFileLinux": "{{ path(env.XDG_CONFIG_HOME ?? path(user.home, '.config'), 'MyApp', 'config.json') }}"
  }
}

Runtime arguments with environment variables:

{
  "jdeploy": {
    "args": [
      "-Dconfig.dir={{ env.XDG_CONFIG_HOME ?? path(user.home, '.config') }}",
      "-Dapp.mode={{ if env.DEBUG }}debug{{ else }}release{{ end }}",
      "-Ddata.path={{ path(user.home, 'Documents', 'MyApp') }}"
    ]
  }
}

Platform-Specific Arguments

In some cases you may want a setting to only be applied on a specific platform. For example some JVM options may be mac specific. jDeploy provides a minimal syntax for marking an argument as platform-specific. For system properties, you would add a condition after the -D prefix, so instead of -Dfoo=bar, you would have -D[CONDITION]foo=bar where CONDITION is one or more platform names ("mac-x64", "mac-arm64", "linux", "win") delimited by pipes. Some examples:

-D[mac]foo=bar

Same as -Dfoo=bar, but only on Mac.

-D[win]foo=bar

Same as -Dfoo=bar, but only on Windows.

-D[win|linux]foo=bar

Same as -Dfoo=bar, but only on Windows and Linux.

A similar syntax is used for platform-specific JVM options: -X[CONDITION]value, where CONDITION is one or more platform names ("mac-x64", "mac-arm64", "linux", "win") delimited by pipes. Some examples:

-X[mac]ms2G

Same as -Xms2G, but only on Mac.

-X[mac|linux]mx4G

Same as -Xmx4G, but only on Mac and Linux.

Finally, for program arguments (i.e. arguments that are passed to your main(args) method), you can mark them as platform-specific by adding a -[CONDITION] prefix. E.g. -[mac]anArgOnlyAddedOnMac. Some more examples:

-[mac]foo

Same as foo, but only on Mac.

-[linux|win]foo

Same as foo, but only on Linux and Windows.

File Descriptor Limits (Unix/Linux/macOS)

Since version 5.2.3, applications can configure file descriptor limits on Unix-like systems (macOS and Linux) using the -Djdeploy.file.limit system property. This is useful for applications that need to open many files or network connections simultaneously.

Configuration

Add the file descriptor limit to your runtime arguments in package.json:

{
  "jdeploy": {
    "args": [
      "-Djdeploy.file.limit=8192"
    ]
  }
}
Platform-Specific Configuration

Use platform-conditional syntax to set different limits per platform:

{
  "jdeploy": {
    "args": [
      "-D[mac]jdeploy.file.limit=16384",
      "-D[linux]jdeploy.file.limit=8192"
    ]
  }
}

Or target Unix-like platforms together:

{
  "jdeploy": {
    "args": [
      "-D[mac|linux]jdeploy.file.limit=8192"
    ]
  }
}
Limit Value Use Case

4096

Light file I/O applications

8192

Moderate database/network applications

16384

Heavy concurrent connection applications

Technical Details
  • The limit is set before JVM startup by the launcher

  • Cannot exceed your system’s hard limit (check with ulimit -Hn on Linux/macOS)

  • Windows: No operation performed (Windows uses a different mechanism for handle limits)

  • Non-fatal: If setting the limit fails, a warning is logged and the application continues to launch

  • The -Djdeploy.file.limit property is processed by the launcher and not passed to the JVM as a system property

Use Cases

This feature is particularly useful for applications that:

  • Handle many concurrent network connections

  • Process large numbers of files simultaneously

  • Use databases with connection pooling

  • Experience "Too many open files" errors on Unix-like systems

Runtime Configuration Files

Since version 5.2, jDeploy supports loading additional runtime arguments from external JSON configuration files. This allows environment-specific configuration without repackaging the application.

Configuration

Specify a configuration file path in package.json using the configFile property:

{
  "jdeploy": {
    "configFile": "{{ user.home }}/.myapp/config.json"
  }
}

Platform-specific configuration files take precedence over the general configFile:

configFileMac

Configuration file used on macOS. Takes precedence over configFile.

configFileWin

Configuration file used on Windows. Takes precedence over configFile.

configFileLinux

Configuration file used on Linux. Takes precedence over configFile.

Configuration File Format

Configuration files use JSON format with an args array containing all runtime arguments:

{
  "args": [
    "-Xmx4G",
    "-Xms2G",
    "-Dmyapp.setting=value",
    "-Danother.property={{ user.home }}/data",
    "--verbose",
    "--output={{ app.dir }}/logs"
  ]
}

The args array can contain:

  • JVM options (starting with -X)

  • System properties (starting with -D)

  • Program arguments (passed to your application’s main(String[] args) method)

Placeholders

Configuration files support the same placeholder variables and expressions as package.json arguments (see Placeholder Variables), including:

  • Basic placeholders: {{ user.home }}, {{ executable.path }}, {{ app.dir }}, etc.

  • Environment variables: {{ env.VARNAME }}, {{ env("VAR", "default") }}

  • Coalesce operator: {{ env.APPDATA ?? user.home }}

  • Path functions: {{ path(user.home, "Documents") }}

  • Conditional blocks: {{ if env.DEBUG }}--verbose{{ end }}

Example configuration file using dynamic expressions:

{
  "args": [
    "-Xmx4G",
    "-Dconfig.path={{ path(env.XDG_CONFIG_HOME ?? path(user.home, '.config'), 'myapp', 'config.json') }}",
    "-Dcache.dir={{ path(env.TMPDIR ?? env.TEMP ?? '/tmp', 'myapp-cache') }}",
    "{{ if env.DEBUG }}--verbose{{ end }}"
  ]
}
Argument Order

Arguments from configuration files are appended after arguments defined in package.json. This allows package.json to provide default settings while configuration files override or extend them.

Error Handling

If a configuration file is specified but not found, the application launches normally using only the package.json arguments. No error is raised.

Limitations
  • Platform-conditional syntax (-D[mac]property=value) is not supported in configuration files

  • Configuration files cannot reference other configuration files

  • The launcher sets jdeploy.configFile system property to the resolved configuration file path (if loaded)

jDeploy-Injected System Properties

jDeploy 6.0 injects a comprehensive set of system properties into the JVM that your application can use to detect its runtime environment:

Property Description

jdeploy.mode

Runtime mode: gui for GUI applications, command for CLI commands (including services and MCP servers)

jdeploy.launcher.path

Absolute path to the launcher executable

jdeploy.app.version

Application version from package.json

jdeploy.app.name

Application package name

jdeploy.app.source

Package source URL (npm registry URL or GitHub URL)

jdeploy.prerelease

true if this is a prerelease/snapshot version

jdeploy.updatesAvailable

true if updates are available (checked at launch)

jdeploy.background

true if running in background mode (as a service)

jdeploy.config.file

Path to the runtime configuration file (if loaded)

Example usage:

public class Main {
    public static void main(String[] args) {
        String mode = System.getProperty("jdeploy.mode", "gui");
        String version = System.getProperty("jdeploy.app.version", "unknown");
        boolean updatesAvailable = Boolean.parseBoolean(
            System.getProperty("jdeploy.updatesAvailable", "false")
        );

        if ("command".equals(mode)) {
            runCli(args);
        } else {
            if (updatesAvailable) {
                showUpdateNotification();
            }
            launchGui();
        }
    }
}

See CLI Commands for more information about mode detection and multi-modal applications.

Publishing as Web App (CheerpJ)

jDeploy includes experimental support for deploying Java Swing/AWT-based applications as progressive web apps. From the CheerpJ website:

CheerpJ is a WebAssembly-based Java Virtual Machine for the browser. It has extensive compatibility with Java 8 and provides a full runtime environment1 for running Java applications, applets, libraries, and Java Web Start / JNLP applications in the browser without plugins.

This is currently experimental. Limitations include:

  1. Only Swing/AWT UIs are supported currently. No JavaFX.

  2. Some 3rd-party java libraries may not be supported. You can try to build and find out.

Important
Please read the CheerpJ licensing options before deploying your application, to ensure that you are fully compliant.

To enable CheerpJ support, you should add a "cheerpj" property to the "jdeploy" object in the package.json file. E.g.:

"jdeploy": {
       ...
        "cheerpj": {
            "githubPages": {
                "branchPath": "{{ branch }}",
                "tagPath": "app",
                "branch": "gh-pages",
                "enabled": true
            },
            "enabled": true
        },
    },
...

The above configuration will be used by the github action to publish a web application via github pages.

The following properties, pertaining to Cheerpj, are available.

jdeploy.cheerpj

Boolean value indicating whether the github action should attempt to build the app as a web app using CheerpJ.

jdeploy.cheerpj.githubPages

Object with configuration properties for GitHub pages.

jdeploy.cheerpj.githubPages.enabled

Whether to publish to GitHub pages. Boolean.

jdeploy.cheerpj.githubPages.branch

The branch to use for publishing to GitHub pages. This should match the branch that is configured for GitHub pages on GitHub.

jdeploy.cheerpj.githubPages.branchPath

The path within the pages site where the app should be published for "branch" commits. You can use the {{ branch }} placeholder to include the name of the branch. It will be replaced by the name of the branch on which the commit occurred.

jdeploy.cheerpj.githubPages.tagPath

The path within the pages site where the app should be published for "tag" releases. You can use the {{ tag }} placeholder to include the name of the tag in the path. It will be replaced by the name of the tag.

jdeploy.cheerpj.githubPages.path

Optional. If you want branch commits and tag commits to be published to the same path, then you can use this to set them both in one place.

Tip
The jDeploy IntelliJ Plugin includes a GUI form for configuring CheerpJ in the jDeploy Settings dialog.
cheerpj tab

Building Bundles Locally

Important
When building your bundles locally, you still need to ensure that you run jdeploy publish to publish your app on npm, as the launcher relies on your published package for the automatic updates feature.

The easiest way to distribute your app is to just share the link to its download page at https://www.jdeploy.com/~your-app-name, however, you can optionally just generate your bundles locally as part of the jdeploy package step (which is included when you run jdeploy publish).

There are two flavors of packages that you can create:

bundles

The .exe, .app, etc.. bundles that launch your app directly.

installers

The installers that you can distribute to your users which install your .exe/.app, etc…​ bundles.

If you want to distribute your app to users, the installers provide a better experience since they include options to, for example, add your app to the programs menu (Windows/Linux) or the Dock (Mac).

You can generate bundles via the bundles property of the jdeploy object in your package.json file. E.g.

{
  ...
  "jdeploy" : {
    ...
    "bundles": ["mac-x64", "mac-arm64", "win", "linux"],
  }

}

With this setting the jdeploy package command (and jdeploy publish) will generate your app bundles in the jdeploy/bundles directory. If you only wanted to generate a "mac" bundle for Intel macs, you would do:

"bundles" : ["mac-x64"]

To generate installers, you can add the "installers" property. E.g.

"installers" : ["mac-x64", "mac-arm64", "win", "linux"]

This would result in installer bundles being generated in the jdeploy/installers directory.

Note
The installers generated in this way may differ slightly from the installers available on your download page. This is because the download page uses a different mechanism for generating the packages. Additionally, the download page may provide some installer types that aren’t available locally.

Publishing on GitHub

Important
You must publish your releases to a public repository, or your app installer won’t be able to download updates. If your repository is private, you can use the target_repository parameter for the GitHub action to publish your installers to a different repository that is public.

As of jDeploy 4.0, you can deploy your application on GitHub packages instead of npm. When deploying bundles in this way, you do not require an npm account. Some of the features of this deployment style include:

  1. GitHub actions integration to automate deployments on push/release/tag.

  2. Publish app artifacts in the GitHub release itself.

  3. Branch-App Synchronization: Distribute app bundles that are automatically synchronized with a specific branch of your repo.

  4. Multiple Streams: Distributed multiple versions of your app (e.g. "dev", "stage", "production") that are synchronized to different branches of your repository.

  5. Still includes support for jDeploy Download page. In addition to publishing apps as artifacts of GitHub releases, your bundles will still be available at jdeploy.com on your download page.

Publishing Releases with GitHub Actions

Tip
The jDeploy IntelliJ Plugin provides a project wizard that includes project templates for Codename One, Swing, and JavaFX, which are plug-and-play, including full support for publishing releases using GitHub actions.

To publish your packages on GitHub, you’ll need to add the following to your workflow.

- name: Publish with jDeploy
  uses: shannah/jdeploy@master
  with:
    github_token: '${{ github.token }}'

This should be placed in a step after you have built your app already (e.g. using maven, gradle, ant, etc…​).

If this action is used in a workflow on a branch push, then it will publish artifacts on a release named after the branch that the workflow is running on. We recommend configuring your workflow to only trigger on branches ending in -snapshot (e.g. main-snapshot) and tags beginning with v (e.g. v1.0.0) to avoid creating unwanted tags like "main".

If this action is used in a workflow on a tag push, then it will add the app bundles as artifacts directly on the release/tag in GitHub.

Important
All jobs involving the jDeploy action should use a repository-wide concurrency group to prevent concurrent releases from interfering with each other:
concurrency:
  group: jdeploy-release-${{ github.repository }}
  cancel-in-progress: false
Tip

You can add a working workflow to your app using the jDeploy GUI, by selecting "File" > "Add Github Workflow"

github add workflow

Your App Download Page

The URL convention for the jDeploy download page for apps hosted on GitHub is:

https://www.jdeploy.com/gh/YOUR_GITHUB_ACCOUNT/YOUR_REPO_NAME

E.g. The SwingSet app whose repository is located at https://github.com/shannah/swingset2, will have download page at https://www.jdeploy.com/gh/shannah/swingset2.

To access the download page for a specific branch you can append the branch name to the URL. E.g. The download for the "Swing Set 2 (master)" app, which is synchronized with the "master" branch, the URL would be:

https://www.jdeploy.com/gh/shannah/swingset2/master

Distributing Mac App as a DMG image

By default, MacOS apps are distributed as an installer, which is signed and notarized by jDeploy, so you don’t need to sign it with your own Apple developer certificate. However, if you would prefer to sign the app with your own certificate, you can distribute it as a DMG instead of an installer, you can do this via the jDeploy DMG GitHub action.

The IntelliJ plugin project templates include a DMG job in the GitHub workflow by default, but you do need to define a configuration variable and some secrets in your repository.

For full instructions, see the README.

Publishing Releases for Private Repositories

Your release artifacts must be published to a public repository in order for the jDeploy installer to be able to download updates for your app. If your repository is private, you can publish your releases in a different repository which is public.

Step One: Create a Public Repository for your Releases

For example, consider a scenario where your app is in the private repository yourname/yourapp. Then you could create a new public repository at yourname/yourapp-releases.

Important
Make sure you make at least one commit to this repo before publishing releases to it, or GitHub won’t allow you to add a release. E.g. Add a readme file to the repo.

Step Two: Create a Personal Access Token

In order to publish releases to a different repository, you’ll need to create a personal access token that has permission to create releases in the other repository.

Once you’ve created the personal access token, you’ll need to add it as a secret in your repository.

Step Three: Specify the target_repository in your workflow

Finally, you’ll need to modify the invocation of the jdeploy GitHub action to use the target_repository parameter, and change the github_token parameter to use your personal access token instead of the default github_token.

- name: Publish with jDeploy
  uses: shannah/jdeploy@master
  with:
    github_token: '${{ secrets.YOUR_PERSONAL_ACCESS_TOKEN }}'
    target_repository: 'yourname/yourapp-releases'

The next time your run the workflow, it should publish the releases to the yourapp-releases repository instead of the yourapp repository.

File Description

icon.png

The icon for your app

installsplash.png

The splash screen to display in the installer.

package-info.json

A JSON file including all of the package information about your app.

The "jdeploy" tag

This action will add (and update) a tag named "jdeploy" to your repo, with a file named "package-info.json". This tag must not be deleted. The package-info.json file is used by both the jDeploy launcher and installer obtain details about your app, so that it knows what versions are available.

The jDeploy JavaFX Starter Project Template

If you’re starting out with a new JavaFX project, then the jdeploy-javafx-starter may be what you’re looking for. It provides you with a bare-bones JavaFX maven project, based on the official openjfx javafx-archetype-simple archetype, but it comes loaded with a GitHub workflow to auto-generate native installers for all branches and releases.

Getting started is as simple as pressing "Use this template", and you’ll be all set.

JetBrains Runtime (JBR) Support

Applications can use JetBrains Runtime as their JRE provider, enabling access to JCEF (Java Chromium Embedded Framework) and enhanced rendering capabilities. JBR is the same runtime used by IntelliJ IDEA and other JetBrains IDEs.

To use JBR, specify the jdkProvider and jbrVariant properties in your package.json:

{
  "jdeploy": {
    "javaVersion": "21",
    "jdkProvider": "jbr",
    "jbrVariant": "jcef"
  }
}

JBR Variants

Variant Description

standard

JBR without JCEF

jcef

JBR with JCEF (recommended for GUI applications)

sdk

JBR SDK without JCEF (for development)

sdk_jcef

JBR SDK with JCEF (for development with embedded browser)

Use Cases

  • Applications requiring embedded browsers: JCEF provides Chromium-based browser embedding

  • Enhanced rendering: JBR includes improved HiDPI support and rendering optimizations

  • IDE-like applications: Same runtime used by IntelliJ IDEA and other JetBrains IDEs

  • Advanced GUI features: Better support for complex graphical applications

Note
JBR does not include JavaFX. When using JBR with JavaFX applications, set javafx: true in your package.json to automatically download JavaFX via Maven.

Appendix: package.json Reference

jDeploy uses the package.json file to store all of its configuration details for a project. When you run jdeploy init it will create a default package.json file in the current working directory with default settings.

Note
For full documentation on the package.json file and its standard properties, see the npm package.json documentation.

Most of the jDeploy-related settings are located inside the "jdeploy" object. E.g.

{
    ...
    "jdeploy" : {
        "jar" : "dist/my-app.jar"
        ...
    }
}

However, there are a few properties at the "root" level that will affect how jDeploy deploys your application.

Main Root-Level Properties

The following standard properties are important to jDeploy.

Property Description Required Default

name

The name used to install your app from NPM. This must be unique in the NPM registry. E.g. If your "name" is

Yes

bin

An map containing the binaries for your application. This must contain at least one entry whose key is the name of your app as it will be called on the command-line, and value is "jdeploy-bundle/jdeploy.js"

Yes

dependencies

Must include a dependency on the "shelljs" library. "dependencies": {"shelljs": "^0.8.4"}

Yes

For other root-level properties see npm package.json documentation

Basic jDeploy Object Properties

The following properties should be placed inside the "jdeploy" object of the package.json, and are almost always used.

Property Description Type Required Default

buildCommand

Optional build command that can be used to build the project before packaging. This is typically used for JavaFX projects that are built using Maven or Gradle. E.g. "buildCommand": ["mvn", "package"]

Array<String>

No

None

javaVersion

The Major java version to use for your app’s runtime. E.g. "8", "11", "17", "21", etc..

String

No

"17"

jdk

Whether the app requires the full JDK to run. true or false

boolean

No

false

javafx

Whether the app requires JavaFX to run. true or false

boolean

No

false

javafxVersion

Optional JavaFX version to use. If you don’t specify this, then it will just use whatever version comes with the ZuluFX Java distribution.

String

No

None

jdkProvider

(Since 5.4) The JDK provider to use for the Java runtime. Supported values: "zulu" (default), "jbr" (JetBrains Runtime). When set to "jbr", you can also specify the jbrVariant property.

String

No

"zulu"

jbrVariant

(Since 5.4) The JetBrains Runtime variant to use when jdkProvider is set to "jbr". Supported values: "standard", "jcef", "sdk", "sdk_jcef". The jcef variant includes JCEF (Java Chromium Embedded Framework) and is recommended for GUI applications requiring embedded browsers.

String

No

"standard"

jar

Relative path to the executable jar file that contains your app. You should supply either "war" or "jar", but not both. This value may be the path to a file, or a glob pattern (e.g. using * and ?) that can be used to match a file.

String

No

title

The human-readable title of your app. This is used in the download page, and in the app and installer names.

String

No

A modified version of the package name.

war

Relative path to a war file, or web-app directory that contains your web app. You should supply either "war" or "jar", but not both. This value may be the path to a file, or a glob pattern (e.g. using * and ?) that can be used to match a file.

String

No

Advanced jDeploy Object Properties

The following properties may be placed in the "jdeploy" object of the package.json file.

Property Description Type Required Default

args

List of runtime arguments. See Runtime Arguments.

Array<String>

No

None

bundles

List of the platforms to build bundles for when you run jdeploy package. E.g. ["mac-x64", "mac-arm64", "win", "linux"]. Bundles will be built in the jdeploy/bundles directory. Not related to the bundles available on the download page. See Building Bundles Locally.

Array

No

None

files

List of copy rules for copying files into the deployed application bundle. By default, for executable jar deployments, it will copy the jar file and all files listed in the jar’s class path (from its manifest file); and for war deployments, it will just copy what is inside the war file. If you need additional files to be deployed (e.g. native libs or resources), then you should specify them in this list. See Copy Rules.

Array

No

documentTypes

Array of file and directory association objects. Supports both file extensions (via extension and mimetype properties) and directory associations (via type: "directory"). See File Associations and Directory Associations.

Array

No

urlSchemes

Array of URL schemes to associate with your app. See URL Schemes.

Array

No

installers

List of the platforms to build installers for when you run jdeploy package. E.g. ["mac-x64", "mac-arm64", "win", "linux"]. Installers will be built in the jdeploy/installers directory. Not related to the bundles available on the download page. See Building Bundles Locally.

Array

No

None

downloadPage

(Since 5.0) Object with configuration settings for the download page at jdeploy.com. See Customizing the Download Page. These settings also affect which bundles are added to GitHub releases.

Object

No

None

permissions

(Since 5.0) In order to access some system resources, your app may need to request special permissions. This is an array of objects {"name": "<permission-name>", "description": "<why your app needs it"} that indicate whether your app needs the corresponding permission. Currently these are only used for MacOS builds, but they are generic abstractions, and may be used by other platforms in the future. See App Permissions.

Array of Objects

No

None

jdeployVersion

Optionally specify the version of jDeploy that should be used for building bundles and installers.

String

No

None

port

For war deployments, this specifies the port that the Jetty wrapper should bind to. This can be overridden at runtime using the -Djdeploy.port or -Dport command-line flags, or via the PORT environment variable. If this option is omitted, and no port is specified at runtime, it will use "0" as the port, which will cause it to bind to a random open port.

int

No

0

antFile

Set the ant build file to use for the preCopyTarget and postCopyTarget options.

String

No

build.xml

preCopyTarget

You can optionally specify ANT to run a target before copying files to the jdeploy-bundle directory. You can specify the ant build script that contains the target using the antFile property.

String

No

postCopyTarget

Optional ANT target to be run after files have been copied to the jdeploy-bundle directory. You can specify the ant build script that contains the target using the antFile property.

String

No

preCopyScript

Optional script to be executed before copying files to the jdeploy-bundle directory.

String

No

postCopyScript

Optional script to be executed after copying files to the jdeploy-bundle directory.

String

No

pruneOldVersions

Whether old versions of your app’s bundle should be deleted when the application is updated. (New in 5.1.1). Before 5.1.1, old versions were never deleted. Now default behaviour is true, to delete old versions. Set this to false if you want to keep old versions around.

Boolean

No

true

runAsAdministrator

(Since 5.2) Configure whether the application should run with elevated privileges. Valid values: "disabled" (default - normal privileges), "allowed" (generates both standard and admin launchers), "required" (all launchers use elevation). See Run as Administrator.

String

No

"disabled"

configFile

(Since 5.2) Path to a JSON configuration file containing runtime arguments. Supports placeholders like {{ user.home }}, {{ app.dir }}, etc. Arguments from this file are appended to package.json args. See Runtime Configuration Files.

String

No

None

configFileMac

(Since 5.2) macOS-specific runtime configuration file. Takes precedence over configFile on macOS. See Runtime Configuration Files.

String

No

None

configFileWin

(Since 5.2) Windows-specific runtime configuration file. Takes precedence over configFile on Windows. See Runtime Configuration Files.

String

No

None

configFileLinux

(Since 5.2) Linux-specific runtime configuration file. Takes precedence over configFile on Linux. See Runtime Configuration Files.

String

No

None

minLauncherInitialAppVersion

(Since 5.5) Specifies the minimum application version whose launcher is required to run the current version. When set, launchers older than this version will prompt users with a dialog offering to download and install the latest version. Users can choose to update immediately, skip this version requirement, or be reminded later. This allows applications to adopt new jDeploy features that require updated launcher capabilities. Branch-based releases ignore this property.

String

No

None

Multi-Modal App Properties (Since 6.0)

The following properties enable multi-modal application support, including CLI commands, background services, system tray integration, and AI tool integrations.

singleton

Property Description Type Required Default

singleton

Enable single-instance mode. When true, launching the app while an instance is already running will forward arguments to the existing instance instead of starting a new one. Requires jdeploy-desktop-lib to receive forwarded files and URIs. See Singleton Apps.

boolean

No

false

commands

The commands object defines CLI commands that are installed to the user’s PATH. Each key is the command name, and the value is a command configuration object.

Property Description Type Required Default

commands

Object containing command definitions. Keys are command names (must start with letter and contain only alphanumeric characters, hyphens, and underscores).

Object

No

None

commands.<name>.description

Human-readable description of what the command does.

String

No

None

commands.<name>.args

Array of arguments to pass to the Java application when this command is invoked.

Array<String>

No

[]

commands.<name>.implements

Array of special behaviors this command implements. Values: "service_controller" (manages a background service), "updater" (self-updating command), "uninstaller" (clean uninstall support, since 6.1), "launcher" (launches GUI app).

Array<String>

No

[]

commands.<name>.updateTrigger

Custom trigger argument for the updater implementation. When set, users run <command> <trigger> instead of the default <command> update. (Since 6.1)

String

No

"update"

commands.<name>.uninstallTrigger

Custom trigger argument for the uninstaller implementation. When set, users run <command> <trigger> instead of the default <command> uninstall. (Since 6.1)

String

No

"uninstall"

commands.<name>.embedPlist

When true, embeds a LaunchAgent plist inside the macOS app bundle for this command. Used with service_controller on macOS 13+ for native SMAppService integration. (Since 6.1)

boolean

No

false

Example:

{
  "jdeploy": {
    "commands": {
      "myapp-ctl": {
        "description": "Control the MyApp background service",
        "args": ["--service-mode"],
        "implements": ["service_controller"]
      },
      "myapp-update": {
        "description": "Check for and install updates",
        "implements": ["updater"]
      }
    }
  }
}

See CLI Commands for details.

helper

The helper object configures the Background Helper (system tray) integration.

Property Description Type Required Default

helper.actions

Array of actions to display in the system tray menu.

Array<Object>

No

[]

helper.actions[].label

The text displayed in the menu item.

String

Yes

None

helper.actions[].description

Tooltip text shown when hovering over the menu item.

String

No

None

helper.actions[].url

URL to open when the action is clicked. Can be a custom URL scheme to communicate with your app.

String

Yes

None

Example:

{
  "jdeploy": {
    "urlSchemes": ["myapp"],
    "helper": {
      "actions": [
        {
          "label": "Open Dashboard",
          "description": "Open the main application window",
          "url": "myapp://open/dashboard"
        },
        {
          "label": "View Logs",
          "url": "myapp://open/logs"
        }
      ]
    }
  }
}

See Services for details.

ai

The ai object configures AI tool integrations, allowing your application to be deployed as an MCP (Model Context Protocol) server.

Property Description Type Required Default

ai.mcp.command

The CLI command to use when running your app as an MCP server. Must be a command defined in the commands object.

String

Yes (if configuring MCP)

None

ai.mcp.args

Additional arguments to pass when running as an MCP server.

Array<String>

No

[]

ai.mcp.defaultEnabled

Whether the MCP server should be enabled by default during installation.

boolean

No

false

Example:

{
  "jdeploy": {
    "commands": {
      "myapp-mcp": {
        "description": "MCP server for MyApp",
        "args": ["--mcp-mode"]
      }
    },
    "ai": {
      "mcp": {
        "command": "myapp-mcp",
        "args": ["--stdio"],
        "defaultEnabled": true
      }
    }
  }
}

See AI Integrations for details.

winAppDir

Property Description Type Required Default

winAppDir

Custom Windows installation directory, relative to %USERPROFILE%. Use this to install your app in a location less likely to trigger Windows Defender warnings.

String

No

"jdeploy" (installs to %USERPROFILE%\jdeploy\apps)

Example:

{
  "jdeploy": {
    "winAppDir": "AppData\\Local\\Programs"
  }
}

Platform-Specific Bundle Properties

jDeploy supports creating platform-specific bundles that allow you to publish separate NPM packages for different operating systems and architectures. These properties are placed at the root level of package.json (not inside the jdeploy object).

Property Description Type Required Default

platformBundlesEnabled

Boolean flag to enable platform-specific bundle generation. When enabled, jDeploy will create separate bundles for each specified platform.

boolean

No

false

fallbackToUniversal

Boolean flag that allows fallback to the universal bundle if a platform-specific version is unavailable. When set to false, only platform-specific bundles will be used.

boolean

No

true

packageMacX64

NPM package name for macOS Intel (x64) builds.

String

No

None

packageMacArm64

NPM package name for macOS Apple Silicon (ARM64) builds.

String

No

None

packageWinX64

NPM package name for Windows x64 builds.

String

No

None

packageWinArm64

NPM package name for Windows ARM64 builds.

String

No

None

packageLinuxX64

NPM package name for Linux x64 builds.

String

No

None

packageLinuxArm64

NPM package name for Linux ARM64 builds.

String

No

None

Example configuration:

{
  "name": "my-app",
  "version": "1.0.0",
  "platformBundlesEnabled": true,
  "fallbackToUniversal": false,
  "packageMacX64": "my-app-macos-intel",
  "packageMacArm64": "my-app-macos-silicon",
  "packageWinX64": "my-app-windows-x64",
  "packageWinArm64": "my-app-windows-arm64",
  "packageLinuxX64": "my-app-linux-x64",
  "packageLinuxArm64": "my-app-linux-arm64",
  "jdeploy": {
    "jar": "target/my-app.jar"
  }
}

Copy Rules

The "jDeploy/files" array includes zero or more copy rules. Each copy rule is an object that may contain the following properties:

Property Description Type Required Default

dir

The relative path to the directory from which files are to be copied.

String

Yes

includes

Specifies files to be included in the copy. Either an array of glob patterns, or a comma-delimited string of glob patterns. The glob pattern uses PathMatcher globs. E.g. "*/.dll".

String or Array

No

All files in dir

excludes

Specifies files to be excluded from the copy. Either an array of glob patterns, or a comma-delimited string of glob patterns. The glob pattern uses PathMatcher globs.

String or Array

No

None

Example Copy Rules*

{
  ...
  "jdeploy" : {
    "files" : [
        {"dir" : "dist", "includes": "lib/*.dll,lib/*.so,lib/*.dylib"}
    ]
  }
  ...
}

This would copy all of the dll, so, and dylib files from "dist/lib" into the jdeploy-bundle directory for deployment.

Automatic jar/war file Resolution

The "jDeploy/jar" and "jDeploy/war" properties specify the location of the jar or war file that constitutes the application. jdeploy init will scan the current directory for eligible jar, war, and web app directories and set the shallowest candidate in the "jDeploy/jar" or "jDeploy/war" directive of the package.json. If you delete this value, or you omit it from the package.json, jdeploy install and jdeploy publish will perform the same search for an appropriate candidate.

Examples

{
  "bin": {"hellojdeploy": "jdeploy-bundle/jdeploy.js"}, // (1)
  "preferGlobal": true, (2)
  "version": "1.0.1",
  "jdeploy": {"jar": "dist/HelloJDeploy.jar"}, (3)
  "dependencies": {"shelljs": "^0.8.4"},
  "license": "ISC",
  "name": "hello-jdeploy", (4)
  "files": ["jdeploy-bundle"] (5)
}
  1. Once installed, the binary name of our app will be "hellojdeploy". I.e. we would start the app by typing "hellojdeploy" at the command prompt.

  2. Indicates that NPM should prefer to install the app globally. If people forget to include the "-g" flag when they try to install the app, they will get a warning.

  3. The executable Jar file that we are deploying is located at "dist/HelloJDeploy.jar".

  4. The command to install the app would be npm install -g jdeploy-bundle

The above package.json deploys an app such that it would be installed with

npm install -g hello-jdeploy

And it would be installed as "hellojdeploy", so that it could be run on the command line:

hellojdeploy

Appendix: Building Executable Jar File

Tip
The jdeploy-javafx-starter template comes preloaded with everything necessary to distribute your app as native installer. If you’re starting fresh, we recommend you start with that template. The following section describes how to modify existing maven projects to produce an executable Jar file.

jDeploy essentially converts an executable Jar file into native bundles for Mac, Windows, and Linux. An Executable Jar file is essentially just a Jar file that can be run via java -jar TheJar.jar. The distinguishing feature of an executable jar file (vs a regular jar file) is that it includes directives in its manifest that specify the "Main" class to run, and a "Class-Path" (i.e. a list of dependencies). Most IDEs and build tools have built-in support for generating executable Jar files. In some cases, however, you may need to explicitly enable this.

Executable Jar Files in Maven

The two most common strategies to produce executable jars in Maven are using either a combination of the maven-dependency-plugin and the maven-jar-plugin, or using the maven-assembly-plugin. The latter approach (maven-assembly-plugin) can generate a single "fat" jar with all of your dependencies. The former approach (maven-dependency-plugin and maven-jar-plugin) places your dependencies in a "libs" directory.

Tip
An alternate approach is to add the spring-boot-starter module as a parent in your pom.xml file. It will include all the configuration necessary to build your app as an executable "uber" jar.

Using the maven-dependency-plugin and maven-jar-plugin

In Maven, for example, you will need to use the maven-dependency-plugin and maven-jar-plugin plugins to package up all the dependencies, and add the appropriate manifest entries to make the jar executable. The following snippet can be added to the <build>/<plugins> section of your pom.xml file.

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-dependency-plugin</artifactId>
    <executions>
        <execution>
            <id>copy-dependencies</id>
            <phase>prepare-package</phase>
            <goals>
                <goal>copy-dependencies</goal>
            </goals>
            <configuration>
                <outputDirectory>
                    ${project.build.directory}/libs
                </outputDirectory>
            </configuration>
        </execution>
    </executions>
</plugin>
<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-jar-plugin</artifactId>
    <configuration>
        <archive>
            <manifest>
                <addClasspath>true</addClasspath>
                <classpathPrefix>libs/</classpathPrefix>
                <mainClass>
                    com.example.YourMainClass
                </mainClass>
            </manifest>
        </archive>
    </configuration>
</plugin>

You will need to change the <mainClass>com.example.YourMainClass</mainClass> entry to point to the main class of your app.

Using the maven-assembly-plugin

The maven-assembly-plugin is an alternate way to produce an executable jar. It will generate a "fat" jar with all dependencies.

Note
With jDeploy there is no benefit to "fat" jar because your app will be bundled as a native bundle anyways.

The following snippet can be added to the <build>/<plugins> section of your pom.xml file.

<plugin>
    <artifactId>maven-assembly-plugin</artifactId>
    <version>2.4</version>
    <executions>
        <execution>
            <id>build-publisher</id>
            <configuration>
                <appendAssemblyId>false</appendAssemblyId>
                <archive>
                    <manifest>
                        <mainClass>com.example.YourMainClass</mainClass>
                    </manifest>
                </archive>
                <descriptorRefs>
                    <descriptorRef>jar-with-dependencies</descriptorRef>
                </descriptorRefs>
                <finalName>${project.artifactId}</finalName>
            </configuration>
            <phase>package</phase>
            <goals>
                <goal>single</goal>
            </goals>
        </execution>

    </executions>
</plugin>

You will need to change the <mainClass>com.example.YourMainClass</mainClass> entry to point to the main class of your app.

Using the Spring Boot Starter

An alternate approach for generating executable jars in your app is to reference the spring-boot starter pom as the parent of your module. This will cause your project to inherit all of the nice build features that Spring boot has to offer.

The idea of using the spring boot starter may seem strange at first, since Spring is a framework for building web applications, but it was build to be modular, so it is possible to use its build tools without actually adding any dependencies to your application.

Add the following to your pom.xml file in order to use Spring boot to generate your jar:

  <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>3.3.0</version>
    <relativePath/> <!-- lookup parent from repository -->
  </parent>


...

  <build>
    <plugins>
      <plugin>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-maven-plugin</artifactId>
        <configuration>
          <mainClass>com.example.YourMainClass</mainClass>
        </configuration>
      </plugin>
    </plugins>
  </build>

Appendix: The jdeploy-javafx-archetype

The jdeploy-javafx-starter Github template, which provides you with a JavaFX app project and native installers out of the box, is essentially the same as the jdeploy-javafx-archetype maven archetype.

If you like to use Maven archetypes to generate your new project, you may prefer to use the archetype directly.

I generally prefer the former approach because sometimes bundling everything into a fat jar can break things. Also, when using jDeploy, there is no benefit to a fat jar, since your app is deployed as a native bundle anyways.

Appendix: Windows Installation Layout

The jDeploy application installer creates a slightly different file structure for the installed application on each platform. Additionally, the layout will depend on which version of Java your application specifies in the javaVersion property of the package.json file. Finally, the layout for apps deployed via npm will differ slightly from apps that are deployed via GitHub releases.

Custom Installation Directory (winAppDir)

By default, jDeploy installs Windows applications to %USERPROFILE%\.jdeploy\apps. Some antivirus software, particularly Windows Defender, may flag executables running from hidden directories (directories starting with .) as potentially suspicious.

To avoid these warnings, you can use the winAppDir property to specify a different installation directory:

{
  "jdeploy": {
    "winAppDir": "AppData\\Local\\Programs"
  }
}

The value is relative to %USERPROFILE%. In this example, the app would be installed to %USERPROFILE%\AppData\Local\Programs instead of %USERPROFILE%\.jdeploy\apps.

Note
Changing winAppDir only affects new installations. Existing installations will remain in their original location.

Windows Installation Layout for Apps Deployed via npm

Given an application with package name "myapp" and title "My App", the following file structure will be created on Windows:

If the app uses Java 8:

Location Description

%HOMEPATH%/.jdeploy/apps/myapp

This is the root directory for the application. It contains the following subdirectories:

%HOMEPATH%/.jdeploy/apps/myapp/bin/My App.exe

The executable file for the application. This is the file that will be run when the user clicks on the app icon.

%HOMEPATH%/.jdeploy/apps/myapp/jre

The Java runtime environment that is bundled with the app.

%HOMEPATH%/.jdeploy/uninstallers/myapp/myapp-uninstall.exe

The uninstaller for the app. This installer will be run when you uninstall the application via Add/Remove Programs.

IMPORTANT: You should not run the installer directly. Always run it via Add/Remove Programs.

%HOMEPATH%/.jdeploy/packages/myapp

This directory contains all of your actual Java application files. This is where your jar file, war file, or web app directory will be installed.

%HOMEPATH%/.jdeploy/log/myapp/myapp.lastrun.log

The log file for the last run of the application. If you write to StdOut or StdErr in your application, this is where the output will be captured.

If the app uses Java 11 or higher:

Location Description

%HOMEPATH%/.jdeploy/apps/myapp

This is the root directory for the application. It contains the following subdirectories:

%HOMEPATH%/.jdeploy/apps/myapp/My App.exe

The executable file for the application. This is the file that will be run when the user clicks on the app icon.

%HOMEPATH%/.jdeploy/uninstallers/myapp/myapp-uninstall.exe

The uninstaller for the app. This installer will be run when you uninstall the application via Add/Remove Programs.

IMPORTANT: You should not run the installer directly. Always run it via Add/Remove Programs.

%HOMEPATH%/.jdeploy/packages/myapp

This directory contains all of your actual Java application files. This is where your jar file, war file, or web app directory will be installed.

%HOMEPATH%/.jdeploy/log/myapp/myapp.lastrun.log

The log file for the last run of the application. If you write to StdOut or StdErr in your application, this is where the output will be captured.

Windows Installation Layout for Apps Deployed via GitHub Releases

The layout for apps deployed via GitHub releases is slightly different. The main difference is that the app is installed in a directory that is named after the hash of the app’s package name and version. This is to ensure that multiple versions of the same app can be installed side-by-side. Each app has a unique hash prefix that is derived from the package name and version of the app. This hash prefix is used to create a unique directory for the app. In addition, the package is installed in the %HOMEPATH%/.jdeploy/gh-packages directory, rather than the %HOMEPATH%/.jdeploy/packages directory.

If the app uses Java 8:

Location Description

%HOMEPATH%/.jdeploy/apps/<APP_HASH_PREFIX>.myapp

This is the root directory for the application. It contains the following subdirectories:

%HOMEPATH%/.jdeploy/apps/<APP_HASH_PREFIX>.myapp/bin/My App.exe

The executable file for the application. This is the file that will be run when the user clicks on the app icon.

%HOMEPATH%/.jdeploy/apps/<APP_HASH_PREFIX>.myapp/jre

The Java runtime environment that is bundled with the app.

%HOMEPATH%/.jdeploy/uninstallers/<APP_HASH_PREFIX>.myapp.<BRANCH_NAME>/myapp-uninstall.exe

The uninstaller for the app. This installer will be run when you uninstall the application via Add/Remove Programs.

IMPORTANT: You should not run the installer directly. Always run it via Add/Remove Programs.

%HOMEPATH%/.jdeploy/gh-packages/<APP_HASH_PREFIX>.myapp

This directory contains all of your actual Java application files. This is where your jar file, war file, or web app directory will be installed.

%HOMEPATH%/.jdeploy/log/github.com/<GITHUB_USER>/<GITHUB_REPO>/myapp/myapp.lastrun.log

The log file for the last run of the application. If you write to StdOut or StdErr in your application, this is where the output will be captured.

If the app uses Java 11 or higher:

Location Description

%HOMEPATH%/.jdeploy/apps/<APP_HASH_PREFIX>.myapp

This is the root directory for the application. It contains the following subdirectories:

%HOMEPATH%/.jdeploy/apps/<APP_HASH_PREFIX>.myapp/My App.exe

The executable file for the application. This is the file that will be run when the user clicks on the app icon.

%HOMEPATH%/.jdeploy/uninstallers/<APP_HASH_PREFIX>.myapp/myapp-uninstall.exe

The uninstaller for the app. This installer will be run when you uninstall the application via Add/Remove Programs.

IMPORTANT: You should not run the installer directly. Always run it via Add/Remove Programs.

%HOMEPATH%/.jdeploy/gh-packages/<APP_HASH_PREFIX>.myapp

This directory contains all of your actual Java application files. This is where your jar file, war file, or web app directory will be installed.

%HOMEPATH%/.jdeploy/log/github.com/<GITHUB_USER>/<GITHUB_REPO>/myapp/myapp.lastrun.log

The log file for the last run of the application. If you write to StdOut or StdErr in your application, this is where the output will be captured.

Why is the layout different on Java 8

Apps that use Java 8 on Windows use a slightly different file structure than other versions of Java because OpenJDK 8’s support for a dedicated Java Runtime Environment (JRE) was very specific about the app structure. On Java 11 and higher, it will use the JRE specified by the JAVA_HOME environment, so the jDeploy launcher simply needs to set this value appropriately before starting the JVM. On Java 8, however, it ignores the JAVA_HOME setting when it comes to the JRE, and instead tries to find the location of the global JRE in the Windows registry.

Using a global JRE is not ideal for apps that are distributed as standalone executables, because it can lead to conflicts with other Java apps on the system, so fortunately, Java 8 provided one other option.

If the app exe resides in a folder named "bin", then the Java launcher will look for a JRE in a directory named "jre" in the same directory as the "bin" directory. E.g. If the app.exe is located at /path/to/bin/app.exe, then it will look for a JRE at /path/to/jre. In order to satisfy this requirement, and not disrupt the majority of apps, we decided to treat Java 8 specially.

We create a "jre" directory in the app’s directory, and copy the JRE into that directory, and move the app’s exe file into a "bin" directory.

Appendix: Command-Line Usage

While the GUI is recommended for most users, jDeploy can also be used entirely from the command line. This section covers installation and usage of jDeploy as a command-line tool.

Installing jDeploy CLI

jDeploy can be installed globally using npm:

npm install -g jdeploy
Note
On macOS and Linux, you may need to use sudo for global installation.

To verify the installation:

jdeploy --version

Basic Commands

Initializing a Project

To initialize a new jDeploy project in your existing Java application directory:

cd /path/to/your/java/app
jdeploy init

This creates a package.json file with default jDeploy configuration. The command will:

  1. Scan for executable JAR files in your project

  2. Create initial configuration based on your project structure

  3. Set up default publishing settings

Publishing Your Application

To publish your application:

# First time only: Log in to npm
npm login

# Publish the application
jdeploy publish

The publish command:

  1. Builds your application bundles

  2. Creates installers for all supported platforms

  3. Publishes to npm or GitHub (based on configuration)

  4. Updates your download page on jdeploy.com

Building Packages Locally

To build packages without publishing:

jdeploy package

This creates bundles and installers in the jdeploy/bundles and jdeploy/installers directories respectively.

Advanced Commands

Cleaning Build Files

To clean all generated files:

jdeploy clean

Running in Debug Mode

For detailed logging during any command:

jdeploy --debug <command>

For example:

jdeploy --debug publish

Command-Line Options

Option Description

--version

Display version information

--debug

Enable debug logging

--help

Show help information

--skip-build

Skip building the application (publish/package)

--skip-installers

Skip creating installers (publish/package)

Environment Variables

jDeploy respects the following environment variables:

Variable Description

JDEPLOY_PORT

Override the default port for web applications

JAVA_HOME

Specify the Java installation to use

NPM_TOKEN

Authentication token for npm publishing

Example Workflow

A typical command-line workflow might look like this:

# Initialize the project
cd my-java-app
jdeploy init

# Edit package.json to configure your app
# Build your Java application (e.g., mvn package)

# Test package building locally
jdeploy package

# When ready, publish
npm login
jdeploy publish

Appendix: Project Templates

When creating a new project in jDeploy, you can choose from several pre-configured project templates that provide a solid foundation for your application. These templates come with jDeploy integration and GitHub workflows already set up.

Project templates are available in the Create New Project dialog
Figure 9. The New Project form allows you to select from pre-configured templates

Built-in Templates

The following templates are available by default:

Swing Template

A basic Java Swing application template that includes:

  • Maven project structure

  • Basic Swing application window

  • jDeploy configuration for native bundles

  • GitHub workflow for automated releases

JavaFX Template

A JavaFX application template based on the official javafx-archetype-simple archetype, featuring:

  • Modern JavaFX application structure

  • FXML support for UI design

  • CSS styling capabilities

  • GitHub workflow for native installers

  • Auto-update configuration

Spring Boot + Swing Template

A template combining Spring Boot with Swing, offering:

  • Spring Boot’s dependency management

  • Swing UI integration

  • Built-in executable JAR support

  • jDeploy publishing configuration

Template Variables

Project templates use placeholder variables that are automatically replaced when creating a new project. The available placeholders are:

Variable Description

{{ appName }}

The name of your application used for package naming

{{ appTitle }}

The human-readable title of your application

{{ groupId }}

The Maven group ID for your project

{{ artifactId }}

The Maven artifact ID for your project

{{ mainClass }}

The fully qualified name of your application’s main class

{{ mainClassName }}

Alternative reference to the main class name

{{ packageName }}

The Java package name for your project

{{ packagePath }}

The package name converted to a directory path (e.g., com.example becomes com/example)

{{ javaVersion }}

The Java version to be used in the project

{{ githubRepository }}

The GitHub repository where the project will be hosted

{{ githubReleasesRepository }}

The GitHub repository where releases will be published

{{ releasesUrl }}

The URL where releases will be available

Contributing Templates

You can contribute your own project templates to the jDeploy community by submitting them to the jdeploy-project-templates repository.

Template Structure

A typical project template includes:

template/
├── src/                    # Source code directory
├── .github/workflows/      # GitHub Actions workflows
├── pom.xml                # Maven configuration
├── package.json           # jDeploy configuration
└── README.md              # Template documentation

Using Templates

To use a project template:

  1. In the jDeploy GUI, select "Create New Project"

  2. Choose a template from the dropdown list

  3. Fill in the required information:

    • Project name

    • Application title

    • Group ID

    • Artifact ID

    • Main class name

    • Package name

    • GitHub repository (if using GitHub integration)

  4. Click "Create Project"

The template system will:

  1. Create a new directory for your project

  2. Replace all template placeholders with your values

  3. Initialize a Git repository (if requested)

  4. Set up the GitHub workflow (if enabled)

Best Practices

When creating templates:

  1. Use the standard placeholder variables consistently

  2. Include comprehensive documentation

  3. Add example code that demonstrates best practices

  4. Include GitHub workflows for automated builds and releases

  5. Configure appropriate jDeploy settings for the template type

  6. Test the template with different placeholder values to ensure proper substitution

Appendix: Change History

This section provides a summary of the major changes in each version of jDeploy. We created this section with the release of 5.0. For prior change history, refer to GitHub release notes.

v6.1 - March 2026

New Features

  • Uninstaller Command: CLI commands can now implement a manifest-driven uninstaller that cleanly removes all installed files, services, PATH entries, and registry keys. See CLI Commands.

  • Local Development Mode: Test jDeploy-packaged applications locally without publishing by configuring local-package-json and local-bundle paths in the launcher’s app.xml.

  • Java Remote Debugging (JDWP): The native launcher can start the JVM with JDWP debugging enabled via debug-port in app.xml or the JDEPLOY_DEBUG_PORT environment variable.

  • Configurable Update Trigger: The updater command implementation now supports a custom updateTrigger property to change the default trigger argument. See CLI Commands.

  • NPX Launch Mode Detection: Applications launched via npm/npx now receive jdeploy.mode=npx as a system property for runtime detection. See CLI Commands.

  • macOS SMAppService Integration: Services on macOS 13+ can use Apple’s SMAppService API for native Launch Agent management with the new embedPlist property. See Services.

Bug Fixes

  • Fixed certificate pinning for platform-specific bundles where JAR filtering invalidated signatures after signing.

  • Fixed Linux shell profile chain preservation—jDeploy no longer creates ~/.bash_profile on Linux, preventing breakage of the ~/.profile~/.bashrc chain.

  • Fixed PATH updates when the installer is launched from GUI applications (IntelliJ IDEA, file managers) where SHELL is not set.

  • Fixed MCP server compatibility by redirecting JRE download progress messages to stderr.

  • Fixed template substitution with missing githubRepository parameter (no longer substitutes "null").

  • Fixed NPM publishing from the desktop GUI where the auth token was lost during validation.

  • Fixed publish settings not being persisted from the Project Editor.

  • Fixed current working directory in command mode—CLI commands now preserve the caller’s working directory.

  • Fixed GitHub Actions workflow triggers to be restricted to snapshot branches and v-prefixed tags.

For detailed information, see the jDeploy 6.1 Release Notes.

v6.0 - February 2026

New Features

  • Multi-Modal App Support: Applications can now be deployed as GUI apps, CLI commands, background services, and MCP servers—all from a single package. See CLI Commands, Services, and AI Integrations.

  • CLI Commands: Install command-line tools to the user’s PATH with the new commands configuration. Commands can implement special behaviors including service controllers, self-updating commands, and GUI launchers.

  • Background Services: Deploy applications as system services managed by native service managers (systemd, launchd, Windows Service Manager). Includes system tray integration via the Background Helper.

  • AI Tool Integration: Deploy your application as an MCP (Model Context Protocol) server for integration with AI coding assistants like Claude Desktop, Claude Code, VS Code Copilot, Cursor, and more.

  • Singleton Mode: Enable single-instance mode to prevent duplicate app instances and forward files/URIs to existing instances via the new jdeploy-desktop-lib.

  • Redesigned Desktop GUI: The Project Editor now features a modern side navigation panel for easier access to all configuration options.

  • Improved Installer: Added Update and Uninstall buttons to installed apps, plus an AI integrations checkbox for enabling MCP servers and skills during installation.

  • jDeploy MCP Server: jDeploy itself is now available as an MCP server, allowing AI coding agents to create, configure, and publish jDeploy projects.

  • Custom Windows Installation Directory: New winAppDir property allows installation to custom directories to avoid Windows Defender warnings.

For detailed information, see the jDeploy 6.0 Release Notes.

v5.5.3 - November 30, 2025

Bug Fixes

  • Fixed issue where release notes weren’t displayed correctly on the first release in a repository.

  • Fixed Windows installer builds in GitHub Actions (path handling issue with script resolution).

  • Fixed build failures when projects don’t provide a custom icon (now correctly falls back to default icon).

  • Improved GitHub repository validation and error messages in Desktop GUI project creation.

  • Fixed handling of minLauncherInitialAppVersion in branch releases (now completely ignored for branch releases).

For detailed information, see the jDeploy 5.5.3 Release Notes.

v5.5.0 - November 25, 2025

New Features

  • Launcher Version Enforcement: Applications can now require minimum launcher versions using the minLauncherInitialAppVersion property. When set, launchers older than the specified version will prompt users with a friendly dialog offering three options: "Update Now" (automatically downloads and launches the installer), "Skip This Version" (won’t prompt again for this version), or "Ask Later" (prompts again in 1 week). This allows applications to adopt new jDeploy features that require updated launcher capabilities while giving users control over when to update. Branch-based releases ignore this property.

For detailed information, see the jDeploy 5.5 Release Notes.

v5.4.3 - November 11, 2025

Bug Fixes

  • Fixed JBR installer to download correct JRE instead of downloading twice (once for installer, once for app).

  • Fixed possible corruption in package-info.json when multiple GitHub workflows release simultaneously (now uses optimistic lock).

  • Fixed occasional install failures when package-info.json temporarily unavailable during publishing (added fallback URLs).

  • Enhanced installer branding to show application’s splash screen and icon instead of generic jDeploy icon.

  • Improved installer error messages to include application name and provide clearer guidance.

For detailed information, see the jDeploy 5.4.3 Release Notes.

v5.4.1 - October 27, 2025

New Features

  • JetBrains Runtime (JBR) Support: Applications can now use JetBrains Runtime as their JRE provider, enabling access to JCEF (Java Chromium Embedded Framework) and enhanced rendering capabilities.

  • Increased Default File Limit on macOS: The default file descriptor limit on macOS has been increased to the kernel maximum to prevent "Too many open files" errors.

  • Improved JavaFX Version Configuration: The javafxVersion property now supports more flexible version specifications, including early access and custom JavaFX versions.

  • Installer Window Icon: The installer window now uses the application’s custom icon instead of the default Java icon.

Bug Fixes

  • Fixed architecture-specific package installation conflicts when installing both x64 and ARM64 versions on systems that support both architectures.

  • Fixed macOS x64 splash screen crash on macOS Ventura and earlier.

For detailed information about these features, see the jDeploy 5.4 Release Notes.

v5.3 - October 18, 2025

New Features

  • Dynamic Placeholder Expressions: Added support for advanced placeholder expressions in runtime arguments and configuration file paths. New features include environment variable access ({{ env.VARNAME }}), coalesce operator (??), string concatenation (+), cross-platform path construction (path()), path normalization (normalize()), and conditional blocks (if/else/end). See Placeholder Variables.

  • Directory Associations: Applications can now register as handlers for directories and folders, enabling project-based workflows. Users can right-click on folders to open them with your application, or drag and drop folders onto the application icon. Configured via documentTypes with type: "directory". See Directory Associations.

Bug Fixes

  • Fixed JRE selection for applications using custom JavaFX versions. When javafxVersion is specified in package.json, the launcher now ensures only JREs without bundled JavaFX are selected, preventing potential version conflicts between Maven-downloaded JavaFX and bundled JavaFX libraries.

For detailed information about these features, see the jDeploy 5.3 Release Notes.

v5.2.3 - October 10, 2025

New Features

  • Added support for custom launcher splash screens via launcher-splash.html. Applications can now provide a fully customizable HTML/CSS splash screen that displays during installation and updates.

  • Enhanced jDeploy installer to use custom launcher-splash.html and icon.png files during initial installation. Previously, custom icons and splash screens were only used after installation and during updates - now they provide a branded experience from the very first installation step.

  • Added file descriptor limit configuration for Unix-like systems (macOS and Linux) via the -Djdeploy.file.limit system property. This allows applications that need to open many files or network connections to configure appropriate limits before JVM startup. See File Descriptor Limits (Unix/Linux/macOS).

v5.2 - October 2, 2025

New Features

  • Added Run as Administrator feature, allowing applications to be configured to run with elevated privileges on Windows, macOS, and Linux. Three modes are supported: disabled (default), allowed (generates both standard and admin launchers), and required (all launchers use elevation). See Run as Administrator.

  • Added CLI command installation option in the Linux installer. Users can now optionally install a symlink in ~/.local/bin during installation, making applications accessible from the command line.

  • Added runtime configuration file support, allowing applications to load additional JVM arguments, system properties, and program arguments from external JSON files. This enables environment-specific configuration without repackaging. See Runtime Configuration Files.

Bug Fixes

  • Fixed installation failure on Linux systems with a graphical environment but no desktop environment (e.g., WSL with graphics support, minimal installations with window manager only).

v5.1 - September 14, 2025

New Features

  • Added platform-specific bundle support, allowing you to create optimized bundles for each platform that exclude unnecessary platform-specific files. This can significantly reduce bundle sizes for applications with platform-specific native libraries. See Platform-Specific Bundles.

  • Added automatic deletion of old application versions to prevent disk space issues. The installer now automatically removes previous versions when installing updates, keeping only the current version installed.

Improvements

  • Reduced bundle sizes for applications using platform-specific native libraries

  • Improved disk space management with automatic cleanup of outdated versions

  • Enhanced the package.json configuration with new properties for platform-specific bundles

v5.0 - August 2025

New Features

  • Added ARM64 builds on Windows and Linux for both GUI and CLI deployed apps.

  • Added ability to customize which platforms are included on the download page. See Customizing the Download Page.

  • Added ability to specify app permissions. See App Permissions.

  • Added automatic platform detection on the download page, so that users are presented with only one download link for their current platform, with other platforms available via a dropdown.

  • Added AI setup support with Claude Code. See Claude Code Integration

Bug Fixes

  • Fixed issue where linux installer was not executable, so users needed to chmod +x it before running.

  • Fixed issue where changing app to use GitHub as a publish target still kept the NPM publish target, causing publish to fail.

  • See GitHub release notes for full list of fixes.