2025, Dec 21 11:00
How to fix Streamlit + LangGraph Docker startup failure: PermissionError from /nonexistent user home
Dockerizing a Streamlit + LangGraph app can crash with PermissionError to /nonexistent. Fix it by creating the non-root user home via adduser --create-home.
Containerizing a Streamlit + LangGraph app can work flawlessly in a local virtual environment and then fail the moment it enters a Docker image. A recurring error that looks unrelated at first glance ends up being a simple filesystem issue: the application tries to write to a home directory that does not exist inside the container.
Reproducing the issue
After moving the app into Docker and starting it on port 8501, the process crashes with a traceback that ends in a permission error. The critical fragments are easy to miss among asyncio noise, but they are decisive.
RuntimeError: no running event loop
PermissionError: [Errno 13] Permission denied: '/nonexistent'
The stack shows a chain where Streamlit attempts to write a file during startup and eventually tries to create directories under the user home. That path resolves to /nonexistent, which is not writable and, in fact, not even present in the filesystem.
Problematic Docker setup
The root cause sits in the way the non-privileged user is created in the image. The user is given a home path that does not exist and is explicitly created without a home directory. When Streamlit initializes, it attempts to write under the user home, hits /nonexistent, and fails.
# syntax=docker/dockerfile:1
ARG PY_VER=3.11.12
FROM python:${PY_VER}-slim as base
ENV PYTHONDONTWRITEBYTECODE=1
ENV PYTHONUNBUFFERED=1
WORKDIR /app
ARG APP_UID=10001
RUN adduser \
--disabled-password \
--gecos "" \
--home "/nonexistent" \
--shell "/sbin/nologin" \
--no-create-home \
--uid "${APP_UID}" \
runsvc
USER runsvc
COPY . .
EXPOSE 8501
CMD ["streamlit", "run", "./app/main.py", "--server.port=8501", "--server.address=0.0.0.0"]
Why it breaks
The traceback shows Streamlit initializing runtime state and attempting to persist data during startup. The chain ends in a directory creation call under the user home. Because the Docker image sets the user home to /nonexistent and explicitly avoids creating it, the process cannot write there, which triggers PermissionError. The apparent asyncio message is a red herring; the blocking failure is the write attempt to a non-existent home path.
Fix
Create the home directory for the non-privileged user. The minimal change is to allow the user creation command to actually create the specified home path.
RUN adduser \
--disabled-password \
--gecos "" \
--home "/nonexistent" \
--shell "/sbin/nologin" \
--create-home \
--uid "${APP_UID}" \
runsvc
With this change, the path used as the user home exists and is writable by that user, so Streamlit can complete its initialization and the app starts normally on port 8501.
Why this detail matters
Many frameworks perform early writes to user-scoped locations when bootstrapping. Running as a non-root user is a sound Docker best practice, but it also means the user environment must be coherent. A missing home directory can derail startup in ways that are not obvious from the first lines of a stack trace.
Takeaways
If you run Streamlit in Docker under a non-privileged account, ensure the user home exists. A single flag that creates the home directory resolves the startup failure and aligns the container with what the application expects at runtime.