Bazzite + FreeIPA: Running an Immutable Enterprise Desktop the Right Way

Meet bazzite-freeipa: A bootc-based container image that combines Fedora Atomic's gaming optimizations with enterprise-grade domain authentication.

Bazzite + FreeIPA: Running an Immutable Enterprise Desktop the Right Way
Photo by Evgeniy Kondratiev / Unsplash

The Problem: Gaming Linux Meets Enterprise Auth

If you're running Bazzite in an enterprise environment with FreeIPA domain authentication, you face a choice: use generic Fedora with stale gaming packages, or maintain a fork of Bazzite yourself to keep freeipa-client and dependencies available across updates.

Neither option is ideal. Until now.

What You'll Learn

  • How bazzite-freeipa bridges gaming and enterprise authentication
  • Why bootc's three-way merge preserves your domain join across updates
  • How to switch to this image from any bootc-based system
  • Setting up and troubleshooting FreeIPA client enrollment

What Is Bazzite-FreeIPA?

bazzite-freeipa is a custom bootc image built on Bazzite (from Universal Blue) that ships FreeIPA client packages pre-installed and pre-configured. More importantly, it's designed to preserve your existing FreeIPA domain join across every bootc update.

Key Features

✨ Pre-Installed FreeIPA Stack freeipa-client, sssd, oddjob, and oddjob-mkhomedir come pre-installed. No package hunting. Just enroll.

🔄 Join State Preserved Thanks to bootc's clever three-way merge, your Kerberos tickets and LDAP enrollment survive every update. Reboot into new image, keep authenticating.

🎮 Gaming-Optimized Base Built on Bazzite, you get Mesa, Nvidia drivers, Proton, GameMode, and all the gaming goodness—now with enterprise auth.

🛡️ Atomic & Immutable Atomic, image-based updates mean no broken configs. Roll back to a known good image with a single reboot if needed.


Why Bootc's Three-Way Merge Matters

The magic is in how bootc handles /etc configuration during updates. This is the foundation of persistent FreeIPA joins.

How It Works

When bootc applies an update:

  1. It diffs the old image's /etc against the new image's /etc
  2. It applies that delta to your local /etc
  3. Files that exist locally but are not in the image are treated as local additions and never touched

The Key Insight: bazzite-freeipa ships the directory skeletons for /etc/ipa/ and /etc/sssd/conf.d/, but ships no config file content inside them. Every file that ipa-client-install writes is therefore a local addition from bootc's perspective, never to be overwritten.

Your /etc/ipa/default.conf, /etc/sssd/sssd.conf, and /etc/krb5.conf are safe across updates. Runtime state under /var (caches, sockets, logs) is never touched by bootc at all.

What Could Break It

  • Running ipa-client-install --uninstall before updating, then expecting the join to survive (it won't)
  • Manually editing a file that a future image version also ships (currently none, by design)

Getting Started: Switching or Installing

From an Existing Bazzite or Universal Blue System

If you're already running a bootc-based system (Bazzite, Bluefin, Aurora, etc.), switching is a single command and a reboot—no reinstall needed.

sudo bootc switch ghcr.io/nativetexan70/bazzite-freeipa:latest

Stage the new image with the above command. Then reboot:

systemctl reboot

After rebooting, confirm you're on the new image:

sudo bootc status
Already joined to FreeIPA? If your current system is already enrolled in a FreeIPA domain, that join state is preserved automatically during the switch.

From a Fresh Install (Non-bootc Systems)

Download or build an ISO from the bazzite-freeipa repository. The installer's post-install script automatically switches the new system to ghcr.io/nativetexan70/bazzite-freeipa:latest.

Building disk images locally: The repository includes a Justfile for building QCOW2 and ISO images:

just build-iso-gnome    # Build a GNOME installer ISO
just build-iso-kde      # Build a KDE installer ISO
just run-vm-iso-gnome   # Run the GNOME ISO in a VM

Disk images are automatically built via GitHub Actions and can be triggered manually from the Actions tab.


Setting Up FreeIPA Client

Prerequisites

Before you begin:

  • Network access to your FreeIPA server
  • DNS resolution to your FreeIPA server and domain
  • A one-time password or admin credentials for enrollment
  • The correct FQDN hostname set on the machine

Step 1: Set the Hostname

This must be done before running ipa-client-install. The hostname is baked into your Kerberos principal at join time.

sudo hostnamectl set-hostname myhost.your.domain.example

Step 2: Join the Domain

All required packages are already installed. Run the FreeIPA enrollment script:

sudo ipa-client-install \
    --domain=your.domain.example \
    --server=ipa.your.domain.example \
    --realm=YOUR.DOMAIN.EXAMPLE \
    --mkhomedir \
    --no-ntp

Flag explanations:

  • --mkhomedir — Creates home directories automatically on first login via oddjob-mkhomedir (enabled in this image)
  • --no-ntp — Recommended if NTP is already managed elsewhere (e.g., systemd-timesyncd or Chrony)
  • --unattended — Add this flag for scripted enrollment, together with --password

Step 3: Verify

After enrollment completes, check that SSSD is running:

systemctl status sssd

Test that a domain user can be resolved:

id <domain-username>

If the user is found and home directory is created on first login, you're good to go.

Leaving the Domain

To remove the machine from FreeIPA cleanly:

sudo ipa-client-install --uninstall

Key Changes to the Base Bazzite Image

This image is built on top of ghcr.io/ublue-os/bazzite-gnome:stable and makes deliberate modifications to support FreeIPA client functionality.

Packages Added

Package Purpose
freeipa-client Core FreeIPA client tooling, also pulls in sssd, krb5-workstation, certmonger, and dependencies
oddjob D-Bus service that allows sssd to perform privileged operations on behalf of unprivileged processes
oddjob-mkhomedir PAM module that automatically creates home directories on first login for domain users

Systemd Units Enabled

Unit Purpose
sssd System Security Services Daemon—handles Kerberos auth, LDAP lookups, and caching
oddjobd D-Bus daemon for oddjob; must be running for home directory creation
podman.socket Inherited from Bazzite base; supports rootless containers

/etc Directory Structure

The image pre-creates the following empty directory skeletons to support bootc's three-way merge:

Path Permissions Purpose
/etc/ipa/ 0755 ipa-client-install writes default.conf here
/etc/sssd/conf.d/ 0750 Drop-in directory for SSSD config fragments

No config files are shipped inside these directories. Every file written by ipa-client-install is a local addition from bootc's perspective and will never be touched by an image update.

Runtime Directories

SSSD cache and socket directories under /var are pre-created to avoid race conditions on first boot:

Path Permissions
/var/lib/sss/db 0711
/var/lib/sss/pipes/private 0755
/var/log/sssd 0755

Hostname Preservation

The upstream Bazzite image ships /etc/hostname containing the default value bazzite. To prevent hostname resets across updates from breaking Kerberos, this image ships /etc/hostname as an empty file.

With an empty file in the image, bootc has no meaningful upstream value to merge against, so the hostname you set is always preserved across updates.

⚠️ Important: Set the correct FQDN hostname before running ipa-client-install. The hostname is baked into the Kerberos principal and LDAP host entry at join time.

Keeping the Image Updated

Automatic Updates

bootc can check for and stage updates automatically via a systemd timer. Enable it:

sudo systemctl enable --now bootc-fetch-apply-updates.timer

Updates are staged in the background and applied on the next reboot. (A reboot does not happen automatically unless you also configure a reboot schedule.)

Manual Updates

To trigger a manual update check:

sudo bootc upgrade
Your FreeIPA configuration is safe. After a bootc update and reboot, sssd reads the same config it had before the update, and domain authentication continues without intervention.

Building Locally & Contributing

Local Development

The repository includes a Justfile for local builds. Requires just and podman (both available on Universal Blue images by default).

just build          # Build the container image
just lint           # Run shellcheck on shell scripts
just format         # Run shfmt on shell scripts
just check          # Validate Justfile syntax
just clean          # Remove build artifacts

just build-qcow2        # Build QCOW2 disk image
just run-vm-qcow2       # Run QCOW2 in VM
just build-iso-gnome    # Build GNOME installer ISO
just run-vm-iso-gnome   # Run GNOME ISO in VM

GitHub Actions & Image Signing

Images are published to GHCR and signed with Cosign. To verify an image:

cosign verify --key cosign.pub \
  ghcr.io/nativetexan70/bazzite-freeipa:latest

The public signing key is in the repository at cosign.pub.


Use Cases

🏢 Enterprise Workstations Developers and sysadmins in FreeIPA-managed environments who want a modern, gaming-capable base without sacrificing domain integration. Get your gaming fixes and your domain auth.

🏠 Home Lab with Domain Auth Self-hosted environments with FreeIPA for user management. Run Bazzite as a workstation image, enjoy the latest Mesa and Proton, and authenticate against your homelab FreeIPA server.

🎓 University/Research Labs Academic institutions running FreeIPA for campus authentication. Researchers get a fully featured Linux gaming environment that integrates seamlessly with institution accounts.

📦 Containerized CI/CD Agents CI/CD pipelines that need FreeIPA client tooling available in container images. Use bazzite-freeipa as a base for building secure, authenticated container workloads.


Getting Help & Contributing

This image is part of the broader Universal Blue ecosystem. For questions, suggestions, and community support:

🔒 Security Note: Images are signed with Cosign. Always verify image signatures before deploying in production. The public key is available in the repository.

Conclusion

bazzite-freeipa solves a real problem: running a modern, gaming-capable Linux distribution that plays nice with enterprise FreeIPA authentication—without forking and maintaining your own image.

By leveraging bootc's three-way merge and shipping configuration directories (but not files), your FreeIPA enrollment survives every update. Switch with a single command, enroll with ipa-client-install, and never manage domain authentication again. The image handles it.

Whether you're a sysadmin in a corporate environment, a researcher in a university lab, or someone running a sophisticated homelab, bazzite-freeipa bridges the gap between enterprise security and modern Linux gaming.


View on GitHub →