RAGELITE / Dockerfile
triflix
initial: automAIta boilerplate (FastAPI + React PWA, single-port HF Space)
a6229e1
# syntax=docker/dockerfile:1.7
#
# automAIta β€” single-container image for HuggingFace Spaces.
#
# HF Space contract (https://huggingface.co/docs/hub/spaces-sdks-docker):
# - Container runs as UID 1000. β†’ user `app`, USER app below.
# - Single port exposed (default 7860). β†’ EXPOSE + uvicorn --port 7860.
# - $HOME must be writable by the runtime user. β†’ ENV HOME=/home/app.
# - Persistent storage (when enabled in Space settings) is mounted at /data
# at RUNTIME ONLY β€” not available during build. Pre-create + chown the dir
# so the runtime mount lands on an owned, writable path.
# - Don't use `VOLUME` for /data (HF doesn't honor it).
# - Build-time secrets must be declared with --mount=type=secret,id=NAME and
# read from /run/secrets/NAME β€” DO NOT bake secrets into env vars.
# ---------- Stage 1: build the React PWA ----------
FROM node:20-alpine AS frontend-build
WORKDIR /fe
COPY frontend/package.json frontend/package-lock.json* ./
RUN npm install --no-audit --no-fund
COPY frontend/ ./
RUN npm run build
# ---------- Stage 2: runtime (FastAPI serves API + built static) ----------
FROM python:3.12-slim AS runtime
ENV PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1 \
UV_LINK_MODE=copy \
UV_COMPILE_BYTECODE=1 \
UV_PROJECT_ENVIRONMENT=/app/.venv \
HOME=/home/app \
PATH="/app/.venv/bin:/home/app/.local/bin:${PATH}" \
AUTOMAITA_STATIC_DIR=/app/static \
AUTOMAITA_DATA_DIR=/data
# Non-root user matching the HF Space UID.
RUN groupadd --system --gid 1000 app \
&& useradd --system --uid 1000 --gid 1000 --create-home --home-dir /home/app --shell /bin/bash app
WORKDIR /app
# Install uv (pip is fine for a single binary install).
RUN pip install --no-cache-dir uv==0.5.11
# Install backend deps. Cached layer if pyproject + lock are unchanged.
COPY --chown=app:app backend/pyproject.toml backend/uv.lock* ./
RUN uv sync --frozen --no-dev || uv sync --no-dev
# Backend source.
COPY --chown=app:app backend/app ./app
# Built frontend β†’ /app/static.
COPY --from=frontend-build --chown=app:app /fe/dist ./static
# Pre-create /data so the runtime persistent-storage mount lands on a path
# already owned by uid 1000. If no storage is attached, this directory still
# exists and is writable β€” the app falls back to ephemeral writes.
RUN mkdir -p /data && chown -R 1000:1000 /data
RUN chown -R app:app /app /home/app
USER app
EXPOSE 7860
CMD ["uv", "run", "--no-sync", "uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "7860"]