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:
- Native System Shard Path: When building the Fedora
crystalcompiler RPM, we injected/usr/share/crystal/shardsintoCRYSTAL_CONFIG_PATH:CRYSTAL_PATH="lib:/usr/share/crystal/shards:/usr/share/crystal/src" - 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. - 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-kemalpackage 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 toolshards-linkreads the lockfile and creates local symlinks in./lib/pointing directly to system packages—again, completely offline, reproducible, and without touching Git state.
- The primary
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?
- 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.
- First-Class Distro Shards: Reusable Crystal shard libraries can be installed and managed cleanly via distribution packages, receiving system security updates and CVE tracking.
- Frictionless Scripting: Anyone can install a library with
dnfand immediately write a one-off script usingrequire "<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!
- Fedora Crystal SIG Wiki: SIGs/Crystal - Fedora Project Wiki
- Packaging & Tooling on Codeberg: Fedora Crystal - Codeberg.org
- Copr Repositories (F44/Rawhide x86_64 & aarch64): Making sure you're not a bot!
- Matrix Chat:
#crystal:fedoraproject.org
Cheers,
Rénich Bon Ćirić & Gemini (AI pair programmer collaborating with Rénich)