[ANN] AppBundler v1.0: Native Installers for Julia Applications

Packaging a Julia application into a native installer has traditionally meant wrestling with platform-specific tooling on each target system. I am pleased to announce AppBundler v1.0 changes that. Turn any Julia application into a Snap (Linux), MSIX (Windows), or DMG (macOS) installer without leaving the Julia ecosystem, using cross-compiled utilities from Yggdrasil instead.

For those following along from earlier announcements, this release marks the first stable version with a substantial API redesign.

Getting started

If you can launch your application with julia --project=. -m MyApp, it’s ready to bundle. Install AppBundler as an app with ]app add AppBundler, make sure ~/.julia/bin is in your PATH, then build:

appbundler build . --build-dir=build --selfsign

This creates a platform-local bundle in the build directory.

This makes it easy to distribute your Julia app to non-technical users via a self-contained installer, avoid setting up packaging tools on every platform, and maintain a simple CI workflow for cross-platform bundling (e.g. Jumbo CI).

Compilation options

Mode Best for Trade-off
None Fastest builds, cross-platform bundling Compilation on the user’s machine
Pkgimages Fast builds, fast startup Loading overhead, larger binaries
Sysimage Fastest startup Longer build time
JuliaC Smallest binaries, fastest startup Requires explicit asset declarations

Sysimage and pkgimage strategies can be combined for applications that load some packages on demand. In all cases, non-Julia assets referenced with pkgdir(@__MODULE__) remain accessible.

Configuration

The resulting bundle can be configured via command line, LocalPreferences.toml, or by overriding configuration file templates within the project’s meta directory. All major LocalPreferences.toml use cases have been manually tested across all three platforms.

See Jumbo, PeaceFounderClient, BonitoBook, and KomaMRI for real-world use cases, and start experimenting with the listed examples. For detailed configuration and deployment options, see the documentation.

What’s next

With v1.0 shipped, the project enters maintenance mode. A second phase of NLNET funding is lined up, which, if approved, would support further development. Near-term plans include trimming indirect dependencies to speed up installation. If funding comes through, priorities include making code signing robust for Apple notarization, integrating MSIX code signing with a hardware token, and adding Flatpak support. Bundling non-Julia projects (Rust, Go) via cross-compilation is another interesting direction. If any of this aligns with your needs, I’m open to consulting and collaboration — feel free to reach out.

Thanks for the good work!

Question: As far as I know, PyPlot.jl is not supported. Which plotting libraries work well with AppBundler? Does Makie work? My experience is that even Plots.jl often fails to plot when put into a system image because the display stack is frozen at compile time.

Makie works just fine, which I had tested for bundling via ImageColorThresholderApp. I did hit an issue on Windows there, but it turned out to be a problem with my virtual machine setup that has issues with Makie itself. Pretty much every other plotting library should work out of the box except PyPlot.jl, as it depends on Python, which AppBundler does not bother bundling. For instance, bundling GR works just fine, which you can try with:

module SinPlotter

using GR

function (@main)(args)
    x = range(0, 2π, length=200) |> collect
    y = sin.(x)

    GR.plot(x, y,
        title = "Plot of sin(x)",
        xlabel = "x",
        ylabel = "y",
        linewidth = 2
    )

    println("Displaying plot. Press Enter to exit.")
    readline()

    return 0
end

export main

end # module SinPlotter

And build it the same way via appbundler build . --build-dir=build --selfsign.

Thanks for this, it really pushes the ecosystem forward!

Just confirming the status of a couple specific cases:

  1. A compiled CUDAApp would not download or compile on the user’s machine.
  2. Windows libraries made with JuliaC are still waiting for upstream support in JuliaC itself.

I would be very interested in this! I’ve had a lot of problems with snap on fedora so I have kind of given up on it. Could you estimate how much work it would be to add flatpak support? I might pick this up if you have no time

Same questions :wink: I don’t have a CUDA system, so I can’t test the built applications, but I do expect CUDA-enabled applications to run just fine as the relevant artefacts are bundled in. Regarding bundling libraries on Windows with MSIX, it may be possible using VFS — you would need to find the appropriate AppxManifest.xml configuration and bundle the libraries manually via the API (Reference · AppBundler.jl). This is a fairly sophisticated use case, so for more hands-on support I’m available for consulting to hammer out the details.

It’s a moderate level of work with some manual testing if one relies on flatpak-builder being installed on the host system. AppBundler so far distributes all tooling via Yggdrasil, and putting flatpak-builder there is a hard problem — it appears that flatpak-builder and the Flatpak runtime are interleaved, which couples it to the Linux kernel and makes the tooling very difficult to bundle independently.

That said, it seems feasible to build a Flatpak package using OSTree alone, which reduces the question to whether OSTree can be cross-compiled with a Yggdrasil recipe. I don’t have an answer to that yet, but it’s the first thing to figure out.

Thanks! Would be great to have this information in a github issue to track progress, if it isn’t already

Sure! Open an issue

AppBundler v1.1

After a nice summer, I’m back full time on AppBundler development. Recent PRs and requests have pushed me toward supporting more bundle formats, and this release delivers several of them.

Windows bootstrapper. A new bootstrapper installer for Windows has landed, which you can see in the video. Enable it for MSIX builds with the preference msix.bootstrapper = true.

AppImage support via --target-bundle=appimage. Great for distributing terminal-based applications. Particularly useful for HPC users hitting hydration problems on network drives.

DMG/HFS+ support. Previously, DMGs were based on ISO images. macOS accepts these fine, but they stand out against the industry default, which is now supported too. Enable it with the preference dmg.backend = "hfsplus".

Rather than fight with cross-compiling the open source HFS+ tooling, I had Claude Opus 5.5 implement the image builder directly, and it did so in one step: HFSImg.jl. This also opens the door to reproducible bundles, more on that later.

Other changes

  • Refactored -D argument parsing for overriding preferences from the command line.
  • The meta project can now rely on JuliaC, which makes builds easier to reproduce locally and shortens CI scripts.
  • Self-signing no longer applies to the DMG itself, only to the app inside. Signing both made installation doubly painful.

Breaking change: LocalPreferences.toml now uses nested syntax, for example juliac.trim. Templates have also been reworked around a {{{COMMAND}}} placeholder, which will enable bundling projects in other programming languages in the future.

Recent examples: PeaceFounderClient v0.1.4-alpha and the Jumbo v26.10.13 release.

AppBundler v1.2

I am thrilled to announce AppBundler v1.2, a step closer to a generic bundling solution. This release focuses on visual assets. It adds support of ICNS generation for DMG bundles, and it resolves the MSIX installer background issue with ability to specify the installer icon background plate. The code now uses the PNGFiles package directly and adds SVG support for icon sources via resvg_jll. You can also try these features independently of AppBundler through the new IconTools.jl package. The resulting dependency graph is much slimmer, with 98 dependencies compared to 159 at v1.0.1.

appbundler

This release also adds new features to the AppBundler CLI. You can now get a full overview of an AppBundler project by running:

appbundler status <project>

which prints the project’s directory structure and its customization options.

A new inline command:

appbundler inline <project> <target>

inlines an override into your project, for example:

appbundler inline . meta/dmg/icon.icns
appbundler inline . meta/dmg/certificate.pfx
appbundler inline . meta/dmg/Info.plist --render
appbundler inline . .github/workflows/Release.yml

where icon.icns and certificate.pfx are generated with the command wheras --render inlines template variables. Together with status, it makes setting up a project and discovering its customization options much easier.

Once the project is set up as you like, finish with:

appbundler build . --build-dir=build

to get the desired bundle. In the future, low level bundling commands stage and pack commands will enable bundling of non-Julia project. The design is under discussion in #47.

Future releases will focus on modularizing AppBundler according to #50 and #43. This will make it easier to support lazy loading improve maintainability and, eventually, make the AppBundler CLI snappier. Work is also ongoing to remove Conda and PyCall, and I hope the next release will get us there.

Great package! What are your thoughts on Makie/GLMakie compatibility in the near future? I strongly believe that GLMakie will allow for easy implementation of complex GUIs and 3D scenes when combined with agentic code generation. Having AppBundler + GLMakie support would be a game changer IMHO.

Currently, a major issue for adaption of Julia-based solutions in my social circle is the expectance of getting an installer + GUI. The willingness of learning Julia + VS Code + managing Pkgs is not high enough.

GLMakie might already work (I understand Makie should work even with GLMakie backend on) Windows. AppBundler should support anything that worked before you try to bundle (my understanding). So it’s misplaced that it doesn’t support [GL]Makie. Then it’s about this issue:

Please try if it works for you on Windows and Julia 1.13 (or 1.11/10). What I haven’t confirmed, but AI tells me is that, that issue is about running GLMakie in a VM. So maybe AppBundler.jl (and Makie) fully supports GLMakie. It’s just if you bundle on macOS and test in a VM there it fails.

GitHub - Clemapfel/Mousetrap.jl: Finally, a GUI Engine made for Julia · GitHub for GUI does say:

“All features are available for all 64-bit versions of Linux, FreeBSD, macOS, and Windows.”

I think “Does not launch on Windows” on it at AppBundler.jl may be outdated. Note though that it doesn’t depend on GLMakie by default (that support is optional, with it might not work on Windows? Not the problem of either package). It depends on Gtk4.jl:

“Gtk4.jl has also been reported to interfere with GLMakie.jl, though this doesn’t always seem to happen.”

As already mentioned, GLMakie is supported, and it’s already distributed with the Jumbo Julia distribution. However, I wouldn’t personally recommend it for GUIs in self-contained applications. Compilation on a headless server like GitHub CI is problematic, so some parts are still compiled at runtime, which makes the application very slow to show up. This could be avoided on machines with graphics capabilities, but those are often unavailable in CI.

Regarding GLMakie broken on Windows · Issue #5342 · MakieOrg/Makie.jl · GitHub, this isn’t specific to AppBundler but to GLMakie on Windows in a VM. On real Windows machines it works fine in practice.

There are quite many options available for making stand alone GUI applications in Julia that are supported well. See AppBundler/examples.

I will give it a try.

That works fine for me if configured correctly. See for example: AWEMeta.jl/.github/workflows at main · OpenSourceAWE/AWEMeta.jl · GitHub

Many of these workflows build a system image that includes GLMakie using Github runners on Windows, Linux and Mac. You need:

  • sufficient virtual memory; on Linux: use zram+swap
  • a dummy graphics device

On Linux:

DISPLAY=:0 xvfb-run -s '-screen 0 1024x768x24' ./scripts/test_kitecontrollers_main

On a Github Windows runner you need:

   # GLMakie needs OpenGL, which the runner has no driver for; Mesa renders in software
      - name: Install Mesa
        uses: ssciwr/setup-mesa-dist-win@v3
      

For a full example. look at the TE3.yml example in the repo linked above.

The workaround works well on Linux. However, I recall the issues were mainly on the Windows and/or macOS runners, where I couldn’t find an equivalent.

Compiling a large project including GLMakie worked in principle. However, I was not able to test the deployment due to certificate issues. This is not a AppBundler issue, but rather on my side.