FreeIPA server running in Docker on Debian 13 / cgroups v2.
./setup.sh # generate .env and docker-compose.yml
make start # start (installs IPA on first run, ~5 min)
make logs # follow installation progress
make status # check service health after install| Target | Description |
|---|---|
make setup |
Re-run interactive setup wizard |
make start |
Start the container |
make stop |
Stop the container gracefully |
make restart |
Restart the container |
make logs |
Follow live logs |
make status |
Container status + ipactl status |
make shell |
Open bash inside the container |
make update |
Pull new image and recreate (data preserved) |
make clean |
Destructive — wipe data and force reinstall |
These settings are non-obvious and all required. Removing any of them breaks the install.
FreeIPA runs systemd as PID 1. On Debian 13 with cgroups v2 and Docker's systemd cgroup driver, systemd inside a container cannot create its own cgroup namespace without access to the host's cgroup tree.
Without these: container exits immediately with code 255 (systemd fails to start).
Using privileged: true causes a hard failure during installation:
GSS ERROR gss_acquire_cred_from() failed: SPNEGO cannot find mechanisms to negotiate
KerberosError: No valid Negotiate header in server response
FreeIPA server configuration failed.
The installer runs ipa-client-install --on-master as a final step. This authenticates to the local Apache via GSSAPI/SPNEGO using gssproxy. In a privileged container, something in the kernel's security context breaks gssproxy's ability to provide SPNEGO credentials to mod_auth_gssapi.
Remove privileged: true entirely. The container does not need it.
FreeIPA requires the IPv6 loopback interface (::1) to be active, even when you're not using IPv6 for anything. Without this sysctl, some internal Kerberos operations that bind to ::1 fail.
This replaces the sysctl -w call that fix-permissions.sh was previously doing at runtime.
The IPA installer detects it is running in a container and checks available RAM by reading:
- cgroup v2:
/sys/fs/cgroup/memory.maxand/sys/fs/cgroup/memory.current
With cgroup: host, the container sees the full host cgroup tree. The root cgroup does not have memory.max at /sys/fs/cgroup/memory.max — that file lives in the container's actual cgroup slice (e.g. /sys/fs/cgroup/system.slice/docker-xxx.scope/). The installer cannot find it and throws:
Unable to determine the amount of available RAM
FreeIPA server configuration failed.
--skip-mem-check bypasses this check. The host has sufficient RAM; the check is just broken by the cgroup namespace setup.
Standard requirement for any container running systemd. Without tmpfs on /run, systemd's socket and PID files conflict across restarts.
Runs as a background loop during installation. Keeps /var/lib/ipa/gssproxy/ owned root:ipaapi (0:289) with mode 770, and http.keytab with mode 660.
The IPA installer (or the volume template initialization) resets this directory to root:root 700. If gssproxy cannot read its keytab because of wrong permissions, authentication fails. The script detects and corrects this faster than the installer can reset it.
Replaces the stock /usr/sbin/ipa-server-configure-first during installation. Starts fix-permissions.sh in the background, runs the original installer, then kills the background fixer when done.
- Container starts,
populate-volume-from-templatecopies defaults from the image's/data-templateinto./data/ - Installer runs (~5 minutes). Watch with
make logs. - On success: container exits with code 123 (systemd shuts down) and Docker restarts it
- Second start: IPA services come up normally (
ipactl start)
Installation is complete when you see:
FreeIPA server configured.
All IPA state lives in ./data/. The container can be destroyed and recreated without losing data. IPA is considered "installed" when ./data/etc/ipa/ca.crt exists.
To force a full reinstall: make clean.