98 lines
5 KiB
Docker
98 lines
5 KiB
Docker
# =============================================================================
|
|
# VideoPress — Dockerfile
|
|
# =============================================================================
|
|
#
|
|
# Build:
|
|
# docker build -t videopress .
|
|
#
|
|
# Run (quick):
|
|
# docker run -d \
|
|
# -p 8080:8080 \
|
|
# -v /your/video/path:/media \
|
|
# --name videopress \
|
|
# videopress
|
|
#
|
|
# See docker-compose.yml for a fully-configured example.
|
|
# =============================================================================
|
|
|
|
# ── Stage 1: Python dependency builder ──────────────────────────────────────
|
|
FROM python:3.12-slim AS builder
|
|
|
|
WORKDIR /build
|
|
|
|
# Install build tools needed to compile gevent's C extensions
|
|
RUN apt-get update && apt-get install -y --no-install-recommends \
|
|
gcc \
|
|
libffi-dev \
|
|
&& rm -rf /var/lib/apt/lists/*
|
|
|
|
COPY requirements.txt .
|
|
|
|
# Install into an isolated prefix so we can copy just the packages
|
|
RUN pip install --upgrade pip \
|
|
&& pip install --prefix=/install --no-cache-dir -r requirements.txt
|
|
|
|
|
|
# ── Stage 2: Runtime image ───────────────────────────────────────────────────
|
|
FROM python:3.12-slim
|
|
|
|
# ── System packages: ffmpeg (includes ffprobe) ───────────────────────────────
|
|
RUN apt-get update && apt-get install -y --no-install-recommends \
|
|
ffmpeg \
|
|
&& rm -rf /var/lib/apt/lists/*
|
|
|
|
# ── Copy Python packages from builder ────────────────────────────────────────
|
|
COPY --from=builder /install /usr/local
|
|
|
|
# ── Create a non-root application user ───────────────────────────────────────
|
|
# UID/GID 1000 is conventional and matches most Linux desktop users,
|
|
# which makes the volume-mapped files readable without extra chown steps.
|
|
RUN groupadd --gid 1000 appuser \
|
|
&& useradd --uid 1000 --gid 1000 --no-create-home --shell /sbin/nologin appuser
|
|
|
|
# ── Application code ─────────────────────────────────────────────────────────
|
|
WORKDIR /app
|
|
COPY app/ app/
|
|
COPY wsgi.py run.py gunicorn.conf.py requirements.txt ./
|
|
COPY templates/ templates/
|
|
COPY static/ static/
|
|
|
|
# ── Media volume mount point ──────────────────────────────────────────────────
|
|
# The host directory containing videos is mounted here at runtime.
|
|
# All file-system access by the application is restricted to this path.
|
|
RUN mkdir -p /media && chown appuser:appuser /media
|
|
|
|
# ── Data directory for the SQLite settings database ──────────────────────────
|
|
# Mounted as a named volume in docker-compose so settings survive restarts.
|
|
RUN mkdir -p /data && chown appuser:appuser /data
|
|
|
|
# ── File ownership ────────────────────────────────────────────────────────────
|
|
RUN chown -R appuser:appuser /app
|
|
|
|
# ── Switch to non-root user ───────────────────────────────────────────────────
|
|
USER appuser
|
|
|
|
# ── Environment defaults (all overridable via docker run -e or compose) ──────
|
|
# MEDIA_ROOT — the path inside the container where videos are accessed.
|
|
# Must match the container-side of your volume mount.
|
|
# PORT — TCP port Gunicorn listens on (exposed below).
|
|
# LOG_LEVEL — Gunicorn log verbosity (debug | info | warning | error).
|
|
ENV MEDIA_ROOT=/media \
|
|
DB_PATH=/data/videopress.db \
|
|
PORT=8080 \
|
|
LOG_LEVEL=info \
|
|
PYTHONUNBUFFERED=1 \
|
|
PYTHONDONTWRITEBYTECODE=1
|
|
|
|
# ── Expose the web UI port ────────────────────────────────────────────────────
|
|
# The UI and API share the same port — no separate API port is needed.
|
|
EXPOSE 8080
|
|
|
|
# ── Health check ─────────────────────────────────────────────────────────────
|
|
HEALTHCHECK --interval=30s --timeout=10s --start-period=15s --retries=3 \
|
|
CMD python3 -c \
|
|
"import urllib.request; urllib.request.urlopen('http://localhost:8080/')" \
|
|
|| exit 1
|
|
|
|
# ── Start Gunicorn ────────────────────────────────────────────────────────────
|
|
CMD ["gunicorn", "-c", "gunicorn.conf.py", "wsgi:application"]
|