feat: setup houdini image build

This commit is contained in:
Simon Lambin
2026-09-09 08:31:28 +02:00
commit 105bb36b29
11 changed files with 485 additions and 0 deletions

52
houdini/Dockerfile Normal file
View File

@@ -0,0 +1,52 @@
# syntax=docker/dockerfile:1.7
#
# Base + install command follow SideFX's own docker/Ubuntu/Dockerfile
# (shipped inside the Houdini tarball). Ubuntu 22.04 matters here, not just
# as a default: gccXX.X-tagged tarballs need a libstdc++ new enough to match,
# and Ubuntu's base image keeps libstdc++6 updated while RHEL/Rocky pin their
# system toolchain for the OS lifetime.
FROM docker.io/library/ubuntu:22.04 AS base
ARG HOUDINI_TARBALL=houdini-linux.tar.gz
# Plain YYYY-MM-DD, no "SideFX-" prefix (that prefix is for the newer,
# unrelated houdini_installer launcher tool). Current EULA date: 2021-10-13,
# see https://www.sidefx.com/legal/license-agreement/
ARG HOUDINI_EULA_DATE=2021-10-13
RUN apt-get update && apt-get install -y --no-install-recommends \
sudo procps \
bc fontconfig wget libasound2 libgl1 libglu1 libglu1-mesa libglx0 \
libegl1 libatomic1 libxkbfile1 libxcb-cursor0 \
libice6 libnss3 libopengl0 libpci3 libsm6 libx11-6 libx11-xcb1 \
libxcb-icccm4 libxcb-image0 libxcb-keysyms1 libxcb-randr0 \
libxcb-render-util0 libxcb-render0 libxcb-shape0 libxcb-shm0 \
libxcb-sync1 libxcb-util1 libxcb-xfixes0 libxcb-xinerama0 \
libxcb-xkb1 libxcb1 libxcomposite1 libxcursor1 libxdamage1 \
libxext6 libxi6 libxkbcommon-x11-0 libxkbcommon0 libxrandr2 \
libxrender1 libxss1 libxt6 libxtst6 && \
rm -rf /var/lib/apt/lists/*
WORKDIR /tmp/houdini
COPY ${HOUDINI_TARBALL} ./houdini.tar.gz
# Flags match SideFX's own Dockerfile verbatim (space before the date, no
# '='). The installer creates /opt/hfs<major.minor> (e.g. /opt/hfs22.0), not
# a generic /opt/hfs — add our own stable symlink so houdini-run and the
# profile.d snippet don't need to know the version.
RUN tar xzf houdini.tar.gz --strip-components=1 && \
./houdini.install --auto-install --accept-EULA ${HOUDINI_EULA_DATE} && \
ln -s "$(find /opt -maxdepth 1 -type d -name 'hfs[0-9]*' | sort -V | tail -1)" /opt/hfs && \
rm -rf /tmp/houdini
COPY houdini-run /usr/local/bin/houdini-run
RUN chmod +x /usr/local/bin/houdini-run && \
printf 'if [ -d /opt/hfs ]; then _p="$PWD"; cd /opt/hfs && . ./houdini_setup >/dev/null 2>&1; cd "$_p"; fi\n' \
> /etc/profile.d/houdini.sh
FROM base AS amd
RUN apt-get update && apt-get install -y --no-install-recommends \
libgl1-mesa-dri libglx-mesa0 mesa-vulkan-drivers && \
rm -rf /var/lib/apt/lists/*
FROM base AS nvidia
# GL/Vulkan/CUDA come from the host driver via `distrobox --nvidia`.

203
houdini/README.md Normal file
View File

@@ -0,0 +1,203 @@
# Houdini Apprentice in distrobox, from a private registry
Two images built from one shared base:
- `houdini-amd` — Houdini + Mesa (radeonsi / RADV) for AMD/Intel GPUs
- `houdini-nvidia` — Houdini; the NVIDIA driver is injected at runtime by `distrobox --nvidia`
Files: `Dockerfile` (multi-stage), `houdini-run` (in-image launcher), `build-and-push.sh`.
> **Single-image alternative (honest note):** because `distrobox --nvidia`
> mounts the host driver at runtime, a *single* image (base + Mesa) usually
> works for both vendors, with the GPU chosen at `distrobox create` time. Two
> images are cleaner and leaner per vendor, which is what this setup does — but
> if you'd rather maintain one, build only the `amd` target and use it with or
> without `--nvidia`.
---
## 0. Prerequisites
- `podman` (or `docker`) and `distrobox` on the host.
- A private registry — see the [repo-level README](../README.md) for setup.
- Your Houdini Linux tarball from your SideFX account, e.g.
`houdini-22.0.429-linux_x86_64_gcc14.2.tar.gz`, placed next to the `Dockerfile`.
The `gccXX.X` suffix means the runtime needs a `libstdc++` new enough for
that build; that's why the base image is Ubuntu 22.04, not Rocky/RHEL —
Ubuntu's base image keeps `libstdc++6` updated, while RHEL-family distros
pin their system toolchain for the OS's lifetime.
- A SideFX account (Apprentice is free) to activate the license on first launch.
> This Dockerfile mirrors the one SideFX ships inside the tarball itself
> (`docker/Ubuntu/Dockerfile`) for the base image, package list, and install
> command — verified against the vendor's own recipe rather than guessed. Their
> package list is for headless `hython`/`sesinetd` use, though; running the
> actual `houdinifx` GUI needed 3 more runtime libs (`libegl1`, `libatomic1`,
> `libxkbfile1`), found by test-building and `ldd`-checking the binaries —
> already folded into the `Dockerfile`.
> **EULA / redistribution:** Houdini binaries are baked into these images. Keep
> the registry **private**. Do not push Houdini images to a public registry.
---
## 1. Build & push
Edit the variables at the top of `build-and-push.sh` (registry, version,
tarball name, EULA date), then run it. It builds the shared base once, then the
two variants, and pushes all tags. The base layers are stored only once in the
registry.
```bash
./build-and-push.sh
```
> **EULA date format:** plain `YYYY-MM-DD`, no `SideFX-` prefix (that prefix
> belongs to a different, unrelated tool — the newer credential-based
> `houdini_installer` launcher, not the `houdini.install` script used here).
> Current EULA date is `2021-10-13`; double check against
> https://www.sidefx.com/legal/license-agreement/ since SideFX can update it.
---
## 2. Use it via distrobox
Pick the image matching your GPU. The two `--additional-flags` bits below are
what make Apprentice licensing survive (see section 3):
- `--hostname houdini-apprentice` — stable machine name for the node-lock
- a bind mount of the `licenses` file so the activation persists
```bash
# One-time: create the persisted license file on the host.
mkdir -p ~/.houdini-apprentice
touch ~/.houdini-apprentice/licenses
```
**NVIDIA:**
```bash
distrobox create \
--name houdini \
--image registry.lan:5000/houdini-nvidia:latest \
--nvidia \
--additional-flags "--hostname houdini-apprentice -v $HOME/.houdini-apprentice/licenses:/usr/lib/sesi/licenses"
```
**AMD / Intel** (drop `--nvidia`; distrobox exposes `/dev/dri` automatically):
```bash
distrobox create \
--name houdini \
--image registry.lan:5000/houdini-amd:latest \
--additional-flags "--hostname houdini-apprentice -v $HOME/.houdini-apprentice/licenses:/usr/lib/sesi/licenses"
```
Then:
```bash
distrobox enter houdini
houdini-run # starts sesinetd, then launches Houdini FX
```
On **first launch** Houdini prompts to install the free Apprentice license: log
in with your SideFX account.
To add a real app-menu entry (icon, no terminal window) instead of a plain
binary shortcut, export a `.desktop` file from inside the box — the icon path
must point at Houdini's own bundled icon (there's no separate `.desktop` file
shipped, so this one is written by hand):
```bash
distrobox enter houdini
cat > /tmp/houdini.desktop << 'EOF'
[Desktop Entry]
Type=Application
Name=Houdini FX
Comment=Houdini Apprentice (distrobox)
Exec=houdini-run
Icon=/opt/hfs/houdini/cloudsubmit/houdini_icon.png
Terminal=false
Categories=Graphics;3DGraphics;Education;
StartupWMClass=houdinifx-bin
EOF
distrobox-export --app /tmp/houdini.desktop
```
`distrobox-export` copies the icon out to `~/.local/share/icons/` on the host
and writes `~/.local/share/applications/houdini-houdini.desktop`. Distrobox
also auto-creates a generic `houdini.desktop` ("Terminal entering Houdini")
for every box at creation time — set `NoDisplay=true` in that file (or delete
it) if you don't want both showing up in the app menu.
---
## 3. Apprentice licensing — read this
Apprentice is the fragile part, by design. Confirmed SideFX behaviour:
- **No Login Licensing** for Apprentice — only node-locked (workstation)
licenses served by a **local** `sesinetd`, which must be running for Houdini
to start at all.
- The license is **node-locked to the machine hardware _and_ the machine name**.
Change the hostname and the license is invalidated — hence the fixed
`--hostname houdini-apprentice`. Always create the box with the same hostname.
- Apprentice licenses are **valid 30 days**, then you reinstall a fresh free set
(a quick re-login in the same dialog).
- On Linux the keys live in `/usr/lib/sesi/licenses`. The bind mount in section
2 persists just that file into `~/.houdini-apprentice/`, so recreating or
re-pulling the container keeps the activation (within the 30 days, same host).
Confirmed by an actual Apprentice activation. Two gotchas that took a while
to track down, both now handled automatically by `houdini-run`:
- **File permissions.** `sesinetd` runs as `www-data`, not as your
distrobox user. A bind-mounted file created on the host with `touch`
belongs to your host user and isn't writable by `www-data`, so activation
fails with `Failed to open license file` in
`/var/log/sidefx/sesinetd.log` — surfaced in the GUI as an unrelated-
looking **"Success (curl error 0)"** error that just loops back to the
install-license prompt. `houdini-run` now `chmod 666`s the file before
starting `sesinetd` every time, so this self-heals.
- **Stuck self-signed cert.** If `sesinetd`'s self-signed TLS cert
generation is interrupted (it was, once, across our own container
restarts), it leaves 0-byte `auth.cert`/`auth.priv` files behind, and the
daemon then spins at ~100% CPU retrying against them forever instead of
regenerating — silently breaking activation with no obvious error.
`houdini-run` now deletes those files first if they're empty.
Practical consequences:
- **Same physical machine, stable hostname:** activate once; survives container
recreation and image updates until the 30-day expiry.
- **A different physical machine:** the hardware differs, so you re-activate
there (still free). The image is portable; the *license state* is per-machine.
Inspect or manage licenses from inside the box with `houdini-run hkey`
(graphical) or `sesictrl` (CLI).
> **"Container-enabled licenses" — why this doesn't apply here:** SideFX's own
> docker README warns that regular (non-floating) licenses need special
> "container enabled" entitlements to run `sesinetd` in Docker, and their own
> `docker-compose.yml` works around it with `network_mode: host`. 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`), so the container sees the real host network identity
> — same situation as SideFX's own host-network workaround. Regular Apprentice
> licensing is expected to work unmodified.
---
## 4. Known-fragile / to verify (honest status)
| Area | Confidence | Note |
|---|---|---|
| Overall Dockerfile + distrobox shape | High | Standard distrobox GUI/GPU pattern. |
| Base image + install command (`--auto-install --accept-EULA <date>`) | High | Copied from SideFX's own `docker/Ubuntu/Dockerfile`; test-built end to end against the real 22.0.429 tarball. |
| `/opt/hfs` symlink | High | The installer only creates a major.minor symlink (`/opt/hfs22.0`); the Dockerfile adds the generic `/opt/hfs` one itself — confirmed by test build. |
| EULA date value | Medium | `2021-10-13` as of writing; SideFX can update the EULA without notice, check the legal page. |
| Licensing works under distrobox's shared netns | High | Confirmed with a real Apprentice activation via the GUI, over the host's shared network namespace. |
| Runtime library list | High | Vendor's list plus 4 libs it was missing for the GUI (`libegl1`, `libatomic1`, `libxkbfile1`, `libxcb-cursor0`), all confirmed by `ldd`-checking `houdinifx-bin`/Qt plugins and by an actual successful launch. |
| `houdini-run` strict-mode compatibility | High | `houdini_setup` isn't written for `set -euo pipefail` (unset vars, `which java` expected to fail silently) — fixed by relaxing those flags just around the `source`; confirmed end to end. |
| `sesinetd` auto-start via the launcher | High | Confirmed under distrobox's real non-root user + sudo setup, including the license-file-permission and stuck-cert fixes described in section 3. |
| distrobox default hostname | Medium | We pin it explicitly rather than trust the default. |
| Actual GUI rendering (X11/Wayland via XWayland) | High | Confirmed: real AMD hardware acceleration (`glxinfo` reports direct rendering, AMD Radeon Vega renderer, not a software fallback), Houdini FX runs and renders normally through XWayland on a Hyprland session. First launch was noticeably slow; subsequent launches were fluid. |
| AMD OpenCL (sims) | Low | Not set up here; needs ROCm/rusticl. GL/Vulkan viewport is fine. |

21
houdini/build-and-push.sh Normal file
View File

@@ -0,0 +1,21 @@
#!/usr/bin/env bash
# Build both variants (shared base) and push. Heavy: run when ready.
set -euo pipefail
REGISTRY="${REGISTRY:-registry.lan:5000}"
VERSION="${VERSION:-22.0.429}"
TARBALL="${TARBALL:-houdini-22.0.429-linux_x86_64_gcc14.2.tar.gz}"
# Date the EULA at https://www.sidefx.com/legal/license-agreement/ was last
# updated, format YYYY-MM-DD, no "SideFX-" prefix.
EULA_DATE="${EULA_DATE:-2021-10-13}"
ENGINE="${ENGINE:-podman}"
ARGS=(-f Dockerfile --build-arg "HOUDINI_TARBALL=${TARBALL}" --build-arg "HOUDINI_EULA_DATE=${EULA_DATE}")
for target in amd nvidia; do
"${ENGINE}" build "${ARGS[@]}" --target "${target}" \
-t "${REGISTRY}/houdini-${target}:${VERSION}" \
-t "${REGISTRY}/houdini-${target}:latest" .
"${ENGINE}" push "${REGISTRY}/houdini-${target}:${VERSION}"
"${ENGINE}" push "${REGISTRY}/houdini-${target}:latest"
done

47
houdini/docker/README Normal file
View File

@@ -0,0 +1,47 @@
There are two versions of the Dockerfiles, one for Ubuntu Linux and one for
Windows. Note that the Ubuntu image size is much smaller.
Prerequisites:
1. You need Docker and Docker-compose installed if you're on Linux.
2. You need Docker Desktop installed if you're on a Windows or Mac host.
3. You need to modify "EULA_DATE" in the Dockerfile to specify the date for the
installer's `accept-EULA` argument. For example, if the EULA at
https://www.sidefx.com/legal/license-agreement/ was last updated on
April 10, 2020, you would set "EULA_DATE" in the Dockerfile to 2020-04-10.
4. To run sesinetd within docker you must have container enabled licenses.
Regular licenses will not work within containers.
To create a Houdini Docker image:
1. Download a build of Houdini from www.sidefx.com for the container's platform.
4. Navigate to one of the "Ubuntu" or "Windows" folders.
3. Ensure you have set "EULA_DATE" in the Dockerfile.
4. Run `docker-compose build`
Some handy docker-compose commands:
1. `docker-compose run -d -p 1715:1715 sesinetd` will create a container
running the license server (sesinetd).
2. `docker-compose run hython` will start up hython in the interactive terminal
running the container. In the Linux container hython is in
/opt/hfs[VERSION].
In the Windows container, hython is in
C:/Program Files/Side Effects Software/Houdini[VERSION].
3. `docker-compose build` will build/rebuild the Houdini Docker images.
Some notes about Docker:
1. Docker requires that the Windows image version matches your host's Windows
version. The current Windows Houdini image is based on windows:1903; you
might need to update the Dockerfile accordingly.
2. The Mac and Windows versions do not support the "host" network, but host is
reachable on the special DNS name "host.docker.internal". If you have a
running sesinetd license server on host and would like to connect to it for
license checkout, you could switch point hserver to it.
3. All platforms (Mac/Linux/Windows) support the Linux version image. Only
Windows hosts support the Windows image.
4. Don't forget to toggle which daemon (Linux or Windows) the Docker CLI talks
to if you are using Windows Docker Desktop. You can switch it from the
Docker Desktop menu by selecting:
"Switch to Windows containers to use Windows containers" and
"Switch to Linux containers to use Linux containers"

View File

@@ -0,0 +1,15 @@
FROM ubuntu:22.04
RUN apt-get update && apt-get install -y --no-install-recommends bc fontconfig wget libasound2 libgl1 libglu1 libglu1-mesa libglx0 libice6 libnss3 libopengl0 libpci3 libsm6 libx11-6 libx11-xcb1 libxcb-icccm4 libxcb-image0 libxcb-keysyms1 libxcb-randr0 libxcb-render-util0 libxcb-render0 libxcb-shape0 libxcb-shm0 libxcb-sync1 libxcb-util1 libxcb-xfixes0 libxcb-xinerama0 libxcb-xkb1 libxcb1 libxcomposite1 libxcursor1 libxdamage1 libxext6 libxi6 libxkbcommon-x11-0 libxkbcommon0 libxrandr2 libxrender1 libxss1 libxt6 libxtst6 && rm -rf /var/lib/apt/lists/*
RUN mkdir houdiniInstaller
COPY houdini* /houdiniInstaller/
# Please update the following EULA_DATE to match the latest updated
# date of EULA in yyyy-mm-dd format. E.g. "2020-05-05"
# For Houdini 18.0 and previous version, EULA_DATE should be left empty
ARG EULA_DATE=""
RUN tar -xf /houdiniInstaller/houdini* -C /houdiniInstaller \
&& ./houdiniInstaller/houdini*/houdini.install --auto-install --accept-EULA ${EULA_DATE} \
&& rm -r /houdiniInstaller

View File

@@ -0,0 +1,11 @@
version: '3'
services:
hython:
container_name: hython
hostname: hython
build:
context: .
dockerfile: Dockerfile
network_mode: host
image: houdini:22.0

View File

@@ -0,0 +1,19 @@
FROM mcr.microsoft.com/windows:1903
SHELL ["powershell", "-Command", "$ErrorActionPreference = 'Stop'; $ProgressPreference = 'SilentlyContinue';"]
COPY "houdini-*-win64-vc141.exe" "C:\\"
# Please update the following EULA_DATE to match the latest updated
# date of EULA in yyyy-mm-dd format. E.g. "2020-05-05"
# For Houdini 18.0 and previous versions, EULA_DATE should be left empty
ARG EULA_DATE=""
RUN Start-Process "C:\\houdini-*-win64-vc141.exe" '/S /AcceptEULA=${EULA_DATE} /DesktopIcon=No /FileAssociations=No /StartMenu=No' -Wait;
RUN Remove-Item -Force "C:\\houdini-*-win64-vc141.exe"
RUN $vtokens = (Get-ItemProperty -Path 'HKLM:\SOFTWARE\Side Effects Software').ActiveVersion.Split('.'); \
$helppath = (Get-ItemProperty -Path 'HKLM:\SOFTWARE\Side Effects Software\Houdini').psobject.properties[$vtokens[0] + '.' + $vtokens[1] + '.0.' + $vtokens[2]].Value + 'houdini\help'; \
Remove-Item -Force $helppath -Recurse;

View File

@@ -0,0 +1,11 @@
version: '3'
services:
hython:
container_name: hython
hostname: hython
build:
context: .
dockerfile: Dockerfile
image: houdini:22.0

34
houdini/houdini-run Normal file
View File

@@ -0,0 +1,34 @@
#!/usr/bin/env bash
# Start sesinetd (required for Apprentice), then launch Houdini.
set -euo pipefail
HFS="${HFS:-/opt/hfs}"
# distrobox forwards the host shell's environment into the container. On a
# host that sets LD_LIBRARY_PATH itself (e.g. NixOS), that leaks in libs
# built for the host's glibc, which crash Houdini's binaries at load time.
# houdini_setup only ever *prepends* to LD_LIBRARY_PATH, so start it clean.
unset LD_LIBRARY_PATH
# houdini_setup isn't written for strict mode (unset vars, commands like
# `which java` that are expected to fail silently), so relax around the source.
_p="$PWD"; cd "${HFS}"
set +euo pipefail; source ./houdini_setup >/dev/null; set -euo pipefail
cd "${_p}"
if ! pgrep -x sesinetd >/dev/null 2>&1; then
# sesinetd runs as www-data (dropped from root by sudo below); a license
# file bind-mounted from the host belongs to the host user instead and
# isn't writable by www-data, which makes activation silently fail
# ("Failed to open license file" in /var/log/sidefx/sesinetd.log, visible
# in the GUI as an unrelated-looking "Success (curl error 0)" error).
sudo chmod 666 /usr/lib/sesi/licenses 2>/dev/null || true
# If self-signed cert generation ever gets interrupted, it leaves 0-byte
# cert/key files behind; sesinetd then spins at ~100% CPU retrying
# against them forever instead of regenerating. Clear those out first.
for f in /usr/lib/sesi/ssl/auth.cert /usr/lib/sesi/ssl/auth.priv; do
[ -e "$f" ] && [ ! -s "$f" ] && sudo rm -f "$f"
done
sudo /usr/lib/sesi/sesinetd >/dev/null 2>&1 &
sleep 2
fi
exec "${@:-houdinifx}"