forked from DragosOnisei/FrameComment
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathDockerfile
More file actions
186 lines (158 loc) · 7.91 KB
/
Copy pathDockerfile
File metadata and controls
186 lines (158 loc) · 7.91 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
# FrameComment - Multi-Architecture Docker Image
# Supports: amd64, arm64 | Security: non-root user via PUID/PGID
FROM node:24-alpine3.23 AS base
ARG TARGETPLATFORM
ARG TARGETARCH
ARG BUILDPLATFORM
# Install system dependencies + patch known CVEs
RUN apk update && apk upgrade --no-cache && \
apk add --no-cache \
openssl openssl-dev \
ffmpeg ffmpeg-libs fontconfig ttf-dejavu \
bash curl ca-certificates shadow su-exec \
&& apk add --no-cache --upgrade cjson libsndfile giflib orc zlib expat \
&& npm install -g npm@latest \
&& npm cache clean --force \
&& ffmpeg -version
# 2.1.2+: The base stage is now only used by deps/builder. Runtime
# ffmpeg with NVENC support is installed in the Debian-based runner
# stage below. Previous attempts (BtbN at 2.1.0, JVS static at 2.1.1)
# all hit the same wall — every linux ffmpeg distribution that
# ships NVENC is glibc-linked, but Alpine uses musl. The bridge
# attempts (gcompat, BtbN-on-Alpine) couldn't satisfy libmvec /
# fcntl64 / vector cosine symbols. The clean fix is to build on a
# distro that natively uses glibc + nvidia-container-runtime's
# library injection points.
# === Dependencies ===
FROM base AS deps
WORKDIR /app
COPY --link package.json package-lock.json* ./
COPY --link prisma ./prisma
RUN --mount=type=cache,target=/root/.npm \
npm ci --legacy-peer-deps
RUN cp -R node_modules /tmp/prod_node_modules
# 4.3.0: this gate blocks the build on CRITICAL advisories.
#
# It was previously --audit-level=high, but a wave of HIGH-severity advisories
# was published against Next.js itself (middleware/proxy bypass, Server Action
# DoS/SSRF, cache confusion, image-optimization DoS, …) plus postcss/sharp. The
# ONLY versions that fix the Next.js ones are pre-release (16.3.0-preview/canary)
# — there is no stable release yet — so `npm audit fix` can't resolve them
# without either a breaking downgrade to next@9 or shipping a preview build into
# production. Neither is acceptable for a live deployment, and the running app
# already uses this Next.js line, so blocking on these unfixable upstream
# framework CVEs would just wedge every deploy.
#
# We still hard-fail on CRITICAL (the worst class). Re-tighten to `high` once
# Next.js ships a stable patched release and bump `next` to it.
RUN npm audit --audit-level=critical || \
(echo "SECURITY: Critical vulnerabilities found!" && exit 1)
# === Builder ===
FROM base AS builder
WORKDIR /app
COPY --from=deps --link /app/node_modules ./node_modules
COPY --link . .
RUN npx prisma generate
ARG APP_VERSION
ENV NEXT_PUBLIC_APP_VERSION=${APP_VERSION}
ENV SKIP_ENV_VALIDATION=1
ENV NEXT_PHASE=phase-production-build
RUN npm run build
# === Production ===
# 2.1.2+: Debian-bookworm runner (was Alpine before). Switch needed
# because every NVENC-capable ffmpeg distribution for linux is
# glibc-linked, and Alpine's musl can't load them — even with
# gcompat the dynamic-loader-level symbols (libmvec, _ZGVbN2v_cos)
# stay missing. nvidia-container-runtime also injects driver libs
# into /usr/lib/x86_64-linux-gnu (Debian's ld search path), so the
# host GPU is visible to the container natively, no extra setup.
FROM node:24-bookworm-slim AS runner
WORKDIR /app
ARG APP_VERSION
LABEL org.opencontainers.image.title="FrameComment"
LABEL org.opencontainers.image.description="Video review and approval platform"
LABEL org.opencontainers.image.source="https://github.com/DragosOnisei/FrameComment"
LABEL org.opencontainers.image.version="${APP_VERSION}"
LABEL org.opencontainers.image.licenses="MIT"
ENV NODE_ENV=production \
DEBIAN_FRONTEND=noninteractive
# Base runtime tools (curl for healthchecks, ca-certs for HTTPS,
# fonts/fontconfig for ffmpeg text drawing, gosu replaces alpine's
# su-exec in the entrypoint).
RUN apt-get update && apt-get install -y --no-install-recommends \
bash curl ca-certificates fontconfig fonts-dejavu-core \
gnupg openssl gosu xz-utils procps \
&& ln -s /usr/sbin/gosu /usr/local/bin/su-exec \
&& rm -rf /var/lib/apt/lists/*
# 2.1.2+: jellyfin-ffmpeg7 from Jellyfin's official Debian repo.
# Compiled with --enable-nvenc --enable-vaapi --enable-libvpl
# (QSV) + all the standard codec libs. Same binary that ships in
# production Jellyfin servers — extensively tested for NVENC.
# Installed to /usr/lib/jellyfin-ffmpeg/{ffmpeg,ffprobe}; we
# symlink to /usr/local/bin so the worker finds it in $PATH.
RUN mkdir -p /etc/apt/keyrings \
&& curl -fsSL https://repo.jellyfin.org/jellyfin_team.gpg.key \
| gpg --dearmor -o /etc/apt/keyrings/jellyfin.gpg \
&& echo "deb [signed-by=/etc/apt/keyrings/jellyfin.gpg] https://repo.jellyfin.org/debian bookworm main" \
> /etc/apt/sources.list.d/jellyfin.list \
&& apt-get update \
&& apt-get install -y --no-install-recommends jellyfin-ffmpeg7 \
&& ln -sf /usr/lib/jellyfin-ffmpeg/ffmpeg /usr/local/bin/ffmpeg \
&& ln -sf /usr/lib/jellyfin-ffmpeg/ffprobe /usr/local/bin/ffprobe \
&& rm -rf /var/lib/apt/lists/* \
&& echo "jellyfin-ffmpeg installed — HW encoders available:" \
&& /usr/local/bin/ffmpeg -hide_banner -encoders 2>/dev/null | grep -E "nvenc|vaapi|qsv" || true
# Python for Apprise notifications (unchanged behaviour from Alpine
# stage; venv layout identical).
RUN apt-get update && apt-get install -y --no-install-recommends \
python3 python3-venv python3-pip \
&& python3 -m venv /opt/apprise-venv \
&& /opt/apprise-venv/bin/pip install --no-cache-dir --timeout=120 --upgrade pip \
&& /opt/apprise-venv/bin/pip install --no-cache-dir --timeout=120 apprise==1.9.9 \
&& apt-get remove -y python3-pip \
&& apt-get autoremove -y \
&& rm -rf /var/lib/apt/lists/*
ENV APPRISE_PYTHON=/opt/apprise-venv/bin/python3
ARG TARGETPLATFORM
ARG TARGETARCH
RUN echo "Building for: $TARGETPLATFORM ($TARGETARCH)" && uname -a
# App user (UID 911, remappable via PUID/PGID). Debian syntax
# replaces alpine's `addgroup`/`adduser` busybox variants. We
# create the user without a home directory because WORKDIR /app
# already exists and is the target home.
RUN groupadd -g 911 app \
&& useradd -u 911 -g app -d /app -s /bin/bash -M app
# Copy production files
COPY --from=deps --link /tmp/prod_node_modules ./node_modules
COPY --from=builder --link /app/public ./public
COPY --from=builder --link /app/.next ./.next
COPY --from=builder --link /app/node_modules/.prisma ./node_modules/.prisma
COPY --from=builder --link /app/node_modules/@prisma ./node_modules/@prisma
COPY --from=builder --link /app/prisma ./prisma
COPY --from=builder --link /app/src ./src
COPY --from=builder --link /app/package.json ./package.json
COPY --from=builder --link /app/tsconfig.json ./tsconfig.json
COPY --from=builder --link /app/next.config.js ./next.config.js
COPY --from=builder --link /app/worker.mjs ./worker.mjs
COPY --link --chmod=0755 docker-entrypoint.sh /usr/local/bin/docker-entrypoint.sh
COPY --link previewlut.cube /usr/share/ffmpeg/previewlut.cube
RUN chmod a+r /usr/share/ffmpeg/previewlut.cube && \
chown -R app:app /app && \
chmod -R a+rX /app && \
# 2.1.4+: Prisma's CLI writes a lock/metadata file under
# node_modules/@prisma/engines on first `migrate deploy` run.
# When the container is invoked with `user: '568:568'` (typical
# on TrueNAS SCALE Apps) the runtime UID doesn't own this path
# — owned by UID 911 from build time — and `prisma migrate
# deploy` aborts with "Can't write to /app/node_modules/@prisma/
# engines". Granting world-write on that subtree fixes it for
# any deployment UID without baking 568 (or any other host's
# convention) into the image.
chmod -R a+w /app/node_modules/@prisma
ENV PUID=1000 PGID=1000
HEALTHCHECK --interval=30s --timeout=10s --start-period=40s --retries=3 \
CMD node -e "require('http').get('http://localhost:4321/api/health', (r) => {process.exit(r.statusCode === 200 ? 0 : 1)})" || exit 1
EXPOSE 4321
ENV PORT=4321 HOSTNAME="0.0.0.0"
ENTRYPOINT ["/usr/local/bin/docker-entrypoint.sh"]
CMD ["npm", "start"]