llama-server / Dockerfile
Ved Gupta
Apply minimal safe perf wins for free-tier HF Space
cc953bd
Raw
History Blame Contribute Delete
4.26 kB
# syntax=docker/dockerfile:1.7
# ---------- Stage 1: official llama.cpp server image ----------
# Use the upstream prebuilt server-only image (~71 MB) instead of compiling
# from source. This dropped Space build time from ~30 min (compiling all of
# mtmd/clip/vision-models on a 2-vCPU runner) to ~30 sec, and the upstream
# image is built/tested by the llama.cpp maintainers for every release.
# Pin to a tag (overridable) for reproducibility; defaults to :server.
ARG LLAMA_CPP_IMAGE=ghcr.io/ggml-org/llama.cpp:server
FROM ${LLAMA_CPP_IMAGE} AS llama
# ---------- Stage 2: download model ----------
FROM ubuntu:24.04 AS modelfetch
ARG MODEL_REPO=unsloth/gemma-4-E2B-it-GGUF
ARG MODEL_FILE=gemma-4-E2B-it-UD-Q4_K_XL.gguf
ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get update && apt-get install -y --no-install-recommends \
ca-certificates \
curl \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /model
# Download GGUF from Hugging Face. URL is constructed only from build-args
# (not from any user-controlled runtime input), so this does not violate
# the rule against using user input in network/file paths at runtime.
RUN set -eux; \
url="https://huggingface.co/${MODEL_REPO}/resolve/main/${MODEL_FILE}?download=true"; \
echo "Downloading ${url}"; \
curl -fL --retry 5 --retry-delay 5 --retry-all-errors \
-o "/model/${MODEL_FILE}" \
"${url}"; \
ls -lh "/model/${MODEL_FILE}"
# ---------- Stage 3: runtime ----------
# Must match the upstream :server image's glibc/libstdc++ ABI. Upstream is
# built on ubuntu:24.04 (glibc 2.39, libstdc++ from gcc-14); debian bookworm
# only has glibc 2.36 / older libstdc++ and the binary fails to load with
# "GLIBC_2.38 not found" / "GLIBCXX_3.4.32 not found".
FROM ubuntu:24.04 AS runtime
ENV DEBIAN_FRONTEND=noninteractive
# Slim runtime: only what llama-server actually links against (libgomp for
# OpenMP) plus curl for HEALTHCHECK and tini for PID 1. The upstream :server
# image is built without OpenBLAS, so no libopenblas0 needed; libcurl4 is
# pulled in because the upstream image has LLAMA_CURL=ON, but we never use
# the --hf-repo path at runtime — it's a small (~6 MB) cost we accept to
# avoid maintaining our own build.
RUN apt-get update && apt-get install -y --no-install-recommends \
ca-certificates \
curl \
libcurl4 \
libgomp1 \
tini \
&& apt-get autoremove -y \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/* /var/cache/apt/archives/*
ARG MODEL_FILE=gemma-4-E2B-it-UD-Q4_K_XL.gguf
# Hugging Face Spaces requires a non-root user with UID 1000 and a writable
# /home/user directory. ubuntu:24.04 ships with a default `ubuntu` user
# already at UID 1000, so we rename that user/group/home to `user` instead
# of creating a new one (which would fail with "UID 1000 is not unique").
RUN userdel -r ubuntu 2>/dev/null || true \
&& groupadd -g 1000 user \
&& useradd -m -u 1000 -g 1000 -s /bin/bash user
# Upstream :server image is built with GGML_BACKEND_DL=ON, meaning the CPU
# backend (libggml-cpu-*.so) is a runtime plugin that llama-server dlopens
# from its own directory. Keep the binary and ALL its *.so* siblings together
# in /app and add /app to the dynamic linker path so both PUBLIC libs and
# DL-plugin backends resolve correctly. Splitting them across /usr/local/bin
# vs /usr/local/lib breaks plugin discovery ("no backends are loaded").
COPY --from=llama /app/ /app/
RUN echo "/app" > /etc/ld.so.conf.d/llama.conf && ldconfig \
&& ln -sf /app/llama-server /usr/local/bin/llama-server
COPY --from=modelfetch /model/${MODEL_FILE} /models/${MODEL_FILE}
RUN chown -R user:user /models
COPY --chown=user:user entrypoint.sh /usr/local/bin/entrypoint.sh
RUN chmod +x /usr/local/bin/entrypoint.sh
USER user
WORKDIR /home/user
ENV MODEL_PATH=/models/${MODEL_FILE} \
PORT=7860 \
HOST=0.0.0.0 \
CTX_SIZE=4096 \
THREADS=2 \
THREADS_BATCH=4 \
PARALLEL=1 \
N_PREDICT=-1 \
OMP_WAIT_POLICY=active \
LLAMA_API_KEY=""
EXPOSE 7860
HEALTHCHECK --interval=30s --timeout=10s --start-period=120s --retries=3 \
CMD curl -fsS "http://127.0.0.1:${PORT}/health" || exit 1
ENTRYPOINT ["/usr/bin/tini", "--", "/usr/local/bin/entrypoint.sh"]