# DaVinci Resolve image — design notes 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`). **Update 2026-09-09: the `amd` and `nvidia` targets both build successfully**, tested with a real `DaVinci_Resolve_21.1_Linux.run` on Docker Desktop/WSL2 (Windows host, no distrobox available there). See "Build test" below for what was actually verified and how. What's still unverified: an actual `distrobox create` + `davinci-run` GUI launch on real Linux/GPU hardware — Windows can't test that part of the stack at all. Treat every "High" below as "confirmed by an actual build", "Medium/Low" as still resting on prior art alone. ## Prior art checked before writing this - [zelikos/davincibox](https://github.com/zelikos/davincibox) — Fedora toolbox base, actively maintained, tested by its maintainer against real AMD/Intel GPUs (no NVIDIA hardware). This is where the base image, `davinci-dependencies` package list, the silent-install invocation, and the patchelf fix all come from, read as raw files from GitHub (not paraphrased from search snippets). - [fat-tire/resolve](https://github.com/fat-tire/resolve) — Rocky Linux base, raw podman/docker (no distrobox), bakes Resolve into the image at **build time** using the same `-i -a -y` installer flags. Cross-checking this against davincibox's `setup-davinci` (which uses the same flags, at container-runtime instead) is what gave confidence that `-i -a -y` is a real, working non-interactive install path and not a guess — two independent projects landed on the same flags. - Universal Blue / openSUSE forum threads, Blackmagic forum thread: read for context, not copied from directly. ## Why these choices - **Fedora toolbox base, not Ubuntu (unlike Houdini).** Kept as-is from davincibox rather than switched to Ubuntu for consistency with Houdini, because it's the only base upstream has actually run real Resolve installs against, and it already ships the sudo/user bootstrap distrobox expects. The repo convention explicitly allows a different base per app. - **Resolve baked in at build time, not via a post-`distrobox create` setup script.** davincibox installs Resolve *after* the box exists (`setup-davinci`), because it's meant to be a generic base image handed out to many users with different Resolve versions. This repo's own convention is the opposite: bake the actual software in and push a ready-to-run image (see Houdini). Confirmed this is actually possible (not just theoretically) by finding fat-tire/resolve, which does exactly this — bakes Resolve into a Dockerfile `RUN` step with no display attached, using the same installer flags. - **`-i -a -y` install flags.** Not documented anywhere by Blackmagic directly (as far as this research found) — the confidence here comes from two independent community projects (davincibox, fat-tire/resolve) using the exact same flags to drive the same `.run`/AppImage installer non-interactively. `QT_QPA_PLATFORM=minimal` + `SKIP_PACKAGE_CHECK=1` layered on top come from davincibox specifically (zelikos/davincibox#35) — fat-tire/resolve doesn't set these, so it's possible they're only needed in some environments; kept them since we're building with no display at all, which is the case they're meant for. - **patchelf fix, copied verbatim including the full library paths.** davincibox's `setup-davinci` builds `--add-needed` arguments from full paths (`/usr/lib64/libglib-2.0.so.0`, ...), not bare sonames. This looks unusual — patchelf's `--add-needed` conventionally takes a soname — but it's copied exactly as upstream has it working, rather than "corrected" based on how patchelf is normally used elsewhere. If this turns out not to work, that's the first thing to check against upstream's current version of the script (it may have changed since this was written). - **`amd` variant installs both ROCm and Intel's `intel-compute-runtime`.** Matches davincibox's single `-opencl` variant, which is the same reasoning Houdini's `Dockerfile` uses for "amd variant also works for Intel" — no separate Intel target. `mesa-libOpenCL` (rusticl) is removed first because it's confirmed (zelikos/davincibox#173) to break ROCm. - **`nvidia` variant is untouched (no OpenCL packages added).** Matches Houdini's NVIDIA variant shape (nothing baked in, host driver only) but the actual runtime wiring differs — see the "NVIDIA gotcha" section in `README.md`. davincibox's README documents needing the NVIDIA Container Toolkit + CDI device injection (`--device nvidia.com/gpu=all`) rather than distrobox's simpler `--nvidia` flag, for reasons not fully explained in their docs (something about their toolbox target needing `NVIDIA_VISIBLE_DEVICES`/`NVIDIA_DRIVER_CAPABILITIES` env vars set at the container level, which `--nvidia`'s driver-file bind-mount alone doesn't set up). Not resolved here; flagged as an open question for whoever tests this first. - **App menu entry: hand-written `.desktop`, same approach as Houdini — reversed from an earlier version of this doc.** Originally adapted Resolve's own shipped `.desktop` (`/opt/resolve/share/DaVinciResolve.desktop`) via `sed`, matching davincibox's `add-davinci-launcher`. Abandoned after a real-hardware test (2026-09-09, see "App menu entry bug hunt" below) hit two real bugs in that approach in a row (a `distrobox-export` double-wrap, then a Resolve crash on the shipped file's `%u` field code) — at which point Simon called it: not worth carrying davincibox's `sed` adaptation and its edge cases just to reuse an icon reference, when a 10-line hand-written `.desktop` (Houdini's pattern) sidesteps all of it. The shipped `.desktop`'s only real advantage — the `MimeType=` line for file-manager "open project" association — isn't worth the fragility. - **Not ported from davincibox:** `switcheroo-control` multi-GPU handling (`list-gpus`/`switcherooctl launch` wrapping in `run-davinci`) and runtime GPU auto-detection in the launcher (`lshw`-based, only needed because davincibox ships one image covering all GPU vendors — this repo already splits that at build time via `amd`/`nvidia` targets). Both are real upstream features, deliberately left out here rather than overlooked — add them back from davincibox's `run-davinci` if dual-GPU laptop switching turns out to matter. ## Build test (2026-09-09) Ran on Docker Desktop (WSL2 backend, Windows host) with the real `DaVinci_Resolve_21.1_Linux.run` (free version) placed next to the Dockerfile — `docker build --target amd` then `--target nvidia`. No distrobox involved (Windows), so this only covers the image build itself, not an actual GUI launch. - **Dependency install (`base` stage, both variants): passed clean.** All 165 packages in `davinci-dependencies` resolved and installed on `fedora-toolbox:44` with 0 errors — confirms the list still matches current Fedora 44 repos. - **Silent install (`-i -a -y`, `QT_QPA_PLATFORM=minimal`, `SKIP_PACKAGE_CHECK=1`) actually works at Docker build time, no display attached.** Installer output ended with `DaVinci Resolve installed to /opt/resolve` / `Done`. Only benign warnings: missing `xdg-icon-resource`/ `xdg-mime`/`gtk-update-icon-cache` (desktop-integration helpers not installed, not needed at build time) and a udev reload failure (no real udev/sysfs in a build container — expected). No AppImage magic-byte issue hit with this particular `.run` (that workaround, from fat-tire/resolve, is still not ported — add it back if a future version's `.run` fails `--appimage-extract`). - **patchelf fix ran successfully.** `find /opt/resolve/bin -executable -type f -exec patchelf ...` reported `patchelf: not an ELF executable` for 3 files (non-ELF things under `bin/` with the executable bit set, e.g. shell scripts) — harmless, and doesn't fail the build because `find -exec cmd {} \;` (semicolon form) does not propagate `cmd`'s exit status to `find`'s own. Verified with `patchelf --print-needed` that `resolve` and its sibling binaries now carry all 4 `--add-needed` entries. - **`ldd /opt/resolve/bin/resolve` (the actual entry point): zero missing libraries.** This is the one that matters — confirms the dependency list is sufficient for the main binary as actually invoked. - **False alarm, worth remembering:** running `ldd` directly on individual `.so` files under `/opt/resolve/libs/*.so` in isolation shows dozens of "not found" (`libc++.so.1`, every bundled Qt5/OpenCV/Kaldi/OpenEXR lib, etc.). This is **not a real problem** — `resolve`'s own RPATH is `$ORIGIN/../libs/:$ORIGIN/../libs/Fusion:...` (old-style `DT_RPATH`, padded with a long run of `x` characters so Blackmagic's own installer can rewrite the real path post-install without changing the binary's size — same trick as the `RESOLVE_INSTALL_LOCATION` placeholder in the `.desktop` files). `DT_RPATH` on the main executable applies transitively to the whole process's library resolution, not just its own direct `NEEDED` entries — so at actual runtime, through `resolve`, all of these resolve fine. Testing a `.so` file in isolation loses that context and produces a misleading "missing" list. **Lesson for next time: to check for real missing libraries in an RPATH-based bundle like this one, `ldd` only the actual entry point binary, not the library files underneath it** — scanning every `.so` individually (the approach that worked for Houdini's Qt plugins, which don't use this RPATH trick) gives false positives here. - **The only genuinely missing library found:** `libcuda.so.1`, needed by `libDecoderCUDA.so` (CUDA RAW decode path) — expected and correct, that comes from the host NVIDIA driver, not from anything baked into the image. - **`nvidia` target builds instantly** (shares the cached `base` stage) and carries `NVIDIA_VISIBLE_DEVICES=all`/`NVIDIA_DRIVER_CAPABILITIES=all` as expected. Its actual GPU passthrough behavior (`--nvidia` vs CDI, see README) is still unverified — no NVIDIA hardware available to test from here either. - Not exercised at all: actual GUI launch, license-free playback, OpenCL compute on real AMD/Intel hardware, audio, the app-menu-entry steps, Studio+dongle path. All of that needs a real Linux host with distrobox. ## Real GUI launch test (2026-09-09) Simon ran `distrobox create` + `davinci-run` for real, `amd` target, on actual Linux/GPU hardware (AMD Radeon 680M integrated GPU, `rocm-opencl` + `intel-compute-runtime` both present as installed by the `amd` stage) — the one thing the Docker Desktop/WSL2 build test above explicitly could not cover. Result: **it works.** `davinci-run` launched `/opt/resolve/bin/resolve`, which went through the v21 "what's new" popup → Project Manager → Create New Project → a Transcode window, all mapped and focused normally under Hyprland (XWayland, confirmed via `hyprctl clients`). This is the first confirmation of the full stack (distrobox create → box → `davinci-run` → real GPU) actually working end to end, not just the image build. - **False alarm during my own debugging, worth recording so it isn't re-chased:** my first few attempts ran `davinci-run | head -N` to inspect output, which reproduced `Failed to create application support directories` every time (plus `log4cxx: No appender could be found`). This looked exactly like a real startup bug and cost real debugging time (strace, permission checks, `getpwuid`/nsswitch checks, `QT_PLUGIN_PATH` host-env-leak theory — all dead ends). The actual cause: `head -N` closes its end of the pipe after N lines, sending Resolve's process group a SIGPIPE that kills a startup helper before it finishes setting up its app-support directories. Piping through `tail` instead (which reads to EOF, no early close) or not piping at all made the error disappear immediately, every time. **Lesson: never pipe `davinci-run`'s stdout through something that can close its read end early (`head`, `sed -q`, etc.) when diagnosing a "failed to start" report — redirect to a file or use `tail` instead, or the pipe itself becomes the bug.** - Real host environment leaking into the box (Nix/home-manager: `QT_PLUGIN_PATH`, `PATH`, `XDG_DATA_DIRS` all full of `/nix/store/...` entries) turned out to be harmless for Resolve itself — it uses its own bundled Qt5 via `DT_RPATH` regardless of `QT_PLUGIN_PATH`. Not something `davinci-run` needs to guard against. - **No audio confirmed for real, as predicted in the README's "Known gotchas":** `ResolveDebug.txt` is full of `ALSA lib dlmisc.c:339: (snd_dlobj_cache_get0) [error.core] Cannot open shared library libasound_module_pcm_pipewire.so` — the host's PipeWire ALSA plugin is a Nix store path the box can't see. Not investigated further since the README already documents the fix (swap to `alsa-plugins-pulseaudio` inside the box); this just confirms the gotcha is real, not hypothetical. - Not covered by this test: actually opening/editing a real project, playback, OpenCL compute correctness, NVIDIA path (this host is AMD), Studio+dongle, app-menu-entry end-to-end. ## App menu entry bug hunt (2026-09-09) The README originally had Simon adapt Resolve's shipped `DaVinciResolve.desktop` via `sed` (davincibox's approach — see the "Why these choices" entry above, now reversed). Testing that end to end on real hardware surfaced two real bugs, back to back, before it was abandoned in favor of a hand-written `.desktop` (Houdini's pattern): 1. **`distrobox-export` double-wrap.** The README's `sed` prepended `Exec=distrobox enter -n davinci -- /usr/local/bin/davinci-run ` onto the shipped `Exec=` line *before* running `distrobox-export --app` — but `distrobox-export --app` *also* wraps whatever `Exec=` it's given with `distrobox-enter -n --` at export time. Result: the exported `.desktop` ran `distrobox-enter -n davinci -- distrobox enter -n davinci -- ...` — the outer enter (host, has podman) worked, but the *inner* `distrobox enter` then tried to run **inside the box itself**, where podman/distrobox aren't installed → "podman not installed" error. Fix at the time: drop the manual `distrobox enter -n davinci --` prefix from the `sed` and let `distrobox-export` do that wrapping alone. 2. **Resolve crashes on any stray argument.** Fixing bug 1 wasn't enough — the shipped `.desktop`'s `Exec=RESOLVE_INSTALL_LOCATION/bin/resolve %u` still carried a `%u` field code through to the final `Exec=`. Any launcher that doesn't strip unused field codes (confirmed on Simon's Hyprland/caelestia setup — not every launcher implements the freedesktop desktop-entry spec's field-code stripping) passes the literal string `"%u"` as an argument. Resolve does not ignore unknown arguments gracefully: it tries to load whatever it's given as a config file, fails, and hits `resolve: .../AppConfig.cpp:272: void AppConfig::LoadAllSiteInfo(): Assertion \`m_SiteEnabledIdx > 0' failed` (SIGABRT). An earlier, different bad argument (the literal path to Resolve's own binary, from a still-broken version of bug-1's fix) produced a *different* crash, SIGSEGV, confirmed via `coredumpctl` + installed `gdb` — same underlying lesson: Resolve is not defensive about its argv at all, so the `.desktop`'s `Exec=` must never pass it anything unexpected. - **Testing-methodology false alarm folded into this, same session:** the "Failed to create application support directories" error from the "Real GUI launch test" section above turned out to be a *third*, unrelated artifact (the `head`-pipe SIGPIPE issue) — three different causes produced superficially similar "Resolve won't start" symptoms in the same debugging session. Lesson for next time: don't assume a repeat of a previously-diagnosed failure mode; check the actual current error output (`coredumpctl list`, the log file, or in this case the process's own crash dump) before re-explaining an old theory. - **Decision:** rather than keep patching the shipped-`.desktop` adaptation for whatever the next edge case turns out to be, switched to a hand-written `.desktop` with no field codes at all (see README) — same pattern Houdini already uses, and it sidesteps this entire class of bug by construction. Confirmed working end to end after the switch: icon → `distrobox-export`'s wrapper → `davinci-run` → Project Manager window, no crash. ## What's still unverified after this build test | Area | Confidence | Note | |---|---|---| | Base image + dependency list | High | Build-confirmed against Fedora 44 repos, 2026-09-09. | | `-i -a -y` + `QT_QPA_PLATFORM=minimal` + `SKIP_PACKAGE_CHECK=1` at Docker build time, no display | High | Build-confirmed: installer completed and reported success. | | patchelf fix (paths, not sonames) | High | Build-confirmed: ran without failing the build, `--print-needed` shows the entries landed on `resolve` and siblings. | | Dependency sufficiency for the actual entry point (`ldd /opt/resolve/bin/resolve`) | High | Build-confirmed: zero missing libraries. | | AppImage magic-bytes workaround (fat-tire's Arch/Manjaro-specific issue) | Not ported | Not hit with the 21.1 `.run` tested here; add back from fat-tire/resolve's Dockerfile if a future version's `--appimage-extract` fails on it. | | `amd` variant (ROCm + Intel packages install) | High | Build-confirmed at Docker-build time, and now real-hardware-confirmed: `davinci-run` launched successfully on an AMD Radeon 680M host with `rocm-opencl`/`intel-compute-runtime` installed, 2026-09-09. OpenCL kernel *compute correctness* (actual color-science rendering) still not specifically checked — only that Resolve starts and its windows render normally. | | `nvidia` variant / `--nvidia` vs CDI | Low | Still a real open question — see "NVIDIA gotcha" in README. Image builds fine either way; only the *runtime* GPU passthrough method is unverified. The 2026-09-09 real-hardware test above was AMD, not NVIDIA. | | App menu entry steps | High | Real-hardware-confirmed 2026-09-09 after switching to a hand-written `.desktop` (Houdini's pattern) — see "App menu entry bug hunt" above. The originally-documented shipped-`.desktop`-adaptation approach is abandoned; do not resurrect it without re-reading that section. | | Actual GUI launch (`distrobox create` + `davinci-run`) | High | Real-hardware-confirmed 2026-09-09 on AMD/Hyprland: reached Project Manager → Create New Project → Transcode window, all mapped/focused normally. See "Real GUI launch test" above. | | Audio | Low (confirmed broken as predicted) | `ResolveDebug.txt` shows the exact `libasound_module_pcm_pipewire.so` failure the README's "Known gotchas" section already predicted. Fix documented there, not yet applied/tested. | | Rendering correctness / playback / licensing / project work | Untested | The 2026-09-09 test only confirmed the app starts and its dialogs render — no project was opened, no clip played back, no license flow exercised. | ## 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 `../houdini/CLAUDE.md` are for future-Claude context.