Skip to content

Commit d2e58c1

Browse files
authored
Merge pull request #355 from Luce-Org/feat/docker-rocm
feat(docker): ROCm/HIP image variant (:rocm)
2 parents 50107ad + 8b2208e commit d2e58c1

15 files changed

Lines changed: 341 additions & 54 deletions

.github/workflows/docker.yml

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,19 +12,38 @@ on:
1212
# event=tag` + `type=semver` rules below.
1313
release:
1414
types: [published]
15-
# Build + push the rolling `:cuda12` tag on every main merge so the public
16-
# image tracks main. The metadata-action `enable=` rule below gates the
17-
# moving tag on `github.ref == refs/heads/main`, and the build step's
18-
# `push:` condition includes push events on main.
15+
# Build + push the rolling `:cuda12` / `:rocm` tags when an image-affecting
16+
# file changes on main, so the public images track main without a ~2h
17+
# rebuild on every unrelated commit (docs, harness, server tweaks that
18+
# don't reach the image). Same paths as the PR guard below. The
19+
# metadata-action `enable=` rule gates the moving tag on
20+
# `github.ref == refs/heads/main`; the build step's `push:` condition
21+
# includes push events on main.
1922
push:
2023
branches: [main]
24+
paths:
25+
- Dockerfile
26+
- Dockerfile.rocm
27+
- docker-bake.hcl
28+
- .dockerignore
29+
- .github/workflows/docker.yml
30+
- server/CMakeLists.txt
31+
- server/src/**
32+
- server/test/**
33+
- server/include/**
34+
- server/scripts/**
35+
- server/deps/**
36+
- server/pyproject.toml
37+
- pyproject.toml
38+
- uv.lock
2139
# Build-only CI guard on PRs that touch the docker surface. We never push
2240
# from a PR — even if we wanted to, GITHUB_TOKEN on PRs from forks lacks
2341
# `packages:write`. The point is to catch Dockerfile / bake-file / arch-
2442
# list regressions before they land on main.
2543
pull_request:
2644
paths:
2745
- Dockerfile
46+
- Dockerfile.rocm
2847
- docker-bake.hcl
2948
- .dockerignore
3049
- .github/workflows/docker.yml
@@ -76,7 +95,7 @@ jobs:
7695
strategy:
7796
fail-fast: false
7897
matrix:
79-
variant: [cuda12]
98+
variant: [cuda12, rocm]
8099
steps:
81100
- name: Free runner disk space
82101
# The default ubuntu-latest image keeps ~25 GB of preinstalled

Dockerfile

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -148,15 +148,15 @@ ARG BUILD_TIME=""
148148
RUN apt-get update && apt-get install -y --no-install-recommends \
149149
ca-certificates \
150150
curl \
151-
docker.io \
152151
libgomp1 \
153152
pciutils \
154153
&& rm -rf /var/lib/apt/lists/*
155154

156155
# uv manages Python 3.12 (required by the workspace) and resolves the
157156
# lucebox-dflash + pflash members declared in pyproject.toml.
158-
RUN curl -LsSf https://astral.sh/uv/install.sh \
159-
| env UV_INSTALL_DIR=/usr/local/bin UV_NO_MODIFY_PATH=1 INSTALLER_NO_MODIFY_PATH=1 sh
157+
# uv (pinned) copied from the official image rather than `curl | sh`, so the
158+
# version is fixed and no remote installer script runs at build time.
159+
COPY --from=ghcr.io/astral-sh/uv:0.11.2 /uv /uvx /usr/local/bin/
160160

161161
# Install Python to a world-readable location, not /root/.local/share/uv/
162162
# (the default). The container runs as the host UID for bind-mount sanity

Dockerfile.rocm

Lines changed: 215 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,215 @@
1+
# syntax=docker/dockerfile:1.7
2+
3+
# ─── Stage 1: builder ───────────────────────────────────────────────────────
4+
# ROCm/HIP sibling of the CUDA Dockerfile. ROCM_VERSION / UBUNTU_VERSION /
5+
# DFLASH_HIP_ARCHES are build args so the same Dockerfile can be repinned.
6+
# • lucebox-hub:rocm — ROCm 6.4.x, gfx1151 (+ optional fat gfx list)
7+
# See docker-bake.hcl for the canonical invocation.
8+
#
9+
# NOTE: gfx1151 (Strix Halo / Ryzen AI MAX) needs ROCm >= 6.4.1. The default
10+
# stays on 6.4.1 because the 7.2.x stack has shown intermittent problems on
11+
# Strix Halo. The flip side: against a ROCm 7.x HOST driver the 6.4.x
12+
# userspace can segfault at model load (seen on gfx1151 + host ROCm 7.2.2:
13+
# SIGSEGV in backend creation, bogus 1.28 TB VRAM report) — in that case
14+
# rebuild with ROCM_VERSION=7.2.2 to match the host. Rule of thumb: keep the
15+
# base's major version aligned with the host driver.
16+
ARG ROCM_VERSION=6.4.1
17+
ARG UBUNTU_VERSION=22.04
18+
FROM rocm/dev-ubuntu-${UBUNTU_VERSION}:${ROCM_VERSION} AS builder
19+
20+
ARG DEBIAN_FRONTEND=noninteractive
21+
22+
# Fat-binary HIP arch list, semicolon-separated. Each gfx target adds build
23+
# time + kernel code the same way CUDA arches do, so narrow this to your host
24+
# for fast local builds: DFLASH_HIP_ARCHES=gfx1151 docker buildx bake rocm-local
25+
# dflash-supported gfx targets (all have the sm_75+ equivalent features dflash
26+
# assumes; pre-RDNA/CDNA1 parts are excluded):
27+
# gfx1151 Strix Halo / Ryzen AI MAX+ 395 (the lucebox appliance iGPU)
28+
# gfx1100 RDNA3 RX 7900 XTX / W7900
29+
# gfx1200 RDNA4 RX 9070
30+
# gfx942 CDNA3 MI300X / MI300A
31+
# gfx90a CDNA2 MI200 / MI250
32+
ARG DFLASH_HIP_ARCHES="gfx1151"
33+
34+
# ROCm toolchain on PATH (hipcc + amdclang). The rocm/dev-ubuntu base installs
35+
# ROCm at /opt/rocm; ROCM_PATH lets server/CMakeLists.txt resolve the HIP rpath
36+
# and rocwmma header roots (server/CMakeLists.txt:33-41).
37+
ENV ROCM_PATH=/opt/rocm
38+
ENV PATH=/opt/rocm/bin:/opt/rocm/lib/llvm/bin:${PATH}
39+
40+
# Unlike the CUDA image there is NO driver-stub symlink step: the ROCm base
41+
# ships the real HIP runtime libs (libamdhip64.so etc.) and the host kernel
42+
# driver (/dev/kfd, /dev/dri) is wired in at run time via --device.
43+
# hipblas/rocblas: ggml's HIP backend hard-requires them
44+
# (deps/llama.cpp/ggml/src/ggml-hip/CMakeLists.txt does find_package(hipblas)
45+
# for its BLAS matmul path). The rocm/dev-ubuntu base ships the HIP toolchain
46+
# but NOT the math libs, so they are installed explicitly from the ROCm apt
47+
# repo the base image already configures.
48+
RUN apt-get update && apt-get install -y --no-install-recommends \
49+
build-essential \
50+
ca-certificates \
51+
cmake \
52+
curl \
53+
git \
54+
git-lfs \
55+
hipblas-dev \
56+
libcurl4-openssl-dev \
57+
ninja-build \
58+
pkg-config \
59+
python3 \
60+
rocblas-dev \
61+
&& rm -rf /var/lib/apt/lists/*
62+
63+
WORKDIR /src
64+
65+
# COPY ordering mirrors the CUDA Dockerfile: C++ build inputs first so the
66+
# (slow) HIP build layer stays cached across Python-only edits.
67+
COPY server/CMakeLists.txt /src/server/CMakeLists.txt
68+
COPY server/include /src/server/include
69+
COPY server/src /src/server/src
70+
COPY server/test /src/server/test
71+
COPY server/hip_compat /src/server/hip_compat
72+
COPY server/deps /src/server/deps
73+
# status.html: dflash_server's POST_BUILD copies server/share/status.html into
74+
# build/share/. Without this COPY the build links then dies on the missing file.
75+
COPY server/share /src/server/share
76+
77+
# Submodules must be populated on the host before `docker build` (.git/ is
78+
# .dockerignore'd so they cannot be fetched inside the image).
79+
RUN test -f /src/server/deps/llama.cpp/ggml/CMakeLists.txt \
80+
|| (echo "ERROR: server/deps/llama.cpp submodule not initialised. Run on host:" >&2 \
81+
&& echo " git submodule update --init --recursive" >&2 \
82+
&& exit 1)
83+
84+
# Configure + build for HIP. DFLASH27B_GPU_BACKEND=hip selects the ggml-hip
85+
# backend (server/CMakeLists.txt:70-78); DFLASH27B_HIP_ARCHITECTURES pins the
86+
# gfx list. Block-Sparse-Attention is a CUDA-only kernel set, so BSA is OFF for
87+
# HIP; FA_ALL_QUANTS OFF keeps the fattn build tractable (matches the lucebox
88+
# native HIP build). CMAKE_BUILD_WITH_INSTALL_RPATH bakes the $ORIGIN-relative
89+
# rpath (incl. ggml-hip + ROCm lib) so the binaries find their .so files after
90+
# the COPY into the runtime stage.
91+
RUN cmake -S /src/server -B /src/server/build \
92+
-G Ninja \
93+
-DCMAKE_BUILD_TYPE=Release \
94+
-DCMAKE_BUILD_WITH_INSTALL_RPATH=ON \
95+
-DDFLASH27B_GPU_BACKEND=hip \
96+
-DDFLASH27B_HIP_ARCHITECTURES="${DFLASH_HIP_ARCHES}" \
97+
-DDFLASH27B_FA_ALL_QUANTS=OFF \
98+
-DDFLASH27B_ENABLE_BSA=OFF \
99+
&& cmake --build /src/server/build --target test_dflash dflash_server test_server_unit --parallel
100+
101+
# Prune the build tree to only what the runtime stage needs: the native server,
102+
# test_dflash, test_server_unit, and the ggml shared libs their embedded rpath
103+
# ($ORIGIN/deps/...) looks up.
104+
RUN cd /src/server/build \
105+
&& find . -mindepth 1 -maxdepth 1 \
106+
! -name test_dflash ! -name dflash_server ! -name test_server_unit ! -name deps -exec rm -rf {} + \
107+
&& find deps -mindepth 1 -type f ! -name 'lib*.so*' -delete \
108+
&& find deps -depth -type d -empty -delete
109+
110+
# Python sources, workspace manifests, lockfile, READMEs (same as CUDA stage).
111+
COPY pyproject.toml uv.lock README.md /src/
112+
COPY server/pyproject.toml server/README.md /src/server/
113+
COPY server/scripts /src/server/scripts
114+
COPY optimizations/pflash /src/optimizations/pflash
115+
COPY optimizations/megakernel /src/optimizations/megakernel
116+
117+
# ─── Stage 2: runtime ───────────────────────────────────────────────────────
118+
# Runtime reuses the ROCm base so the HIP runtime libs (libamdhip64,
119+
# libhsa-runtime64, librocm-core, …) the binaries link against are present and
120+
# version-matched to the builder. This makes a larger image than a minimal
121+
# runtime would; slimming to `ubuntu:${UBUNTU_VERSION}` + the AMD repo's
122+
# `rocm-language-runtime` meta-package is a follow-up optimisation (the build
123+
# stage is unaffected, so it can be done independently).
124+
FROM rocm/dev-ubuntu-${UBUNTU_VERSION}:${ROCM_VERSION} AS runtime
125+
126+
ARG DEBIAN_FRONTEND=noninteractive
127+
128+
# Image identity baked in for /props.build (git_sha / image_tag / build_time),
129+
# wired from docker-bake.hcl. Empty in non-bake builds → JSON null at /props.
130+
ARG GIT_SHA=""
131+
ARG IMAGE_TAG=""
132+
ARG BUILD_TIME=""
133+
134+
ENV ROCM_PATH=/opt/rocm
135+
ENV PATH=/opt/rocm/bin:${PATH}
136+
137+
# hipblas/rocblas runtime libs: the dflash binaries link ggml-hip against
138+
# them (see builder stage). Their .so files must be present at runtime for
139+
# the server to load.
140+
RUN apt-get update && apt-get install -y --no-install-recommends \
141+
ca-certificates \
142+
curl \
143+
hipblas \
144+
libgomp1 \
145+
pciutils \
146+
rocblas \
147+
&& rm -rf /var/lib/apt/lists/*
148+
149+
# uv (pinned) manages Python 3.12 + resolves the lucebox-dflash + pflash
150+
# workspace. Copied from the official image rather than `curl | sh`, so the
151+
# version is fixed and no remote installer script runs at build time.
152+
COPY --from=ghcr.io/astral-sh/uv:0.11.2 /uv /uvx /usr/local/bin/
153+
154+
ENV UV_PYTHON_INSTALL_DIR=/opt/uv/python \
155+
UV_TOOL_DIR=/opt/uv/tools
156+
157+
WORKDIR /opt/lucebox-hub
158+
159+
COPY --from=builder /src/pyproject.toml /src/uv.lock /src/README.md /opt/lucebox-hub/
160+
COPY --from=builder /src/optimizations/pflash /opt/lucebox-hub/optimizations/pflash
161+
COPY --from=builder /src/optimizations/megakernel/pyproject.toml \
162+
/src/optimizations/megakernel/README.md \
163+
/opt/lucebox-hub/optimizations/megakernel/
164+
165+
COPY --from=builder /src/server/scripts /opt/lucebox-hub/server/scripts
166+
COPY --from=builder /src/server/pyproject.toml /src/server/README.md \
167+
/opt/lucebox-hub/server/
168+
COPY --from=builder /src/server/build /opt/lucebox-hub/server/build
169+
170+
COPY share/model_cards /opt/lucebox-hub/share/model_cards
171+
RUN mkdir -p /opt/lucebox-hub/server/share \
172+
&& ln -s /opt/lucebox-hub/share/model_cards \
173+
/opt/lucebox-hub/server/share/model_cards
174+
175+
RUN test -x /opt/lucebox-hub/server/build/test_dflash \
176+
&& test -x /opt/lucebox-hub/server/build/dflash_server \
177+
&& test -x /opt/lucebox-hub/server/build/test_server_unit \
178+
&& test -f /opt/lucebox-hub/server/share/model_cards/qwen3.6-27b.json \
179+
&& chmod +x /opt/lucebox-hub/server/scripts/entrypoint.sh
180+
181+
RUN printf '%s\n%s\n%s\n' "$GIT_SHA" "$IMAGE_TAG" "$BUILD_TIME" \
182+
> /opt/lucebox-hub/IMAGE_INFO
183+
184+
# Register the ggml lib dirs with ld.so. HIP names its backend lib dir
185+
# `ggml-hip` (server/CMakeLists.txt:74), where the CUDA image uses `ggml-cuda`.
186+
RUN printf '%s\n%s\n' \
187+
/opt/lucebox-hub/server/build/deps/llama.cpp/ggml/src \
188+
/opt/lucebox-hub/server/build/deps/llama.cpp/ggml/src/ggml-hip \
189+
> /etc/ld.so.conf.d/lucebox-ggml.conf \
190+
&& ldconfig
191+
192+
ENV UV_LINK_MODE=hardlink \
193+
UV_NO_CACHE=1
194+
RUN uv sync --no-dev --frozen --no-editable 2>/dev/null \
195+
|| uv sync --no-dev --frozen --no-editable
196+
197+
RUN chmod -R a+rX /opt/lucebox-hub/.venv /opt/lucebox-hub /opt/uv
198+
199+
# Models live in server/models/ — bind-mount or volume them in.
200+
# ROCm run example (note: AMD uses --device, not --gpus):
201+
# docker run --rm --device /dev/kfd --device /dev/dri \
202+
# --group-add video --group-add render \
203+
# --security-opt seccomp=unconfined -p 8080:8080 \
204+
# -v "$PWD/server/models:/opt/lucebox-hub/server/models" \
205+
# ghcr.io/luce-org/lucebox-hub:rocm
206+
VOLUME ["/opt/lucebox-hub/server/models"]
207+
208+
ENV DFLASH_HOST=0.0.0.0 \
209+
DFLASH_PORT=8080 \
210+
DFLASH_BIN=/opt/lucebox-hub/server/build/test_dflash \
211+
DFLASH_SERVER_BIN=/opt/lucebox-hub/server/build/dflash_server
212+
213+
EXPOSE 8080
214+
215+
ENTRYPOINT ["/opt/lucebox-hub/server/scripts/entrypoint.sh"]

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ build: ## Build lucebox-hub:cuda12 locally via docker buildx bake.
5151

5252
.PHONY: serve
5353
serve: ## Run the local image, foreground. Models bind-mounted from $(MODELS_DIR).
54-
docker run --rm --gpus all -p 8080:8080 \
54+
docker run --rm --gpus all -p 8000:8080 \
5555
-v $(MODELS_DIR):/opt/lucebox-hub/server/models:ro \
5656
--name lucebox-gemma \
5757
$(IMAGE) serve

README.md

Lines changed: 32 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -157,28 +157,51 @@ Prebuilt images on GHCR track `main`. No CUDA toolkit or build needed. Pull the
157157

158158
<table>
159159
<tr>
160-
<td width="50%" valign="middle">
160+
<td width="38%" valign="middle">
161161

162-
```bash
163-
# NVIDIA (CUDA 12+)
164-
docker run --rm --gpus all -p 8000:8080 \
165-
-v "$PWD/server/models:/opt/lucebox-hub/server/models" \
166-
ghcr.io/luce-org/lucebox-hub:cuda12
167-
```
162+
| GPU | Image tag |
163+
|-----|-----------|
164+
| NVIDIA (CUDA 12+) | `:cuda12` |
165+
| AMD (ROCm 6+) | `:rocm` |
168166

169167
Drop a GGUF model target into `server/models/` first, then
170168
`:8000/v1/chat/completions`. Full tutorial in the
171169
[Docker blog](https://lucebox.com/blog/docker).
172170

173171
</td>
174-
<td width="50%" valign="middle">
172+
<td width="62%" valign="middle">
175173

176-
<a href="https://lucebox.com/blog/docker"><img src="assets/docker.png" alt="Lucebox prebuilt Docker images" width="100%" /></a>
174+
<a href="https://lucebox.com/blog/docker"><img src="assets/docker.png" alt="Lucebox prebuilt Docker images for NVIDIA and AMD" width="100%" /></a>
177175

178176
</td>
179177
</tr>
180178
</table>
181179

180+
**Install and run:**
181+
182+
```bash
183+
# 1. Pull the image for your GPU
184+
docker pull ghcr.io/luce-org/lucebox-hub:cuda12 # NVIDIA
185+
docker pull ghcr.io/luce-org/lucebox-hub:rocm # AMD
186+
187+
# 2. Download a target model into server/models/
188+
hf download unsloth/Qwen3.6-27B-GGUF Qwen3.6-27B-Q4_K_M.gguf \
189+
--local-dir server/models/
190+
191+
# 3a. NVIDIA (CUDA 12+)
192+
docker run --rm --gpus all -p 8000:8080 \
193+
-v "$PWD/server/models:/opt/lucebox-hub/server/models" \
194+
ghcr.io/luce-org/lucebox-hub:cuda12
195+
196+
# 3b. AMD (ROCm 6+, Strix Halo / RX 7900)
197+
docker run --rm --device /dev/kfd --device /dev/dri \
198+
--group-add video --group-add render --security-opt seccomp=unconfined \
199+
-p 8000:8080 -v "$PWD/server/models:/opt/lucebox-hub/server/models" \
200+
ghcr.io/luce-org/lucebox-hub:rocm
201+
```
202+
203+
Then hit `:8000/v1/chat/completions` (OpenAI-compatible).
204+
182205
## Run the Server
183206

184207
Default: Qwen 3.6-27B Q4_K_M target + Lucebox Q4_K_M DFlash drafter on RTX 3090. DDTree budget=22, TQ3_0 KV cache, sliding FA window 2048. OpenAI-compatible HTTP on `:8000`.

assets/docker.png

241 KB
Loading

0 commit comments

Comments
 (0)