Building for Linux
Configure a Nucleus app to build DEB, RPM, AppImage, and pacman packages and publish to the Snap and Flatpak stores.
In this tutorial, you'll configure a Nucleus app to produce every Linux package
format — DEB, RPM, AppImage, and pacman for direct distribution, plus Snap and
Flatpak for the stores — from a single linux { } block, then build and inspect
the output.
Before you start
- The Nucleus Gradle plugin is applied to your project. See Project setup.
- You are on a Linux build host. DEB, RPM, AppImage, pacman, Snap, and Flatpak are Linux-only formats and do not cross-build from macOS or Windows.
- Format-specific tooling is installed as noted in each step (for example
libfuse2for AppImage,snapcraftfor Snap,flatpak-builderfor Flatpak).
Configure the target formats
In build.gradle.kts, list the Linux formats you want in targetFormats(...) and
describe the package in the linux { } block:
nucleus {
application {
nativeDistributions {
targetFormats(
TargetFormat.Deb,
TargetFormat.Rpm,
TargetFormat.AppImage,
TargetFormat.Pacman,
TargetFormat.Snap,
TargetFormat.Flatpak,
)
homepage = "https://myapp.example.com"
linux {
iconFile.set(project.file("icons/app.png"))
packageName = "myapp"
shortcut = true
appCategory = "Utility"
menuGroup = "Development"
debMaintainer = "Your Name <dev@example.com>"
debDepends = listOf("libfuse2", "libgtk-3-0", "libasound2")
rpmLicenseType = "MIT"
rpmRequires = listOf("gtk3", "libX11", "alsa-lib")
pacmanDepends = listOf("gtk3", "libx11", "alsa-lib")
}
}
}
}homepage is required for DEB and RPM builds. The packaging task fails without it.
Build DEB and RPM packages
Each format has its own Gradle task named after the TargetFormat constant:
./gradlew packageDeb
./gradlew packageRpmThe plugin writes a .desktop entry into each package with the correct
Categories=, MimeType=, StartupWMClass=, and Exec= lines. If your app's
window class doesn't match what the window manager reports, set startupWMClass
explicitly:
linux {
startupWMClass = "com-example-MyApp"
}When startupWMClass is unset, Nucleus derives it from mainClass by replacing
dots with hyphens.
Build an AppImage
An AppImage is a single portable executable that runs on most distributions without installation:
./gradlew packageAppImageThe build host needs libfuse2:
sudo apt-get install -y libfuse2Configure the desktop metadata in the appImage { } sub-block:
linux {
appImage {
category = AppImageCategory.Utility
genericName = "My Application"
synopsis = "A short description"
desktopEntries = mapOf("Keywords" to "editor;text;")
}
}Output size and build time depend on nativeDistributions.compressionLevel:
CompressionLevel | Output | Build time |
|---|---|---|
Store | Largest | Fastest |
Normal (default) | Balanced | Moderate |
Maximum | Smallest | Slowest |
nativeDistributions {
compressionLevel = CompressionLevel.Normal
}Package for Arch Linux
TargetFormat.Pacman produces a pacman archive for Arch Linux and derivatives:
./gradlew packagePacmanSet Arch-specific dependencies and, if needed, an independent version string:
linux {
pacmanDepends = listOf("gtk3", "libx11", "alsa-lib")
pacmanPackageVersion = "2.0.0"
}Publish to the Snap Store
Snap targets the Snap Store. The default plugs cover
desktop integration, X11 and Wayland, networking, audio playback, and OpenGL.
Override confinement when your app needs filesystem access beyond home:
linux {
snap {
confinement = SnapConfinement.Strict
grade = SnapGrade.Stable
base = "core22"
plugs = listOf(
SnapPlug.Desktop,
SnapPlug.Home,
SnapPlug.Wayland,
SnapPlug.Network,
SnapPlug.AudioPlayback,
)
compression = SnapCompression.Xz
}
}./gradlew packageSnapThe build host needs snapd and snapcraft --classic.
Publish to Flathub with Flatpak
Flatpak is sandboxed. Declare the permissions your app needs through finishArgs.
The defaults are --share=ipc, --socket=x11, --socket=wayland,
--socket=pulseaudio, and --device=dri; setting finishArgs replaces them, so
include everything you need:
linux {
flatpak {
runtime = "org.freedesktop.Platform"
runtimeVersion = "23.08"
sdk = "org.freedesktop.Sdk"
finishArgs = listOf(
"--share=ipc",
"--socket=wayland",
"--socket=pulseaudio",
"--device=dri",
"--filesystem=home",
"--share=network",
)
}
}./gradlew packageFlatpakFlatpak is a store format and runs through the
sandboxed pipeline: native libraries are
pre-extracted and JVM arguments are redirected. If flatpak-builder or the
runtime isn't installed, the task is skipped rather than failed.
Register deep links and file associations
Top-level protocol(...) and fileAssociation(...) entries are merged into the
generated .desktop file as MimeType= values:
nativeDistributions {
protocol(name = "MyApp URL", schemes = listOf("myapp"))
fileAssociation(
mimeType = "application/x-myapp",
extension = "myapp",
description = "MyApp Document",
)
}This produces:
MimeType=x-scheme-handler/myapp;application/x-myapp;No manual desktopEntries override is needed.
Configuration reference
linux { } exposes iconFile, packageVersion, shortcut, startupWMClass,
packageName, appRelease, appCategory, menuGroup, debMaintainer,
debPackageVersion, debDepends, rpmLicenseType, rpmPackageVersion,
rpmRequires, pacmanPackageVersion, and pacmanDepends. Sub-blocks are
appImage { }, snap { }, flatpak { }, and signing { }.
appRelease sets the package release number — the digit after the - in DEB and
RPM versions. Bump it when you re-release without changing packageVersion. See
the full Gradle DSL reference.
Snap and Flatpak layer their own desktop files into the user environment and
ignore the traditional .desktop install paths used by DEB and RPM.
What's next
- Code signing — sign DEB and RPM packages with GPG.
- Sandboxing — how the Flatpak pipeline repackages your app.
- Publishing — release built artifacts to GitHub, S3, or a generic server.
- Gradle DSL reference — every
linux { }property.