Reproducible Docker and Podman images for local LLM inference, development,
and serving on AMD Ryzen AI Max (Strix Halo).
Images are built directly from pinned Nix flake derivations for gfx1151 and
XDNA2.
Toolbx and Distrobox are not supported. The containers run as the dedicated
gufo user (UID/GID 1000:1000) and contain no sudo or PAM stack.
| Image | Reference | Purpose |
|---|---|---|
gufo-runtime |
ghcr.io/gufo-org/toolboxes/gufo-runtime:edge |
Gufo inference, serving, benchmarks, and diagnostics |
gufo-dev |
ghcr.io/gufo-org/toolboxes/gufo-dev:edge |
C++/HIP development, profiling, and kernel tuning |
Stable releases publish immutable X.Y.Z tags plus the floating X.Y and
latest aliases. The rolling edge channel follows the Gufo revision pinned
by the daily dependency update and also publishes an immutable
sha-<full-gufo-revision> tag. Rolling updates never advance latest.
The stable release workflow also calls Gufo's reusable Windows build for the
exact release tag. It attaches gufo-vX.Y.Z-windows-x86_64.zip and a SHA-256
checksum to the Gufo release.
The ZIP includes gufo.exe and its runtime DLLs; extract the entire archive
before running it. Use gufo.exe serve for the HTTP server.
The Windows caller requires Gufo's reusable workflow to be merged first.
Configure RELEASE_APP_CLIENT_ID (variable) and RELEASE_APP_PRIVATE_KEY
(secret) for this repository, using the release app installed on Gufo with
Contents write permission. The upload job mints its token after the build;
existing release assets are never overwritten. Use the manual stable release
workflow with a Gufo tag to recover a failed run.
The examples use rootless Podman with crun. --userns=keep-id:uid=1000,gid=1000
maps the invoking host user to the image's gufo account, so writable bind
mounts work without changing their ownership. --group-add keep-groups carries
the caller's supplementary GPU groups into the container.
For Docker, omit --userns=keep-id:uid=1000,gid=1000, replace
--group-add keep-groups with numeric --group-add options for the GIDs owning
/dev/kfd and the AMD render node, and ensure writable bind mounts permit UID
1000. See Docker for complete commands.
./refresh-toolboxes.sh all
# Or select the stable channel after a Gufo release is published.
./refresh-toolboxes.sh all latest
# Or pull one image directly.
podman pull ghcr.io/gufo-org/toolboxes/gufo-runtime:edgeThe container user needs the supplementary GIDs that own the host GPU device nodes. On a typical system:
podman run --rm -it \
--userns=keep-id:uid=1000,gid=1000 \
--device /dev/kfd \
--device /dev/dri \
--group-add keep-groups \
--ulimit memlock=-1 \
-v /path/to/models:/models:ro \
ghcr.io/gufo-org/toolboxes/gufo-runtime:edge \
gufo diagnoseReplace renderD128 if the AMD render node has another number. Run a model by
replacing the final command, for example:
gufo bench --model /models/model.gguf -p 512 -n 128Mount a checkout owned by UID 1000 and start the default shell:
podman run --rm -it \
--userns=keep-id:uid=1000,gid=1000 \
--device /dev/kfd \
--device /dev/dri \
--group-add keep-groups \
--ulimit memlock=-1 \
-v "$PWD:/workspace" \
-w /workspace \
ghcr.io/gufo-org/toolboxes/gufo-dev:edgeDocker has no keep-id user namespace and no keep-groups, so pass the
numeric GID that owns the GPU device nodes and make writable bind mounts
accessible to UID 1000:
gpu_gid=$(stat -c '%g' /dev/kfd)
docker run --rm -it \
--device /dev/kfd \
--device /dev/dri \
--group-add "$gpu_gid" \
--ulimit memlock=-1 \
-v /path/to/models:/models:ro \
ghcr.io/gufo-org/toolboxes/gufo-runtime:edge \
gufo diagnosediagnose should report gfx1151 and a loaded amdgpu driver. Run a prompt
or serve the OpenAI-compatible API with the same flags:
docker run --rm -it \
--device /dev/kfd --device /dev/dri --group-add "$gpu_gid" \
--ulimit memlock=-1 \
-v /path/to/models:/models:ro \
ghcr.io/gufo-org/toolboxes/gufo-runtime:edge \
gufo prompt --model /models/model.gguf --prompt "Hello" --max-tokens 128
docker run --rm -p 8080:8080 \
--device /dev/kfd --device /dev/dri --group-add "$gpu_gid" \
--ulimit memlock=-1 \
-v /path/to/models:/models:ro \
ghcr.io/gufo-org/toolboxes/gufo-runtime:edge \
gufo serve --model /models/model.gguf --host 0.0.0.0 --port 8080The served model name comes from the GGUF, not from the file path; read it
from /v1/models and send that exact id in chat requests.
Hugging Face cache directories store their GGUFs as symlinks into
blobs/, so mount the repository root and reference the model through
snapshots/<revision>/. Mounting a snapshot directory alone leaves the
symlinks dangling and the model fails to open:
docker run --rm \
--device /dev/kfd --device /dev/dri --group-add "$gpu_gid" \
--ulimit memlock=-1 \
-v /path/to/models--org--repo:/models:ro \
ghcr.io/gufo-org/toolboxes/gufo-runtime:edge \
gufo prompt --model /models/snapshots/<revision>/model.gguf --prompt "Hello"For the development image, mount the checkout and start the default shell.
hipcc and CMake projects using LANGUAGES HIP compile for gfx1151 without
extra flags:
docker run --rm -it \
--device /dev/kfd \
--device /dev/dri \
--group-add "$gpu_gid" \
--ulimit memlock=-1 \
-v "$PWD:/workspace" \
-w /workspace \
ghcr.io/gufo-org/toolboxes/gufo-dev:edgeBind mounts written by the container are owned by UID/GID 1000:1000 on the
host, because Docker maps the container user directly.
nix run .#stream-gufo-runtime | podman load
nix run .#stream-gufo-dev | podman load
# Docker loads the same streams.
nix run .#stream-gufo-runtime | docker load
nix build .#packages.x86_64-linux.gufo-runtime-imageMIT. See LICENSE.
After the Windows packaging fix is merged into Gufo main, an operator can
rebuild an older release without moving its source tag or republishing OCI images:
gh workflow run publish_release.yml --repo gufo-org/toolboxes --ref main \
-f tag=v0.24.0 -f windows-only=true -f replace-windows-assets=trueThis builds Gufo from the exact release tag using the current Windows packaging
workflow, verifies the ZIP checksum, and replaces both the Windows ZIP and its
.sha256 asset. The asset download URLs remain the same; their contents and
checksums change. Replacement is explicit and defaults to disabled. Ordinary
release dispatches still reject existing assets. No Linux images or image tags
are changed when windows-only is enabled.