Dockerfile.recording-rasterizer
Rama master · réplica de lectura del repositorio original.
#
# Dockerfile.recording-rasterizer - Session Replay Recording Rasterizer Worker
#
# Temporal worker that records session replays to video using Puppeteer + Chromium.
# Based on Dockerfile.node with added Chromium and ffmpeg for video recording.
#
#
# Build stage: Compile nodejs and dependencies
#
FROM ghcr.io/posthog/rust-node-container:bookworm_rust_1.91-node_26.13.0 AS nodejs-build
RUN apt-get update && \
apt-get install -y --no-install-recommends \
"wget" \
"gnupg" \
&& \
mkdir -p /etc/apt/keyrings && \
wget -qO - https://packages.confluent.io/clients/deb/archive.key | gpg --dearmor -o /etc/apt/keyrings/confluent-clients.gpg && \
echo "deb [signed-by=/etc/apt/keyrings/confluent-clients.gpg] https://packages.confluent.io/clients/deb/ bookworm main" > /etc/apt/sources.list.d/confluent-clients.list && \
apt-get update && \
apt-get install -y --no-install-recommends --allow-downgrades \
"make" \
"g++" \
"gcc" \
"python3" \
"librdkafka1=2.12.0-1.cflt~deb12" \
"librdkafka++1=2.12.0-1.cflt~deb12" \
"librdkafka-dev=2.12.0-1.cflt~deb12" \
# libssl pinned to the 3.0 series (ABI-stable), not an exact version: Debian rotates
# point releases out of the security archive, which breaks exact pins on uncached builds.
"libssl-dev=3.0.*" \
"libssl3=3.0.*" \
"zlib1g-dev" \
&& \
rm -rf /var/lib/apt/lists/*
WORKDIR /code
COPY turbo.json package.json pnpm-lock.yaml pnpm-workspace.yaml tsconfig.json ./
COPY ./bin/turbo ./bin/turbo
COPY ./patches ./patches
COPY ./rust ./rust
COPY ./common/esbuilder/ ./common/esbuilder/
COPY ./common/plugin_transpiler/ ./common/plugin_transpiler/
COPY ./common/hogvm/typescript/ ./common/hogvm/typescript/
COPY ./nodejs/package.json ./nodejs/tsconfig.json ./nodejs/
COPY ./common/replay-shared/package.json ./common/replay-shared/
COPY ./common/replay-headless/package.json ./common/replay-headless/
SHELL ["/bin/bash", "-e", "-o", "pipefail", "-c"]
ENV BUILD_LIBRDKAFKA=0 \
PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true
# The base image ships standalone Yarn 1 links that block corepack's own yarn shims.
RUN rm -rf /usr/local/bin/yarn /usr/local/bin/yarnpkg /opt/yarn-v* && \
npm install -g corepack@0.36.0
RUN --mount=type=cache,id=pnpm,target=/tmp/pnpm-store-v24 \
corepack enable && \
NODE_OPTIONS="--max-old-space-size=16384" CI=1 pnpm --filter=@posthog/nodejs... install --frozen-lockfile --store-dir /tmp/pnpm-store-v24 && \
NODE_OPTIONS="--max-old-space-size=16384" CI=1 pnpm --filter=@posthog/replay-headless... install --frozen-lockfile --store-dir /tmp/pnpm-store-v24 && \
NODE_OPTIONS="--max-old-space-size=16384" CI=1 pnpm --filter=@posthog/plugin-transpiler... install --frozen-lockfile --store-dir /tmp/pnpm-store-v24 && \
NODE_OPTIONS="--max-old-space-size=16384" bin/turbo --filter=@posthog/plugin-transpiler build
COPY ./nodejs/src/ ./nodejs/src/
COPY ./nodejs/tests/ ./nodejs/tests/
COPY ./nodejs/assets/ ./nodejs/assets/
COPY ./nodejs/bin/ ./nodejs/bin/
COPY ./common/replay-shared/src/ ./common/replay-shared/src/
COPY ./common/replay-shared/tsconfig.json ./common/replay-shared/
COPY ./common/replay-headless/src/ ./common/replay-headless/src/
COPY ./common/replay-headless/build.mjs ./common/replay-headless/
RUN NODE_OPTIONS="--max-old-space-size=16384" bin/turbo --filter=@posthog/replay-headless build
RUN NODE_OPTIONS="--max-old-space-size=16384" bin/turbo --filter=@posthog/nodejs build
#
# Runtime stage: Node.js + chrome-headless-shell + ffmpeg
#
# Uses chrome-headless-shell instead of full Chromium. This binary supports
# HeadlessExperimental.beginFrame for deterministic frame-by-frame capture
# via puppeteer-capture (virtual time, no rAF dependency).
#
# Major-only tag so weekly rebuilds pick up the latest 26.x (node + bundled OpenSSL/V8 patches).
# Must stay same major as the build stage (node 26) for native-addon ABI. Trixie, not bookworm:
# the uWebSockets.js binary for Node 26 needs glibc 2.38, and bookworm ships 2.36. Addons that
# the bookworm build stage links still load here, because glibc is backward compatible.
FROM node:26-trixie-slim
WORKDIR /code
SHELL ["/bin/bash", "-e", "-o", "pipefail", "-c"]
# Cache-bust marker for the floated deps below (ffmpeg, chrome-headless-shell). The weekly
# scheduled rebuild passes a changing value, invalidating the apt/npx layers so security
# updates are actually re-fetched; normal builds pass the constant default and reuse cache.
# Without this, the unpinned layers' cache key never changes and the rebuild is a no-op.
ARG DEPS_CACHE_BUST=pinned
RUN echo "deps cache bust: ${DEPS_CACHE_BUST}"
# Install runtime dependencies: librdkafka, chrome-headless-shell deps, ffmpeg
RUN apt-get update && \
apt-get install -y --no-install-recommends \
"ca-certificates" \
"wget" \
"gnupg" \
"unzip" \
&& \
mkdir -p /etc/apt/keyrings && \
wget -qO - https://packages.confluent.io/clients/deb/archive.key | gpg --dearmor -o /etc/apt/keyrings/confluent-clients.gpg && \
# The bookworm repo, because Confluent publishes no 2.12.0 build for trixie. The runtime
# librdkafka must match the 2.12.0 headers that the build stage compiles node-rdkafka against.
echo "deb [signed-by=/etc/apt/keyrings/confluent-clients.gpg] https://packages.confluent.io/clients/deb/ bookworm main" > /etc/apt/sources.list.d/confluent-clients.list && \
apt-get update && \
apt-get install -y --no-install-recommends --allow-downgrades \
"librdkafka1=2.12.0-1.cflt~deb12" \
"librdkafka++1=2.12.0-1.cflt~deb12" \
# libssl pinned to the 3.5 series (ABI-stable), not an exact version: Debian rotates
# point releases out of the security archive, which breaks exact pins on uncached builds.
"libssl3t64=3.5.*" \
# ffmpeg intentionally unpinned: tracks the latest trixie candidate so security point-updates flow in on rebuild
"ffmpeg" \
# Fonts for recorded pages: without them CJK, Thai, Indic and emoji render as empty boxes, and common UI font
# stacks (Arial, Helvetica, Roboto) fall back to the wider DejaVu, which changes how text wraps and truncates.
fonts-liberation \
fonts-noto-cjk \
fonts-noto-color-emoji \
fonts-noto-core \
# Shared libraries required by chrome-headless-shell
libasound2t64 \
libatk-bridge2.0-0t64 \
libatk1.0-0t64 \
libcups2t64 \
libdbus-1-3 \
libdrm2 \
libgbm1 \
libglib2.0-0t64 \
libnspr4 \
libnss3 \
libpango-1.0-0 \
libx11-6 \
libxcb1 \
libxcomposite1 \
libxdamage1 \
libxext6 \
libxfixes3 \
libxkbcommon0 \
libxrandr2 \
&& \
rm -rf /var/lib/apt/lists/*
# Install chrome-headless-shell via @puppeteer/browsers CLI.
# This provides the minimal Chrome binary that supports HeadlessExperimental.
# Tracks the stable channel so security patches flow in; the weekly scheduled rebuild
# re-resolves it and the validation step below guards install-time breakage.
RUN npx --yes @puppeteer/browsers install chrome-headless-shell@stable \
--path /opt/chrome-headless-shell && \
ln -s "$(find /opt/chrome-headless-shell -name chrome-headless-shell -type f | head -1)" /usr/local/bin/chrome-headless-shell
RUN groupadd -g 10001 posthog && \
useradd -u 10001 -g posthog -m -d /home/posthog posthog && \
chown -R posthog:posthog /code
USER posthog
ARG COMMIT_HASH
RUN echo ${COMMIT_HASH:-unknown} > /code/commit.txt
# Copy compiled nodejs from build stage
COPY --from=nodejs-build --chown=posthog:posthog /code/common/plugin_transpiler/dist /code/common/plugin_transpiler/dist
COPY --from=nodejs-build --chown=posthog:posthog /code/common/plugin_transpiler/node_modules /code/common/plugin_transpiler/node_modules
COPY --from=nodejs-build --chown=posthog:posthog /code/common/plugin_transpiler/package.json /code/common/plugin_transpiler/package.json
COPY --from=nodejs-build --chown=posthog:posthog /code/common/hogvm/typescript/dist /code/common/hogvm/typescript/dist
COPY --from=nodejs-build --chown=posthog:posthog /code/common/hogvm/typescript/node_modules /code/common/hogvm/typescript/node_modules
COPY --from=nodejs-build --chown=posthog:posthog /code/common/hogvm/typescript/package.json /code/common/hogvm/typescript/package.json
COPY --from=nodejs-build --chown=posthog:posthog /code/nodejs/dist /code/nodejs/dist
COPY --from=nodejs-build --chown=posthog:posthog /code/node_modules /code/node_modules
COPY --from=nodejs-build --chown=posthog:posthog /code/nodejs/node_modules /code/nodejs/node_modules
COPY --from=nodejs-build --chown=posthog:posthog /code/nodejs/package.json /code/nodejs/package.json
COPY --from=nodejs-build --chown=posthog:posthog /code/common/replay-headless/dist /code/common/replay-headless/dist
COPY --from=nodejs-build --chown=posthog:posthog /code/common/replay-headless/package.json /code/common/replay-headless/package.json
ENV NODE_ENV=production \
BUILD_LIBRDKAFKA=0 \
PUPPETEER_EXECUTABLE_PATH=/usr/local/bin/chrome-headless-shell \
PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true
# Validate runtime dependencies
RUN chrome-headless-shell --version && ffmpeg -version
EXPOSE 6738
CMD ["node", "nodejs/dist/session-replay/recording-rasterizer/index.js"]