Files
langflow/docker/build_and_push_ep.Dockerfile
Eric Hare 599878da1d fix(docker): pre-create LANGFLOW_CONFIG_DIR so named volumes inherit uid=1000 ownership (#13212)
* fix: pre-create LANGFLOW_CONFIG_DIR in docker images to fix named volume permissions

The Langflow runtime stage runs as uid=1000 (non-root user), but the
official docker_example compose file mounts a named volume at
/app/langflow (LANGFLOW_CONFIG_DIR). When Docker creates a fresh named
volume, it copies the ownership and permissions of the mount-point
directory from the image. Because /app/langflow did not exist in the
image, Docker materialized the mount point as root:root, and the
non-root container user could not write secret_key, profile_pictures,
or per-user subfolders. The container crashed during initialization
with PermissionError on /app/langflow/secret_key.

Pre-create /app/langflow in every runtime stage and chown it to 1000:0
so a fresh named volume inherits the correct ownership and the
container starts cleanly on a rootful Docker daemon.

Podman's rootless user-namespace mapping masked this for anyone who
validated the compose file on Podman; on a rootful Docker daemon the
container exits in a restart loop.

Fixes #10437

* revert: drop docker_example/README.md changes

The Troubleshooting note will live in the user-facing docs site
instead of the in-repo README.
2026-05-21 18:14:28 +00:00

128 lines
5.1 KiB
Docker

# syntax=docker/dockerfile:1
# Keep this syntax directive! It's used to enable Docker BuildKit
################################
# BUILDER-BASE
# Used to build deps + create our virtual environment
################################
# 1. use python:3.12.3-slim as the base image until https://github.com/pydantic/pydantic-core/issues/1292 gets resolved
# 2. do not add --platform=$BUILDPLATFORM because the pydantic binaries must be resolved for the final architecture
# Use a Python image with uv pre-installed
FROM ghcr.io/astral-sh/uv:python3.14-trixie-slim AS builder
# Install the project into `/app`
WORKDIR /app
# Enable bytecode compilation
ENV UV_COMPILE_BYTECODE=1
# Copy from the cache instead of linking since it's a mounted volume
ENV UV_LINK_MODE=copy
# Set RUSTFLAGS for reqwest unstable features needed by apify-client v2.0.0
ENV RUSTFLAGS='--cfg reqwest_unstable'
RUN apt-get update \
&& apt-get upgrade -y \
&& apt-get install --no-install-recommends -y \
# deps for building python deps
build-essential \
git \
# npm
npm \
# gcc
gcc \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
# Copy files first to avoid permission issues with bind mounts
COPY ./uv.lock /app/uv.lock
COPY ./README.md /app/README.md
COPY ./pyproject.toml /app/pyproject.toml
COPY ./src/backend/base/README.md /app/src/backend/base/README.md
COPY ./src/backend/base/uv.lock /app/src/backend/base/uv.lock
COPY ./src/backend/base/pyproject.toml /app/src/backend/base/pyproject.toml
COPY ./src/lfx/README.md /app/src/lfx/README.md
COPY ./src/lfx/pyproject.toml /app/src/lfx/pyproject.toml
COPY ./src/sdk/README.md /app/src/sdk/README.md
COPY ./src/sdk/pyproject.toml /app/src/sdk/pyproject.toml
# Workspace bundles (LE-1023 pilot+): every directory under ``src/bundles``
# is a uv workspace member, so each bundle's pyproject.toml must be present
# for ``uv sync --no-install-project`` to resolve the workspace. Copy the
# whole tree once rather than enumerating each bundle, so a new bundle does
# not require a Dockerfile edit.
COPY ./src/bundles /app/src/bundles
RUN --mount=type=cache,target=/root/.cache/uv \
RUSTFLAGS='--cfg reqwest_unstable' \
uv sync --frozen --no-install-project --no-editable --extra nv-ingest --extra postgresql --no-group dev
COPY ./src /app/src
COPY src/frontend /tmp/src/frontend
WORKDIR /tmp/src/frontend
RUN --mount=type=cache,target=/root/.npm \
npm ci \
&& ESBUILD_BINARY_PATH="" NODE_OPTIONS="--max-old-space-size=4096" JOBS=1 npm run build \
&& cp -r build /app/src/backend/langflow/frontend \
&& rm -rf /tmp/src/frontend
WORKDIR /app
RUN --mount=type=cache,target=/root/.cache/uv \
RUSTFLAGS='--cfg reqwest_unstable' \
uv sync --frozen --no-editable --extra nv-ingest --extra postgresql --no-group dev
################################
# RUNTIME
# Setup user, utilities and copy the virtual environment only
################################
FROM python:3.14-slim-trixie AS runtime
RUN apt-get update \
&& apt-get upgrade -y \
&& apt-get install --no-install-recommends -y curl git libpq5 gnupg xz-utils \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
COPY --from=builder /usr/local/bin/uv /usr/local/bin/uv
COPY --from=builder /usr/local/bin/uvx /usr/local/bin/uvx
RUN ARCH=$(dpkg --print-architecture) \
&& if [ "$ARCH" = "amd64" ]; then NODE_ARCH="x64"; \
elif [ "$ARCH" = "arm64" ]; then NODE_ARCH="arm64"; \
else NODE_ARCH="$ARCH"; fi \
&& NODE_VERSION=$(curl -fsSL https://nodejs.org/dist/latest-v22.x/ \
| sed -nE "s/.*node-v([0-9]+\.[0-9]+\.[0-9]+)-linux-${NODE_ARCH}\.tar\.xz.*/\1/p" \
| head -1) \
&& if [ -z "$NODE_VERSION" ]; then echo "ERROR: Could not determine Node.js version" && exit 1; fi \
&& curl -fsSL "https://nodejs.org/dist/v${NODE_VERSION}/node-v${NODE_VERSION}-linux-${NODE_ARCH}.tar.xz" \
| tar -xJ -C /usr/local --strip-components=1
RUN useradd user -u 1000 -g 0 --no-create-home --home-dir /app/data
COPY --from=builder --chown=1000 /app/.venv /app/.venv
ENV PATH="/app/.venv/bin:$PATH"
# Pre-create LANGFLOW_CONFIG_DIR (the default location used by the docker_example
# compose file) with the non-root user as owner. When the official compose mounts
# a fresh named volume at /app/langflow, Docker copies this directory's ownership
# and permissions into the new volume, so the in-container uid=1000 user can
# write secret_key, profile_pictures, etc. Without this, the volume is created
# as root:root and Langflow crashes during startup with PermissionError on
# /app/langflow/secret_key. See https://github.com/langflow-ai/langflow/issues/10437
RUN mkdir -p /app/langflow && chown -R 1000:0 /app/langflow && chmod -R g+rwX /app/langflow
LABEL org.opencontainers.image.title=langflow
LABEL org.opencontainers.image.authors=['Langflow']
LABEL org.opencontainers.image.licenses=MIT
LABEL org.opencontainers.image.url=https://github.com/langflow-ai/langflow
LABEL org.opencontainers.image.source=https://github.com/langflow-ai/langflow
WORKDIR /app
ENV LANGFLOW_HOST=0.0.0.0
ENV LANGFLOW_PORT=7860
ENV LANGFLOW_EVENT_DELIVERY=polling
USER 1000
CMD ["python", "-m", "langflow", "run", "--host", "0.0.0.0", "--backend-only"]