9.8 KiB
Houdini image — design notes and debugging history
Context for whoever (Claude) touches this Dockerfile next. Simon doesn't need
to read this — it's implementation reasoning, not usage docs (those are in
README.md).
Why these choices
- Base image + install command copy SideFX's own
docker/Ubuntu/Dockerfile(shipped inside the Houdini tarball underdocker/Ubuntu/), not guessed. That reference folder has been deleted from this repo once its contents were folded in here — it only ever served as a source to copy from, and keeping it around invited drift between two copies of the same info. - Ubuntu 22.04, not Rocky/RHEL. The tarball is
gccXX.X-tagged (heregcc14.2), meaning the runtime needs alibstdc++new enough for that build. Ubuntu's base image keepslibstdc++6updated via regular package updates; RHEL-family distros pin their system toolchain for the whole OS lifetime, so a Rocky 9 base risks aGLIBCXX_xversion mismatch depending on which Houdini build you're on. This was a real, not hypothetical, concern with the 22.0.429/gcc14.2 build tested here. --auto-install --accept-EULA <date>, no--make-dir, no--install-license. An earlier draft of this Dockerfile invented an--install-license <path>flag that doesn't exist, based on scripted- install docs that actually describe a different tool (houdini_installer, the newer credential-based launcher — irrelevant here, since it needs network + stored SideFX credentials at build time, which is worse for a private-registry-baked-tarball workflow). The flags actually used are copy-pasted from SideFX's own Dockerfile.- EULA date is a plain
YYYY-MM-DD, noSideFX-prefix. TheSideFX-YYYY-MM-DDform belongs tohoudini_installer's config file, not tohoudini.install. Mixing the two up silently breaks--accept-EULAmatching. Value as of writing:2021-10-13(from https://www.sidefx.com/legal/license-agreement/ — SideFX can update this without notice, it's not pinned/checked anywhere). /opt/hfssymlink is created explicitly.houdini.install's default behavior only creates a major.minor symlink (/opt/hfs22.0here), not a generic/opt/hfs. The Dockerfile globs for the installedhfs[0-9]*dir and symlinks it to/opt/hfsitself, sohoudini-runand the/etc/profile.dsnippet don't need to know the exact version.
Runtime library list
SideFX's own package list in docker/Ubuntu/Dockerfile is built for headless
hython/sesinetd use (their docker-compose.yml only ever runs hython
or sesinetd as separate services, never the GUI). Running the actual
houdinifx GUI needed 4 more libs, each found by ldd-checking the real
binaries/plugins in a test build and confirmed missing before adding:
libegl1— EGL dispatch,houdinifx-binlinked against it directly.libatomic1—houdinifx-bin/hython-binboth needed it.libxkbfile1— same.libxcb-cursor0— this one took the longest to find. Qt6'sxcbplatform plugin (libqxcb.so, under/opt/hfs<ver>/dsolib/Qt_plugins/platforms/) hard-depends on it (a known Qt6 change from Qt5, where cursor theming moved from Xcursor to xcb-cursor). Without it, Houdini fails at Qt platform-plugin init with a generic "no Qt platform plugin could be initialized" — nothing points at the actual missing symbol unless youlddthe plugin directly.
If a future Houdini/Qt version adds new failures here, the fastest diagnosis
path is: ldd every .so under dsolib/Qt_plugins/ recursively (see git
history of this file / session transcript for the exact one-liner), not
guessing from the vague top-level Qt error message.
houdini-run strict-mode compatibility
houdini_setup_bash (sourced by houdini-run to set up $HFS/PATH/etc.)
is not written for set -euo pipefail:
- It references variables that are legitimately unset in some code paths
(
set -uwould abort). - It does things like
d=$(which java)expecting a silent empty result on failure (set -ewould abort on the failedwhich).
Fix: wrap the source ./houdini_setup call with set +euo pipefail /
set -euo pipefail around it, rather than trying to patch the vendor
script.
NixOS / distrobox host environment leakage
Distrobox forwards the host shell's environment into the container by
design (needed for DISPLAY, WAYLAND_DISPLAY, XDG_*, D-Bus, etc. to
work at all for GUI apps). On a NixOS host that also sets its own
LD_LIBRARY_PATH (observed here pointing at a Nix-store alsa-lib built
against a much newer glibc than Ubuntu 22.04 ships), that leaks into the
container and crashes Houdini's binaries at dynamic-link time
(GLIBC_ABI_GNU2_TLS not found, etc.) — a library meant for the host's
libc, loaded into a binary linked against the container's older libc.
houdini_setup_bash only ever prepends to LD_LIBRARY_PATH if it's
already set — it never resets it. So houdini-run explicitly
unset LD_LIBRARY_PATH before sourcing it, discarding whatever leaked in
from the host and letting Houdini build its own from scratch. This is safe
on any host, not just NixOS — it just happens to matter there.
This class of problem (host env leaking into the container and colliding with container-local binaries) is worth checking first if a new, unexplained crash shows up after this image has otherwise worked before.
sesinetd / Apprentice licensing — the two real bugs
Both now handled automatically inside houdini-run (not just documented as
manual steps), found via /var/log/sidefx/sesinetd.log after a genuinely
confusing symptom in the GUI:
- License file permissions.
sesinetddrops privileges towww-dataafter being started as root viasudo. The license file bind-mounted from the host (~/.houdini-apprentice/licenses:/usr/lib/sesi/licenses) is created withtouchas the host user, so it's not writable bywww-datainside the container. Result: activation's network round-trip to SideFX succeeds fine (curl-level success), but writing the resulting license locally fails — surfaced in the GUI as the deeply misleading "Success (curl error 0)" error dialog that just loops back to the install-license prompt with no indication anything is actually wrong. The real error only shows up in/var/log/sidefx/sesinetd.log:[FATAL - Licensing] [Manager] - Failed to open license file.Fix:houdini-runnowchmod 666s the license file before startingsesinetd, every time, unconditionally. - Stuck self-signed TLS cert. If cert generation for
sesinetd's HTTPS listener gets interrupted (observed here across our own repeated container kill/restart cycles during testing), it leaves 0-byte/usr/lib/sesi/ssl/auth.cert/auth.privbehind. On next start,sesinetdtries to read those, fails (Bad certificate chain: 'PEM lib'), and — rather than regenerating — spins retrying at ~100% CPU forever, never actually finishing startup or opening port 1715. No error surfaces in the GUI for this one at all; the only symptom is a pegged CPU core and a license dialog that never succeeds. Fix:houdini-rundeletes those two files first if either is 0 bytes.
Also confirmed, contra a plausible-sounding but wrong worry: SideFX's own
docker README warns that regular (non-floating) licenses need special
"container enabled" entitlements to run sesinetd in Docker, working around
it with network_mode: host in their own docker-compose.yml. That warning
is about Docker's default isolated network namespace confusing the
node-lock check. Distrobox shares the host's network namespace by default
(no --unshare-netns), which is the same situation as SideFX's own
workaround — so this doesn't apply here, and was confirmed by an actual
successful Apprentice activation.
Confidence table (as of the last real end-to-end test)
Everything below was confirmed with an actual build + an actual
distrobox create + houdini-run launch on a real AMD iGPU (Vega,
raven) host, not just read from docs.
| Area | Confidence | Note |
|---|---|---|
| Base image + install command | High | Matches SideFX's own Dockerfile; built end to end against the real 22.0.429/gcc14.2 tarball. |
/opt/hfs symlink |
High | Confirmed by test build. |
| EULA date value | Medium | 2021-10-13 as of writing; unpinned, SideFX can change it. |
| Licensing works under distrobox's shared netns | High | Confirmed with a real Apprentice activation. |
| Runtime library list | High | Vendor's list + 4 libs added, all confirmed via ldd and an actual successful launch. |
houdini-run strict-mode compatibility |
High | Confirmed end to end. |
sesinetd license-file-permission and stuck-cert fixes |
High | Both bugs actually hit during testing, root-caused via /var/log/sidefx/sesinetd.log, fixed in houdini-run, confirmed working after the fix. |
| distrobox default hostname | Medium | Pinned explicitly (houdini-apprentice) rather than trusting the default, since Apprentice node-locks to hostname too. |
| GUI rendering via XWayland (Hyprland host) | High | Confirmed real AMD hardware acceleration (glxinfo: direct rendering, AMD Radeon Vega renderer, not llvmpipe). First launch was slow (container's first-run package install + cold start); later launches fluid. |
| AMD OpenCL (sims) | Low | Not set up; needs ROCm/rusticl. GL/Vulkan viewport is unaffected. |
| NVIDIA variant | Untested | Builds fine (same base, no extra packages), but never run against real NVIDIA hardware — only the amd target has been used end to end. |
Repo-wide conventions
See the top-level ../README.md for the multi-software repo convention
(one self-contained folder per app, shared registry setup notes) — that
file is meant for Simon to read; this one and the top-level ../CLAUDE.md
(if one exists) are for future-Claude context.