Milestone: Running an Offline Kemal App with DNF on Fedora Linux! 🎉

Hey everyone!

We wanted to share an exciting milestone from our work on the Fedora Crystal SIG (crystal-in-fedora):

Today, we successfully ran a Kemal web application entirely offline inside a network-isolated container (--net=none), after installing Kemal directly as a system library with dnf!

No git clone, no network calls, no shard.yml, no shard.lock, no local lib/ directory, and no vendored tarballs. Just:

# dnf install crystal crystal-kemal

And in a single script (app.cr):

require "kemal"

get "/" do
  "Hello from Fedora Crystal (100% offline)!"
end

Kemal.run

Running crystal run app.cr immediately compiled, resolved all transitive dependencies (radix, exception_page, backtracer), and served HTTP requests completely offline.


How Did We Do It?

While Fedora’s packaging guidelines permit standalone leaf applications to vendor dependencies (similar to Go and Rust using a vendor.tar.xz), distributing reusable shard libraries as unbundled system packages has historically been a challenge. Distributing compiled .so binaries doesn’t work for Crystal because of compile-time macros, generics monomorphization, and whole-program optimizations.

We solved this through three simple mechanisms:

  1. Native System Shard Path: When building the Fedora crystal compiler RPM, we injected /usr/share/crystal/shards into CRYSTAL_CONFIG_PATH:
    CRYSTAL_PATH="lib:/usr/share/crystal/shards:/usr/share/crystal/src"
    
  2. Shard-as-RPM: We package reusable shard libraries as uncompiled source trees into versioned directories (e.g. /usr/share/crystal/shards/kemal-1.14.0/). This leaves the full AST available to the compiler so macros, generics, and LLVM inlining work natively at build time.
  3. The LLVM Primary Symlink Model: To allow single scripts to do require "kemal" while still letting multiple library versions coexist on the system without RPM package collisions:
    • The primary crystal-kemal package provides an unversioned symlink:
      /usr/share/crystal/shards/kemal -> kemal-1.14.0
    • Compatibility packages (like crystal-kemal1.13) live in their own versioned directory without the unversioned symlink.
    • For complex projects with a shard.lock, our companion tool shards-link reads the lockfile and creates local symlinks in ./lib/ pointing directly to system packages—again, completely offline, reproducible, and without touching Git state.

Multithreading & Fibers

As a bonus during our verification, Crystal 1.21.1’s new Execution Contexts concurrency model and multi-threaded fibers ran effortlessly in the same offline container. Resizing the default execution context or creating worker pools scaled smoothly across system threads:

Observed threads: DEFAULT-0, DEFAULT-1, DEFAULT-2, DEFAULT-3

Concurrent compute fibers executed in parallel across all worker threads with zero hitches.


What Does This Mean for the Ecosystem?

  1. True Air-Gapped Development: You can now develop Crystal applications in high-security, offline environments (finance, telecom, defense, offline laptops) using standard system packages without needing internet access or manual vendoring.
  2. First-Class Distro Shards: Reusable Crystal shard libraries can be installed and managed cleanly via distribution packages, receiving system security updates and CVE tracking.
  3. Frictionless Scripting: Anyone can install a library with dnf and immediately write a one-off script using require "<shard>" without initializing a whole shard workspace.

Come Join the Fedora Crystal SIG!

We are actively building out the Crystal packaging ecosystem in Fedora and EPEL, testing compiler builds on Rawhide and Fedora 44+, and packaging key community shards.

If you love Crystal and want to help with packaging, testing, or shaping how Crystal integrates with Linux distributions, we would love to have you with us!

Cheers,
Rénich Bon Ćirić & Gemini (AI pair programmer collaborating with Rénich)

9 Likes