mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-02 02:11:58 +00:00
* feat(ui): adopt the new LiteLLM logo and monogram Swap the bundled admin UI logos for the new brand assets: the primary logo in blue for light mode and white for dark mode, and the monogram for the collapsed sidebar, favicons, and the built-in guardrail cards. /get_image gains a variant=monogram query parameter so the collapsed sidebar can request the monogram while admin-configured UI_LOGO_PATH / UI_LOGO_PATH_DARK logos still take precedence. The bundled light logo moves from JPEG to a transparent PNG. * test(ui): query collapsed sidebar logos by role to stay within the lint budget * fix: point remaining logo consumers at the new bundled assets The Rust gateway UI served /get_image from the removed litellm_logo.jpg, the non-root get_image tests pinned logo.jpg, and a cookbook script read litellm/proxy/logo.jpg. Point them at the monogram and logo.png. * fix(mcp): serve the BYOK OAuth page logo from /get_image The page pointed at /ui/assets/logos/litellm_logo.jpg, which the rebrand removes from the dashboard sources, so the next UI build would drop it. /get_image?variant=monogram is always served by the proxy and follows any admin-configured logo. * fix(ui): invert the LiteLLM monogram on dark guardrail cards The blue monogram has a transparent train cut-out, so on a dark card it read as a muddy blue block. Inverting it yields the brand's white mark, which the logo guidelines prescribe for dark backgrounds. * fix(gateway-ui): serve theme and variant aware logos from the dashboard export The Rust gateway served one monogram for every /get_image request, and the committed export lacked it, so /get_image returned 404 until the next UI release build. Pick the full or monogram logo in light or dark from the query, ship those assets in the dashboard's public dir and the committed export, and drop two comments that restated asserted paths.
217 lines
9.5 KiB
Text
217 lines
9.5 KiB
Text
# syntax=docker/dockerfile:1.7
|
|
|
|
# Base images
|
|
ARG LITELLM_BUILD_IMAGE=cgr.dev/chainguard/wolfi-base@sha256:1d95114038f76513a9ace6fca107d5582b08c65981f81f61cb56bf7fd2ef216d
|
|
ARG LITELLM_RUNTIME_IMAGE=cgr.dev/chainguard/wolfi-base@sha256:1d95114038f76513a9ace6fca107d5582b08c65981f81f61cb56bf7fd2ef216d
|
|
ARG PROXY_EXTRAS_SOURCE=published
|
|
ARG UV_IMAGE=ghcr.io/astral-sh/uv:0.11.7@sha256:240fb85ab0f263ef12f492d8476aa3a2e4e1e333f7d67fbdd923d00a506a516a
|
|
# Pinned by digest like the other base images; bump explicitly on Node upgrades.
|
|
ARG UI_BUILD_IMAGE=node:24.19-alpine3.24@sha256:d32cdf619f63fe0471182d08996dd516c6275bb5fd31ae06e55a570bd9e1ad43
|
|
# Checksum from https://www.pgbouncer.org/downloads/ (the Wolfi repo only carries 1.24.x)
|
|
ARG PGBOUNCER_VERSION=1.25.2
|
|
ARG PGBOUNCER_SHA256=924ad35113fd0a71c8e2dbe85b5d03445532e2b7b37a9f8a48983beea238b332
|
|
|
|
FROM $UV_IMAGE AS uvbin
|
|
|
|
FROM $LITELLM_BUILD_IMAGE AS pgbouncer-builder
|
|
ARG PGBOUNCER_VERSION
|
|
ARG PGBOUNCER_SHA256
|
|
USER root
|
|
RUN apk add --no-cache build-base pkgconf libevent-dev openssl-dev curl
|
|
WORKDIR /build
|
|
RUN curl -fsSL -o pgbouncer.tar.gz "https://www.pgbouncer.org/downloads/files/${PGBOUNCER_VERSION}/pgbouncer-${PGBOUNCER_VERSION}.tar.gz" && \
|
|
echo "${PGBOUNCER_SHA256} pgbouncer.tar.gz" | sha256sum -c - && \
|
|
tar xzf pgbouncer.tar.gz --strip-components=1 && \
|
|
./configure --prefix=/usr/local --with-openssl=/usr && \
|
|
make -j"$(nproc)" pgbouncer && \
|
|
install -m 0755 pgbouncer /usr/local/bin/pgbouncer
|
|
|
|
# Admin UI builder. Pinned to the build platform so the architecture-independent
|
|
# Next.js static export compiles once natively even in a multi-arch build,
|
|
# instead of once per target arch under QEMU.
|
|
FROM --platform=$BUILDPLATFORM $UI_BUILD_IMAGE AS ui-builder
|
|
|
|
ENV NEXT_TELEMETRY_DISABLED=1 \
|
|
npm_config_fund=false \
|
|
npm_config_audit=false
|
|
|
|
WORKDIR /ui
|
|
|
|
COPY ui/litellm-dashboard/package.json ui/litellm-dashboard/package-lock.json ./
|
|
RUN --mount=type=cache,target=/root/.npm npm ci --prefer-offline
|
|
|
|
COPY ui/litellm-dashboard/ ./
|
|
RUN npm run build
|
|
|
|
FROM $LITELLM_BUILD_IMAGE AS builder
|
|
ARG PROXY_EXTRAS_SOURCE
|
|
WORKDIR /app
|
|
USER root
|
|
|
|
COPY --from=uvbin /uv /usr/local/bin/uv
|
|
COPY --from=uvbin /uvx /usr/local/bin/uvx
|
|
|
|
RUN for i in 1 2 3; do \
|
|
apk add --no-cache \
|
|
python-3.13 \
|
|
python-3.13-dev \
|
|
gcc \
|
|
rust \
|
|
bash \
|
|
coreutils \
|
|
curl \
|
|
openssl \
|
|
libsndfile \
|
|
nodejs \
|
|
npm && break || sleep 5; \
|
|
done
|
|
|
|
ENV UV_PROJECT_ENVIRONMENT=/app/.venv \
|
|
UV_LINK_MODE=copy \
|
|
UV_PYTHON_DOWNLOADS=0 \
|
|
PATH="/app/.venv/bin:${PATH}" \
|
|
LITELLM_NON_ROOT=true \
|
|
XDG_CACHE_HOME=/app/.cache
|
|
|
|
# Copy dependency metadata first for layer caching
|
|
COPY pyproject.toml uv.lock ./
|
|
COPY enterprise/pyproject.toml enterprise/
|
|
COPY litellm-proxy-extras/pyproject.toml litellm-proxy-extras/
|
|
|
|
# Install third-party dependencies (cached unless pyproject.toml/uv.lock change)
|
|
RUN --mount=type=cache,target=/app/.cache/uv,id=litellm-uv-cache \
|
|
uv sync --frozen --no-install-project --no-install-workspace --no-default-groups --no-editable \
|
|
--extra proxy \
|
|
--extra proxy-runtime \
|
|
--extra extra_proxy \
|
|
--extra semantic-router \
|
|
--extra saml \
|
|
--extra bedrock-realtime \
|
|
--python python3.13
|
|
|
|
# Copy full source tree
|
|
COPY . .
|
|
|
|
# Replace the committed UI bundle with the one built from this exact source.
|
|
# Clearing first drops the committed bundle's content-hashed chunks that COPY
|
|
# would otherwise leave behind alongside the fresh ones.
|
|
RUN rm -rf litellm/proxy/_experimental/out
|
|
COPY --from=ui-builder /ui/out/. litellm/proxy/_experimental/out/
|
|
|
|
# Set non-root flag for build time consistency
|
|
ENV LITELLM_NON_ROOT=true
|
|
|
|
RUN mkdir -p /var/lib/litellm/ui /var/lib/litellm/assets && \
|
|
cp -r /app/litellm/proxy/_experimental/out/. /var/lib/litellm/ui/ && \
|
|
cp /app/litellm/proxy/logo.png /var/lib/litellm/assets/logo.png && \
|
|
touch /var/lib/litellm/ui/.litellm_ui_ready
|
|
|
|
RUN --mount=type=cache,target=/app/.cache/uv,id=litellm-uv-cache \
|
|
if [ "$PROXY_EXTRAS_SOURCE" = "published" ]; then \
|
|
uv sync --frozen --no-default-groups --no-editable \
|
|
--extra proxy \
|
|
--extra proxy-runtime \
|
|
--extra extra_proxy \
|
|
--extra semantic-router \
|
|
--extra saml \
|
|
--extra bedrock-realtime \
|
|
--python python3.13 \
|
|
--no-sources-package litellm-proxy-extras; \
|
|
else \
|
|
uv sync --frozen --no-default-groups --no-editable \
|
|
--extra proxy \
|
|
--extra proxy-runtime \
|
|
--extra extra_proxy \
|
|
--extra semantic-router \
|
|
--extra saml \
|
|
--extra bedrock-realtime \
|
|
--python python3.13; \
|
|
fi
|
|
|
|
RUN HOME=/opt/prisma XDG_CACHE_HOME=/opt/prisma/.cache PRISMA_BINARY_CACHE_DIR=/opt/prisma/binaries \
|
|
npm_config_cache=/root/.npm \
|
|
prisma generate --schema=./schema.prisma
|
|
|
|
RUN sed -i 's/\r$//' docker/entrypoint.sh && chmod +x docker/entrypoint.sh && \
|
|
sed -i 's/\r$//' docker/prod_entrypoint.sh && chmod +x docker/prod_entrypoint.sh
|
|
|
|
FROM $LITELLM_RUNTIME_IMAGE AS runtime
|
|
ARG PROXY_EXTRAS_SOURCE
|
|
WORKDIR /app
|
|
USER root
|
|
|
|
RUN for i in 1 2 3; do \
|
|
apk upgrade --no-cache && break || sleep 5; \
|
|
done && \
|
|
for i in 1 2 3; do \
|
|
apk add --no-cache python-3.13 bash openssl tzdata libsndfile nodejs libevent && break || sleep 5; \
|
|
done
|
|
COPY --from=pgbouncer-builder /usr/local/bin/pgbouncer /usr/local/bin/pgbouncer
|
|
|
|
# Copy only what runtime needs. The application is installed inside the venv;
|
|
# the rest of the builder's /app is source and build metadata that must not
|
|
# ship (manifest-scanning tools attribute everything in it to this image).
|
|
# entrypoint.sh invokes litellm/proxy/prisma_migration.py by source path.
|
|
COPY --from=builder /app/.venv /app/.venv
|
|
COPY --from=builder /app/docker /app/docker
|
|
COPY --from=builder /app/schema.prisma /app/schema.prisma
|
|
COPY --from=builder /app/litellm/proxy/prisma_migration.py /app/litellm/proxy/prisma_migration.py
|
|
# enterprise/ is imported by source path at runtime (proxy_cli puts the
|
|
# working directory on sys.path; litellm/proxy/hooks resolves
|
|
# enterprise.enterprise_hooks from it)
|
|
COPY --from=builder /app/enterprise /app/enterprise
|
|
COPY --from=builder /app/litellm-proxy-extras /app/litellm-proxy-extras
|
|
# Prisma CLI + engines are baked under /opt/prisma, a fixed path every runtime
|
|
# uid can read and that no cache volume mount shadows (unlike /app/.cache or
|
|
# $HOME/.cache under readOnlyRootFilesystem + emptyDir or arbitrary-uid setups).
|
|
# PRISMA_CLI_QUERY_ENGINE_TYPE=binary makes the CLI use the baked binary query
|
|
# engine directly, so `prisma migrate deploy` on a fresh database needs no npm
|
|
# and no network access; without it the CLI looks for the library engine, which
|
|
# prisma stopped baking, and falls back to a download that fails offline or as a
|
|
# non-writable uid (#33650, #24554).
|
|
COPY --from=builder /opt/prisma /opt/prisma
|
|
COPY --from=builder /var/lib/litellm/ui /var/lib/litellm/ui
|
|
COPY --from=builder /var/lib/litellm/assets /var/lib/litellm/assets
|
|
|
|
# XDG_CACHE_HOME is intentionally left unset so it falls back to $HOME/.cache
|
|
# (/app/.cache, writable by the runtime uid). The prisma bake at the read-only
|
|
# /opt/prisma is anchored by PRISMA_BINARY_CACHE_DIR / PRISMA_CLI_PATH, so
|
|
# nothing needs XDG to point there; pointing it at the read-only bake would
|
|
# deny any XDG-aware library that writes a cache at runtime.
|
|
ENV PATH="/app/.venv/bin:${PATH}" \
|
|
PRISMA_BINARY_CACHE_DIR=/opt/prisma/binaries \
|
|
PRISMA_CLI_PATH=/opt/prisma/binaries/node_modules/.bin/prisma \
|
|
PRISMA_CLI_QUERY_ENGINE_TYPE=binary \
|
|
HOME=/app \
|
|
LITELLM_NON_ROOT=true \
|
|
PRISMA_SKIP_POSTINSTALL_GENERATE=1 \
|
|
PRISMA_HIDE_UPDATE_MESSAGE=1 \
|
|
PRISMA_ENGINES_CHECKSUM_IGNORE_MISSING=1 \
|
|
PRISMA_OFFLINE_MODE=true
|
|
|
|
RUN mkdir -p /nonexistent /app/.cache /var/lib/litellm/assets /var/lib/litellm/ui && \
|
|
chown -R nobody:nogroup /app /var/lib/litellm/ui /var/lib/litellm/assets /nonexistent && \
|
|
PRISMA_PATH=$(python -c "import os, prisma; print(os.path.dirname(prisma.__file__))") && \
|
|
chown -R nobody:nogroup "$PRISMA_PATH" && \
|
|
LITELLM_PKG_MIGRATIONS_PATH="$(python -c 'import os, litellm_proxy_extras; print(os.path.dirname(litellm_proxy_extras.__file__))' 2>/dev/null || echo '')/migrations" && \
|
|
[ -n "$LITELLM_PKG_MIGRATIONS_PATH" ] && chown -R nobody:nogroup "$LITELLM_PKG_MIGRATIONS_PATH" || true && \
|
|
LITELLM_PROXY_EXTRAS_PATH=$(python -c "import os, litellm_proxy_extras; print(os.path.dirname(litellm_proxy_extras.__file__))" 2>/dev/null || echo "") && \
|
|
chgrp -R 0 "$PRISMA_PATH" /var/lib/litellm/ui /var/lib/litellm/assets && \
|
|
[ -n "$LITELLM_PROXY_EXTRAS_PATH" ] && chgrp -R 0 "$LITELLM_PROXY_EXTRAS_PATH" || true && \
|
|
chmod -R g=u "$PRISMA_PATH" /var/lib/litellm/ui /var/lib/litellm/assets && \
|
|
[ -n "$LITELLM_PROXY_EXTRAS_PATH" ] && chmod -R g=u "$LITELLM_PROXY_EXTRAS_PATH" || true && \
|
|
chmod -R g+w "$PRISMA_PATH" /var/lib/litellm/ui /var/lib/litellm/assets && \
|
|
[ -n "$LITELLM_PROXY_EXTRAS_PATH" ] && chmod -R g+w "$LITELLM_PROXY_EXTRAS_PATH" || true && \
|
|
chmod -R g+rX "$PRISMA_PATH" /var/lib/litellm/ui /var/lib/litellm/assets && \
|
|
chmod -R a+rX /opt/prisma && \
|
|
test -x /opt/prisma/binaries/node_modules/.bin/prisma && \
|
|
test -f /opt/prisma/binaries/node_modules/prisma/build/index.js && \
|
|
ls /opt/prisma/binaries/node_modules/@prisma/engines/query-engine-* >/dev/null 2>&1 && \
|
|
python -c "from prisma.client import BINARY_PATHS; paths = list(BINARY_PATHS.query_engine.values()); assert paths and all(p.startswith('/opt/prisma/') for p in paths), paths"
|
|
|
|
USER 65534
|
|
|
|
EXPOSE 4000/tcp
|
|
|
|
ENTRYPOINT ["/app/docker/prod_entrypoint.sh"]
|
|
CMD ["--port", "4000"]
|