How To Install Kotlin on Fedora 44

Install Kotlin on Fedora 44

Fedora 44 landed with a noticeably tighter package base than its predecessors, and that’s exactly where a lot of developers run into friction when they go looking for Kotlin. Type sudo dnf search kotlin on a fresh install and you’ll come up mostly empty — Kotlin isn’t in the default Fedora repositories, and it never really has been, despite being one of the more popular JVM languages for backend services, Android tooling, and increasingly, server-side microservices. That gap catches a lot of people off guard, especially those coming from Ubuntu or Debian where a quick apt install habit doesn’t translate cleanly.

Having managed production JVM workloads across a handful of distros, the honest answer is that “installing Kotlin” is rarely just one command. It depends on whether you’re setting up a personal dev box, a CI runner, or a build server that needs a pinned, reproducible toolchain. Fedora’s fast release cadence and its habit of shipping bleeding-edge OpenJDK builds (Fedora 44 ships with java-latest-openjdk tracking OpenJDK 26 early-access builds alongside stable LTS versions) adds another layer most tutorials gloss over entirely.

This guide walks through every practical installation path — DNF via COPR, SDKMAN, Snap, and manual binary installs — along with the JVM prerequisites nobody mentions until something breaks. It also covers the stuff that actually matters once Kotlin is running on a real machine: version pinning, memory tuning for the Kotlin daemon, firewall considerations if you’re exposing a Ktor service, and the errors that show up most often in the field. If you’re setting up a workstation for Android/Kotlin development or provisioning a build agent for a CI/CD pipeline, this should save a couple of hours of trial and error.

Why Kotlin Isn’t in Fedora’s Default Repos

Fedora’s packaging philosophy leans heavily on upstream-first tooling and strict licensing review, and Kotlin’s build pipeline — which pulls in JetBrains’ own toolchain, bundled dependencies, and a fairly aggressive release schedule — doesn’t fit neatly into Fedora’s package review process. JetBrains ships Kotlin primarily as a standalone compiler archive, a Homebrew formula, and a Snap package, explicitly recommending SDKMAN for Unix-like systems rather than distro-native packages. That’s a deliberate choice on their end: it keeps Kotlin’s release velocity decoupled from any single distro’s packaging cycle.

For sysadmins, this matters practically. It means Kotlin version management on Fedora looks more like Node.js or Rust than like installing gcc — you’re expected to use a version manager or handle the binaries yourself.

Prerequisites Before You Touch Kotlin

Confirm you’re actually on Fedora 44

Run this first, every time, especially on cloned VM images or provisioning scripts where the base image might be stale:

cat /etc/fedora-release
uname -r

Install a JDK

Kotlin compiles to JVM bytecode by default, so you need a working JDK before the compiler will do anything useful. Fedora 44 offers several OpenJDK builds — pick based on what you’re targeting:

sudo dnf install java-21-openjdk-devel

Java 21 LTS is the safe, boring choice for production toolchains, and it’s what most Kotlin/Gradle build configurations expect as a baseline right now. If you want to live closer to the edge, Fedora 44 also carries java-latest-openjdk-devel, which tracks OpenJDK 26 early-access builds — fine for experimentation, risky for anything you need to be stable six months from now.

Verify it installed correctly:

java -version
javac -version

If you have multiple JDKs installed (common on dev machines juggling Android Studio and server projects), set the active one explicitly:

sudo alternatives --config java
sudo alternatives --config javac

Skipping this step is the single most common reason kotlinc fails silently or picks the wrong bytecode target later.

Method 1: DNF via COPR (Recommended for DNF-Native Workflows)

Fedora’s community COPR repositories fill gaps like this one, and there’s an actively maintained Kotlin build specifically targeting Fedora releases, including Fedora 44. This is the closest thing to a “native” install experience.

sudo dnf copr enable goncalossilva/kotlin
sudo dnf install kotlin

If you also work with Kotlin/Native or want linting tools bundled in the same workflow:

sudo dnf install kotlin-native ktlint detekt

Why this method first: it integrates with dnf update, respects your system’s package cache, and — critically for server environments — plays nicely with configuration management tools like Ansible that expect RPM-based state tracking. If you’re provisioning a build server via Ansible or Puppet, this is the path that won’t fight your automation.

One caveat worth flagging from experience: COPR repos are community-maintained, not Red Hat/Fedora-official. Pin the repo and audit it before rolling it into a hardened production image. Run dnf copr enable with --assumeno first if you want to preview what’s being added before committing.

Method 2: SDKMAN (Best for Multi-Version Development)

SDKMAN is what JetBrains itself recommends for Unix-like systems, and it’s genuinely the better option if you need to juggle multiple Kotlin versions across projects — which, if you’ve worked on more than one codebase for longer than a year, you will.

Install SDKMAN:

curl -s "https://get.sdkman.io" | bash
source "$HOME/.sdkman/bin/sdkman-init.sh"

Restart your shell or source your .bashrc/.zshrc, then install Kotlin:

sdk install kotlin

Check what’s installed and switch between versions on demand:

sdk list kotlin
sdk use kotlin 2.4.0
sdk default kotlin 2.4.0

This is the method to reach for when a client project is pinned to Kotlin 1.9.x while your personal projects have moved to 2.4.x. SDKMAN keeps these isolated per-shell without any risk of one project’s build silently picking up the wrong compiler version — a class of bug that’s maddening to diagnose because the error messages rarely point back to the actual cause.

Making it stick across sessions and SSH logins

A subtle gotcha: SDKMAN initializes via your interactive shell config, so non-interactive SSH sessions (common in CI runners or cron jobs) won’t have kotlinc on the PATH unless you explicitly source the init script in the relevant service or script:

source "$HOME/.sdkman/bin/sdkman-init.sh"
kotlinc -version

Forgetting this is a classic cause of “it works on my machine but fails in CI.”

Method 3: Snap Package

If your workflow already leans on Snap — some Fedora shops do, particularly ones that standardized tooling across Ubuntu and Fedora fleets — this is a low-friction option:

sudo dnf install snapd
sudo ln -s /var/lib/snapd/snap /snap

Log out and back in (or reboot) so the snap paths register correctly, then:

sudo snap install kotlin --classic

The --classic flag matters here; Kotlin needs broader filesystem access than a strictly confined snap allows, since the compiler shells out to the JVM and touches arbitrary project directories.

Realistically, Snap is the least common choice among Fedora sysadmins specifically, mostly because Fedora’s own packaging culture tends to view Snap as a secondary citizen compared to Flatpak or native RPM. It works fine — just don’t expect it to feel like a first-class Fedora experience.

Method 4: Manual Binary Install

Sometimes you need full control — air-gapped environments, hardened servers where you can’t add third-party repos, or CI images built from scratch. Manual installation is more verbose but gives you exact version pinning with zero external dependency at install time.

  1. Download the release archive from GitHub Releases (check the current version first):
    KOTLIN_VERSION=2.4.0
    curl -L -o kotlin-compiler.zip \
      "https://github.com/JetBrains/kotlin/releases/download/v${KOTLIN_VERSION}/kotlin-compiler-${KOTLIN_VERSION}.zip"
  2. Unzip into a stable, version-tagged location:
    sudo mkdir -p /opt/kotlin
    sudo unzip kotlin-compiler.zip -d /opt/kotlin
  3. Add it to the system PATH — for a shared server, drop this into /etc/profile.d/ so it applies to every user session:
    echo 'export PATH=$PATH:/opt/kotlin/kotlinc/bin' | sudo tee /etc/profile.d/kotlin.sh
    sudo chmod +x /etc/profile.d/kotlin.sh
    source /etc/profile.d/kotlin.sh
  4. Verify:
    kotlinc -version

This is the approach for build servers where reproducibility matters more than convenience — you know exactly which binary is running, there’s no dependency on a third-party repo staying online, and you can bake it directly into a container image or golden AMI.

Testing the Installation

Regardless of which method you used, confirm everything works end to end with a real compile:

cat <<'EOF' > hello.kt
fun main() {
    println("Kotlin is running on Fedora 44")
}
EOF

kotlinc hello.kt -include-runtime -d hello.jar
java -jar hello.jar

If that prints the expected string, the compiler, runtime, and JVM are all correctly wired together. Don’t skip this — a compiler that “installs successfully” but can’t find a valid JDK will often fail only at compile time, not at install time, which wastes debugging effort later.

Performance Considerations for the Kotlin Daemon

Kotlin’s compiler runs a background daemon (kotlin-daemon) to speed up repeated compilations, similar in spirit to Gradle’s daemon. On memory-constrained VMs — think small cloud instances used as CI runners — this daemon can quietly eat into available RAM if left unmanaged.

Set explicit JVM heap bounds for the daemon via a kotlin-daemon.opts or by passing options at compile time:

kotlinc -J-Xmx1024m -J-Xms256m hello.kt -d hello.jar

On build servers handling concurrent CI jobs, this single tweak prevents the daemon from ballooning and starving other processes during traffic spikes in your pipeline. It’s a small thing, but it’s exactly the kind of small thing that turns into a 3 AM page when a dozen builds queue up simultaneously.

For disk I/O, keep Kotlin’s incremental compilation caches (.kotlin/sessions, Gradle’s .gradle cache) on fast local storage rather than network-mounted home directories — this matters a lot on shared build infrastructure where NFS latency can double build times without anyone noticing until someone benchmarks it.

Security Considerations

A few things worth locking down, particularly on shared or internet-facing build servers:

  • Pin exact Kotlin and JDK versions in CI configs rather than tracking “latest,” since JetBrains actively maintains security advisories and signs releases with published PGP keys.
  • Verify downloaded archives against published checksums when using the manual install method — don’t trust a curl pipe blindly on production infrastructure.
  • Keep the JDK patched via sudo dnf update java-21-openjdk\* on a regular cadence; JVM CVEs do surface, and an outdated JDK undermines any Kotlin-level hardening.
  • If Kotlin is compiling code for a Ktor or Spring Boot service that will bind to a network port, make sure firewalld rules are scoped tightly.
sudo firewall-cmd --permanent --add-port=8080/tcp
sudo firewall-cmd --reload

Never leave a dev-mode Ktor server bound to 0.0.0.0 on a box with a public IP, even “temporarily.” That’s a classic case of a testing shortcut turning into an exposed attack surface.

  • Run build processes under a dedicated, unprivileged service account rather than root — obvious in principle, frequently ignored in practice on smaller teams’ CI boxes.

Troubleshooting Common Errors

“kotlinc: command not found” after installation

Almost always a PATH issue. If you installed via SDKMAN, confirm the init line is present in your shell rc file. If manual, confirm /etc/profile.d/kotlin.sh is executable and sourced. Check with:

echo $PATH | tr ':' '\n' | grep kotlin

“Unable to find a JDK” or JAVA_HOME errors during compilation

Kotlin needs JAVA_HOME set correctly, especially when multiple JDKs coexist:

sudo alternatives --config java
echo 'export JAVA_HOME=$(dirname $(dirname $(readlink -f $(which java))))' | sudo tee -a /etc/profile.d/kotlin.sh

Kotlin daemon hangs or consumes excessive memory on low-RAM VMs

Cap the heap explicitly as shown above, or disable the daemon entirely for one-off CI compiles by passing -Xuse-daemon=false — slower per build, but predictable, which matters more on ephemeral runners.

COPR package conflicts with a manually installed version

If you previously installed Kotlin manually and then add the COPR repo, you can end up with two kotlinc binaries fighting over PATH precedence. Check with which -a kotlinc and remove the stale one, or scope PATH order deliberately.

Version mismatch between Kotlin and Gradle plugin

This shows up as cryptic “unsupported metadata version” errors. Cross-check the Kotlin Gradle plugin compatibility matrix before bumping either version independently — this is one of those errors where the stack trace actively misleads you about the root cause.

r00t is a Linux Systems Administrator and open-source advocate with over ten years of hands-on experience in server infrastructure, system hardening, and performance tuning. Having worked across distributions such as Debian, Arch, RHEL, and Ubuntu, he brings real-world depth to every article published on this blog. r00t writes to bridge the gap between complex sysadmin concepts and practical, everyday application — whether you are configuring your first server or optimizing a production environment. Based in New York, US, he is a firm believer that knowledge, like open-source software, is best when shared freely.

Related Posts