Publish Kotlin Multiplatform Applications with Conveyor

AppBazaAppBaza
2 min read

This guide outlines a real-world configuration for publishing a Kotlin Multiplatform application with Hydraulic Conveyor.

For this tutorial, I am using maxDEV — a cross-platform (supports Mac, Windows, Linux) developer productivity app as an example.

Conveyor configuration

conveyor.conf:

include required("https://raw.githubusercontent.com/hydraulic-software/conveyor/master/configs/jvm/extract-native-libraries.conf")
include "/stdlib/jdk/17/amazon.conf"

app {
  fsname = maxdev
  display-name = maxDEV
  updates = aggressive

  icons = "icons/icon.png"
  windows.icons = "icons/icon.ico"

  mac.notarization {
 app-specific-password = "!!!!-!!!!-!!!!-!!!"
  }

  jvm.extract-native-libraries = true

  site.base-url = "https://kenanbek.github.io/maxDEV/download.html"
  site.github.oauth-token = "!!!"
  site.github.pages-branch = "gh-pages"
}

conveyor.compatibility-level = 17
conveyor.billing-email = "!!!"
conveyor.license-key = "!!!"

For Apple notarization, check this link.

For the Conveyor license key and billing, you need a Hydraulic subscription.

Gradle configuraitons

libs.versions:

[versions]
kotlin = "2.1.10"
kotlinx-coroutines = "1.10.1"
compose-multiplatform = "1.7.0"
androidx-lifecycle = "2.8.4"
junit = "4.13.2"
ktor = "3.1.1"
logback = "1.5.17"
ktlint-gradle = "11.6.1"
ktlint-ruleset-compose = "0.4.22"
conveyor = "1.12"

[plugins]
conveyor = { id = "dev.hydraulic.conveyor", version.ref = "conveyor" }

build.gradle.kts (root):

plugins {
    alias(libs.plugins.conveyor) apply false
}

build.gradle.kts (desktop app):

plugins {
    alias(libs.plugins.conveyor)
}

compose.desktop {
    application {
        mainClass = "com.appbaza.maxdev.MainKt"

        nativeDistributions {
            targetFormats(TargetFormat.Dmg, TargetFormat.Exe, TargetFormat.Msi, TargetFormat.Deb, TargetFormat.Rpm)
            packageName = "com.appbaza.maxdev"
            packageVersion = "1.0.0"

            macOS {
                bundleID = "com.appbaza.maxdev"
                signing {
                    sign.set(true)
                    identity.set("!!!")
                }
                notarization {
                    appleID.set("!!!")
                    password.set("!!!")
                    teamID.set("!!!")
                }
            }
        }
    }
}

Updates

I configured the application to use this GitHub Pages website for distributing updates. For manual updates, I am including a static page on the application’s website as well as referring to the release page where Conveyor publishes all the relevant.

Conveyor also supports other options.

At the current state of the project, I am using the aggressive update mode. Once the app reaches a more stable phase, I might consider the background update mode.

You can read more about update modes here.

Configuring the update mode is as easy as the following configuration in your conveyor.conf:

app {
  fsname = maxdev
  display-name = maxDEV
  updates = aggressive
}

This is another excellent documentation from Hydraulic about updates in general:

Understanding updates - Hydraulic Conveyor

Release

I use the following procedure for preparing a new release for maxDEV:

  1. Finalize code changes.

  2. Bump the version number in build.gradle.kts (:composeApp)

  3. Prepare package (./gradlew clean package)

  4. Prepare download assets (conveyor make site)

  5. Upload site (conveyor make copied-site)

You can reach out to me if you need consultancy and development support for your Kotlin cross-platform desktop projects: send an email to KEN at AppBaza.com.


Originally published at https://www.codervlogger.com on May 2, 2025.

0
Subscribe to my newsletter

Read articles from AppBaza directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

AppBaza
AppBaza

🧑🏻‍💻 maxdev.biz - Cross-platform Developer Productivity Tool 🔀 aiflowly.com - Ai workflows and no-code agents. 🤖 appbaza.com/aieditor - Privacy-first AI client for Mac, Windows, & Linux.