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.
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:
- It diffs the old image's
/etcagainst the new image's/etc - It applies that delta to your local
/etc - 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 --uninstallbefore 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 viaoddjob-mkhomedir(enabled in this image)--no-ntp— Recommended if NTP is already managed elsewhere (e.g.,systemd-timesyncdor 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 abootc updateand reboot,sssdreads 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:
- Universal Blue Forums — Community discussion and support
- Universal Blue Discord — Real-time chat with maintainers and community
- bootc Discussions — bootc-specific topics and questions
- bazzite-freeipa Repository — Issues, pull requests, and source code
🔒 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.