Skip to content

Commit 7d51b92

Browse files
thadamskiclaude
andcommitted
polish: clean Dockerfile, add README and CLAUDE.md
- Dockerfile: remove ENV misuse, use ARG for build flags, separate cmake --build / --install, clean multi-stage layout - README.md: full background on RELR root cause, usage snippet, version update instructions, compatibility table - CLAUDE.md: critical invariants, consumer reference, testing notes Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent a7fbd28 commit 7d51b92

3 files changed

Lines changed: 171 additions & 30 deletions

File tree

CLAUDE.md

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# plex-vaapi-driver
2+
3+
Builds and publishes `ghcr.io/thadamski/plex-vaapi-driver` — a RELR-free Intel iHD VA-API driver for Plex Media Server.
4+
5+
## What this repo does
6+
7+
Plex ships musl < 1.2.3, which does not process RELR (`.relr.dyn`) sections. Alpine's modern toolchain emits RELR by default. This repo builds `intel-media-driver` and `intel-gmmlib` from source without RELR so Plex's musl can load them.
8+
9+
## Critical invariants
10+
11+
**Do not remove `-Wl,-z,nopackrelocs`** from `LDFLAGS` in the Dockerfile. That flag is the entire reason this image exists.
12+
13+
**Do not remove `-static-libstdc++ -static-libgcc`** from `LDFLAGS`. Alpine's `libstdc++.so` and `libgcc_s.so` also carry RELR — removing the static link reintroduces the crash through the C++ runtime dependencies.
14+
15+
## Updating driver versions
16+
17+
Change `ARG IHD_TAG` and `ARG GMM_TAG` at the top of `Dockerfile` and push to `main`. The workflow rebuilds automatically and pushes `:latest` plus a version-pinned tag.
18+
19+
Tag format:
20+
- `intel/media-driver``intel-media-X.Y.Z`
21+
- `intel/gmmlib``intel-gmmlib-X.Y.Z`
22+
23+
## Consumer
24+
25+
This image is consumed by the private repo `thadamski/homelab-fleet` in `apps/media/plex.yaml` as a Kubernetes init container.
26+
27+
## Testing a new build
28+
29+
After rolling the Plex pod with a new image version, confirm hardware transcoding is active by starting a transcode in Plex and checking that the Plex dashboard shows "hw" next to the stream, or inspect `/var/lib/plexmediaserver/Library/Application Support/Plex Media Server/Logs/` for VA-API initialisation messages.

Dockerfile

Lines changed: 45 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,45 +1,60 @@
11
ARG IHD_TAG=intel-media-26.1.6
22
ARG GMM_TAG=intel-gmmlib-22.10.0
33

4+
# ── Builder ───────────────────────────────────────────────────────────────────
45
FROM alpine:latest AS builder
56
ARG IHD_TAG
67
ARG GMM_TAG
78

8-
RUN apk add --no-cache alpine-sdk cmake libva-dev libdrm-dev libpciaccess-dev git patchelf
9+
RUN apk add --no-cache \
10+
alpine-sdk \
11+
cmake \
12+
git \
13+
libdrm-dev \
14+
libpciaccess-dev \
15+
libva-dev \
16+
patchelf
917

10-
# Build without RELR (packed relative relocations) and with static C++ runtime.
11-
# Plex's bundled musl is pre-1.2.3 and silently skips .relr.dyn sections, leaving
12-
# .init_array entries unrelocated → SIGSEGV at raw file offset 0x4310.
13-
ENV LF="-Wl,-z,nopackrelocs -static-libstdc++ -static-libgcc"
18+
# Plex ships musl < 1.2.3, which silently skips RELR (.relr.dyn) sections when
19+
# loading shared libraries. Unrelocated .init_array entries cause a segfault on
20+
# the first constructor call. Both flags are required on every target in this
21+
# build: -z,nopackrelocs suppresses RELR output; -static-libstdc++ and
22+
# -static-libgcc eliminate the C++ runtime .so dependencies, which also carry
23+
# RELR in current Alpine toolchain builds.
24+
ARG LDFLAGS="-Wl,-z,nopackrelocs -static-libstdc++ -static-libgcc"
1425

1526
RUN git clone --depth=1 --branch "${GMM_TAG}" \
16-
https://github.com/intel/gmmlib /tmp/gmmlib && \
17-
cmake -S /tmp/gmmlib -B /tmp/gmmlib/build \
18-
-DCMAKE_BUILD_TYPE=Release \
19-
-DCMAKE_INSTALL_PREFIX=/tmp/sdk \
20-
-DCMAKE_POLICY_VERSION_MINIMUM=3.5 \
21-
-DCMAKE_SHARED_LINKER_FLAGS="${LF}" \
22-
-DCMAKE_EXE_LINKER_FLAGS="${LF}" && \
23-
cmake --build /tmp/gmmlib/build --target install -- -j$(nproc)
27+
https://github.com/intel/gmmlib /src/gmmlib \
28+
&& cmake -S /src/gmmlib -B /build/gmmlib \
29+
-DCMAKE_BUILD_TYPE=Release \
30+
-DCMAKE_INSTALL_PREFIX=/sdk \
31+
-DCMAKE_POLICY_VERSION_MINIMUM=3.5 \
32+
"-DCMAKE_SHARED_LINKER_FLAGS=${LDFLAGS}" \
33+
"-DCMAKE_EXE_LINKER_FLAGS=${LDFLAGS}" \
34+
&& cmake --build /build/gmmlib -j$(nproc) \
35+
&& cmake --install /build/gmmlib
2436

2537
RUN git clone --depth=1 --branch "${IHD_TAG}" \
26-
https://github.com/intel/media-driver /tmp/ihd && \
27-
cmake -S /tmp/ihd -B /tmp/ihd/build \
28-
-DCMAKE_BUILD_TYPE=Release \
29-
-DCMAKE_PREFIX_PATH=/tmp/sdk \
30-
-DCMAKE_POLICY_VERSION_MINIMUM=3.5 \
31-
-DBUILD_TESTING=OFF \
32-
-DMEDIA_RUN_TEST_SUITE=OFF \
33-
-DCMAKE_SHARED_LINKER_FLAGS="${LF}" \
34-
-DCMAKE_EXE_LINKER_FLAGS="${LF}" && \
35-
cmake --build /tmp/ihd/build --target iHD_drv_video -- -j$(nproc)
38+
https://github.com/intel/media-driver /src/ihd \
39+
&& cmake -S /src/ihd -B /build/ihd \
40+
-DCMAKE_BUILD_TYPE=Release \
41+
-DCMAKE_INSTALL_PREFIX=/sdk \
42+
-DCMAKE_PREFIX_PATH=/sdk \
43+
-DCMAKE_POLICY_VERSION_MINIMUM=3.5 \
44+
-DBUILD_TESTING=OFF \
45+
-DMEDIA_RUN_TEST_SUITE=OFF \
46+
"-DCMAKE_SHARED_LINKER_FLAGS=${LDFLAGS}" \
47+
"-DCMAKE_EXE_LINKER_FLAGS=${LDFLAGS}" \
48+
&& cmake --build /build/ihd --target iHD_drv_video -j$(nproc)
3649

37-
RUN mkdir -p /opt/vaapi && \
38-
cp "$(find /tmp/ihd/build -name iHD_drv_video.so | head -1)" /opt/vaapi/ && \
39-
cp -P /tmp/sdk/lib/libigdgmm.so* /opt/vaapi/ && \
40-
patchelf --set-rpath /vaapi:/usr/lib/plexmediaserver/lib /opt/vaapi/iHD_drv_video.so && \
41-
patchelf --set-rpath /vaapi:/usr/lib/plexmediaserver/lib \
42-
/opt/vaapi/libigdgmm.so.12 2>/dev/null || true
50+
# Set RPATH so iHD resolves libva + libdrm from Plex's lib directory at
51+
# runtime, and libigdgmm from the /vaapi volume populated by this image.
52+
RUN install -d /out \
53+
&& cp "$(find /build/ihd -name iHD_drv_video.so -print -quit)" /out/ \
54+
&& cp -P /sdk/lib/libigdgmm.so* /out/ \
55+
&& patchelf --set-rpath '/vaapi:/usr/lib/plexmediaserver/lib' /out/iHD_drv_video.so \
56+
&& patchelf --set-rpath '/vaapi:/usr/lib/plexmediaserver/lib' /out/libigdgmm.so.12
4357

58+
# ── Runtime image ─────────────────────────────────────────────────────────────
4459
FROM alpine:latest
45-
COPY --from=builder /opt/vaapi/ /opt/vaapi/
60+
COPY --from=builder /out/ /opt/vaapi/

README.md

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
# plex-vaapi-driver
2+
3+
[![Build](https://github.com/thadamski/plex-vaapi-driver/actions/workflows/build.yml/badge.svg)](https://github.com/thadamski/plex-vaapi-driver/actions/workflows/build.yml)
4+
5+
Container image providing an Intel iHD VA-API driver compatible with Plex Media Server's bundled musl runtime on Intel Arc / Xe / i915 hardware.
6+
7+
```
8+
ghcr.io/thadamski/plex-vaapi-driver:latest
9+
```
10+
11+
---
12+
13+
## Background
14+
15+
Plex Media Server ships its own musl libc as the dynamic linker for its binaries. This musl build predates version 1.2.3 (released March 2022), which introduced support for [RELR](https://maskray.me/blog/2021-10-31-relative-relocations-and-relr) — a compact encoding for relative relocations (`SHT_RELR` / `.relr.dyn`) that modern toolchains now emit by default.
16+
17+
When Plex's musl loads a shared library that contains a `.relr.dyn` section, it silently skips those relocations. The affected entries in `.init_array` retain their raw link-time file offsets rather than resolved absolute addresses. The dynamic linker then calls that offset value as a function pointer, immediately faulting.
18+
19+
Alpine's pre-built `intel-media-driver` package — and every one of its transitive C++ dependencies — carries `.relr.dyn` sections. This image builds both `intel-media-driver` and `intel-gmmlib` from source with two required flags:
20+
21+
- **`-Wl,-z,nopackrelocs`** — instructs the linker to emit standard `R_X86_64_RELATIVE` entries instead of RELR
22+
- **`-static-libstdc++ -static-libgcc`** — statically links the C++ runtime, eliminating `libstdc++.so` and `libgcc_s.so` as runtime dependencies (both also carry RELR in current Alpine builds)
23+
24+
The resulting libraries are patched with an RPATH of `/vaapi:/usr/lib/plexmediaserver/lib` so that `iHD_drv_video.so` resolves `libigdgmm` from the shared volume and `libva` / `libdrm` from Plex's own lib directory — with no system libraries involved.
25+
26+
> **Note on `VADriverVTable`:** Plex's `libva.so.2` is often described as having a patched VA-API struct layout. Disassembly of both Plex's and Alpine's libva confirms their `VADriverVTable` and `VADriverContext` layouts are identical. The driver built here is compiled against Alpine's standard `libva-dev` headers and is fully compatible with Plex's runtime.
27+
28+
---
29+
30+
## Usage
31+
32+
Deploy as a Kubernetes init container. It copies the pre-built driver files into a shared `emptyDir` volume before the Plex container starts.
33+
34+
```yaml
35+
initContainers:
36+
- name: vaapi-driver
37+
image: ghcr.io/thadamski/plex-vaapi-driver:latest
38+
command: ["sh", "-c", "cp -aP /opt/vaapi/. /vaapi/"]
39+
volumeMounts:
40+
- name: vaapi-driver
41+
mountPath: /vaapi
42+
43+
containers:
44+
- name: plex
45+
env:
46+
- name: LIBVA_DRIVER_NAME
47+
value: iHD
48+
- name: LIBVA_DRIVERS_PATH
49+
value: /vaapi
50+
resources:
51+
limits:
52+
gpu.intel.com/i915: "1"
53+
volumeMounts:
54+
- name: vaapi-driver
55+
mountPath: /vaapi
56+
57+
volumes:
58+
- name: vaapi-driver
59+
emptyDir: {}
60+
```
61+
62+
The Intel GPU device plugin must be running on the node to expose the `gpu.intel.com/i915` resource. Pods accessing `/dev/dri` also require render group membership (typically GID `991`), set via `securityContext.supplementalGroups`.
63+
64+
---
65+
66+
## Updating versions
67+
68+
Driver versions are defined as `ARG` defaults at the top of the `Dockerfile`.
69+
70+
1. Find the latest releases: [intel/media-driver](https://github.com/intel/media-driver/releases) · [intel/gmmlib](https://github.com/intel/gmmlib/releases)
71+
2. Update `IHD_TAG` and `GMM_TAG` in `Dockerfile`
72+
3. Push to `main` — CI rebuilds and publishes both `:latest` and a version-pinned tag
73+
74+
---
75+
76+
## Building locally
77+
78+
```bash
79+
docker build -t plex-vaapi-driver .
80+
81+
# Override versions
82+
docker build \
83+
--build-arg IHD_TAG=intel-media-26.1.6 \
84+
--build-arg GMM_TAG=intel-gmmlib-22.10.0 \
85+
-t plex-vaapi-driver .
86+
```
87+
88+
---
89+
90+
## Compatibility
91+
92+
| Component | Version |
93+
|-----------|---------|
94+
| intel-media-driver | 26.1.6 |
95+
| intel-gmmlib | 22.10.0 |
96+
| Target GPU | Intel Arc / Xe2 (Arrow Lake) — compatible with Broadwell and newer |
97+
| Tested with | Plex Media Server 1.43.x, `linuxserver/plex:latest` |

0 commit comments

Comments
 (0)