Skip to content
Closed
70 changes: 21 additions & 49 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@
#
# ---------------------------------------------------------
#
FROM node:24.13.0-bookworm-slim AS node-base
# Digest-pinned because the runtime stage copies its `node` binary out of this stage.
FROM node:24.13.0-bookworm-slim@sha256:4660b1ca8b28d6d1906fd644abe34b2ed81d15434d26d845ef0aced307cf4b6f AS node-base
WORKDIR /code
SHELL ["/bin/bash", "-e", "-o", "pipefail", "-c"]

Expand Down Expand Up @@ -111,7 +112,7 @@
GITHUB_REF_NAME=$GITHUB_REF_NAME \
GITHUB_REPOSITORY=$GITHUB_REPOSITORY \
GITHUB_SERVER_URL=$GITHUB_SERVER_URL
RUN --mount=type=secret,id=posthog_upload_sourcemaps_cli_api_key \

Check warning on line 115 in Dockerfile

View workflow job for this annotation

GitHub Actions / Lint changed Dockerfiles

Pin versions in apt get install. Instead of `apt-get install <package>` use `apt-get install <package>=<version>`
if ( \
[ -f /run/secrets/posthog_upload_sourcemaps_cli_api_key ] && \
apt-get update && \
Expand Down Expand Up @@ -169,7 +170,7 @@
# self-contained 24MB package with no deps). Materialize it as real files inside the transpiler's
# own node_modules, replacing the pnpm symlink that pointed into the root node_modules. The final
# image then carries just this package instead of the entire ~469MB root /code/node_modules.
RUN cd /code/common/plugin_transpiler && \

Check warning on line 173 in Dockerfile

View workflow job for this annotation

GitHub Actions / Lint changed Dockerfiles

Use WORKDIR to switch to a directory
BABEL_REAL=$(node -e "process.stdout.write(require('path').dirname(require.resolve('@babel/standalone/package.json')))") && \
rm -rf node_modules/@babel/standalone && \
mkdir -p node_modules/@babel && \
Expand All @@ -193,7 +194,7 @@
ENV UV_PROJECT_ENVIRONMENT=/python-runtime

# Install build dependencies
RUN apt-get update && \

Check warning on line 197 in Dockerfile

View workflow job for this annotation

GitHub Actions / Lint changed Dockerfiles

Pin versions in apt get install. Instead of `apt-get install <package>` use `apt-get install <package>=<version>`
apt-get install -y --no-install-recommends \
"build-essential" \
"git" \
Expand Down Expand Up @@ -271,7 +272,7 @@
SHELL ["/bin/bash", "-e", "-o", "pipefail", "-c"]

# Fetch the GeoLite2-City database that will be used for IP geolocation within Django.
RUN apt-get update && \

Check warning on line 275 in Dockerfile

View workflow job for this annotation

GitHub Actions / Lint changed Dockerfiles

Pin versions in apt get install. Instead of `apt-get install <package>` use `apt-get install <package>=<version>`
apt-get install -y --no-install-recommends \
"ca-certificates" \
"curl" \
Expand All @@ -286,22 +287,25 @@
#
# ---------------------------------------------------------
#
FROM python:3.14.7-bookworm@sha256:ecac9e212daacda8a702eae372fceebc0ee36f5805abe087880367e8d061fa5b
# Same digest as the posthog-build stage, so the interpreter matches the one the wheels were built against.
FROM python:3.14.7-slim-bookworm@sha256:9ab8d9c8514b44f90cf0029dd42fdd7e9e211e639c8b995304cc04568dee900f
WORKDIR /code
SHELL ["/bin/bash", "-e", "-o", "pipefail", "-c"]
ENV PYTHONUNBUFFERED 1
ENV PYTHONUNBUFFERED=1
# Granian embeds libpython instead of launching the python3 CLI, so PEP 538 C-locale
# coercion never runs and open() defaults to ASCII under the container's bare locale.
# Force UTF-8 so file reads with non-ASCII bytes don't raise UnicodeDecodeError.
ENV PYTHONUTF8 1
ENV LANG C.UTF-8
ENV PYTHONUTF8=1
ENV LANG=C.UTF-8
# Install OS runtime dependencies.
# Note: please add in this stage runtime dependences only!
# Runtime-only shared libs: lxml/xmlsec are compiled --no-binary in the build stage (which keeps
# its own -dev headers), so the final image needs the runtime .so, not the -dev headers/static libs.
# libxmlsec1-openssl provides the OpenSSL crypto backend that libxmlsec1-dev used to pull in.
RUN apt-get update && \

Check warning on line 305 in Dockerfile

View workflow job for this annotation

GitHub Actions / Lint changed Dockerfiles

Pin versions in apt get install. Instead of `apt-get install <package>` use `apt-get install <package>=<version>`
apt-get install -y --no-install-recommends --allow-downgrades \
# The deploy chart's asset-upload hook runs in this image and downloads s5cmd with curl.
"curl" \
"git" \
"libpq5" \
"libxmlsec1=1.2.37-2" \
Expand All @@ -311,56 +315,24 @@
# point releases out of the security archive, which breaks exact pins on uncached builds.
"libssl3=3.0.*" \
"libjemalloc2" \
# Numba's OpenMP backend needs the system libgomp runtime.
"libgomp1" \
# Python's mimetypes uses /etc/mime.types for artifact content types.
"media-types" \
# psutil is not installed, so joblib's loky kills worker process trees with pgrep.
# Operators also need ps to find a PID for py-spy.
"procps" \
&& \
rm -rf /var/lib/apt/lists/*

# Note: no MS SQL ODBC driver is installed — the data-warehouse MSSQL source uses pymssql, which
# bundles FreeTDS in its wheel and does not use msodbcsql18/unixodbc (there is no pyodbc in the tree).

# Install Node.js 24.13.0 for standalone scripts with architecture detection and verification.
# Only the `node` binary is used at runtime (the plugin transpiler subprocess), so npm/npx/corepack/
# headers are stripped after install. Note: the dev-only `create_channel_definitions_file` management
# command shells out to `npx prettier` to regenerate a checked-in file; it is not run in this image.
ENV NODE_VERSION 24.13.0

RUN ARCH= && dpkgArch="$(dpkg --print-architecture)" \
&& case "${dpkgArch##*-}" in \
amd64) ARCH='x64';; \
ppc64el) ARCH='ppc64le';; \
s390x) ARCH='s390x';; \
arm64) ARCH='arm64';; \
armhf) ARCH='armv7l';; \
i386) ARCH='x86';; \
*) echo "unsupported architecture"; exit 1 ;; \
esac \
&& export GNUPGHOME="$(mktemp -d)" \
&& set -ex \
&& for key in \
5BE8A3F6C8A5C01D106C0AD820B1A390B168D356 \
C0D6248439F1D5604AAFFB4021D900FFDB233756 \
DD792F5973C6DE52C432CBDAC77ABFA00DDBF2B7 \
CC68F5A3106FF448322E48ED27F5E38D5B0A215F \
8FCCA13FEF1D0C2E91008E09770F7A9A5AE15600 \
890C08DB8579162FEE0DF9DB8BEAB4DFCF555EF4 \
C82FA3AE1CBEDC6BE46B9360C43CEC45C17AB93C \
108F52B48DB57BB0CC439B2997B01419BD92F80A \
A363A499291CBBC940DD62E41F10027AF002F8B0 \
; do \
{ gpg --batch --keyserver hkps://keys.openpgp.org --recv-keys "$key" && gpg --batch --fingerprint "$key"; } || \
{ gpg --batch --keyserver keyserver.ubuntu.com --recv-keys "$key" && gpg --batch --fingerprint "$key"; } ; \
done \
&& curl -fsSLO --compressed "https://nodejs.org/dist/v$NODE_VERSION/node-v$NODE_VERSION-linux-$ARCH.tar.xz" \
&& curl -fsSLO --compressed "https://nodejs.org/dist/v$NODE_VERSION/SHASUMS256.txt.asc" \
&& gpg --batch --decrypt --output SHASUMS256.txt SHASUMS256.txt.asc \
&& gpgconf --kill all \
&& rm -rf "$GNUPGHOME" \
&& grep " node-v$NODE_VERSION-linux-$ARCH.tar.xz\$" SHASUMS256.txt | sha256sum -c - \
&& tar -xJf "node-v$NODE_VERSION-linux-$ARCH.tar.xz" -C /usr/local --strip-components=1 --no-same-owner \
&& rm "node-v$NODE_VERSION-linux-$ARCH.tar.xz" SHASUMS256.txt.asc SHASUMS256.txt \
&& ln -s /usr/local/bin/node /usr/local/bin/nodejs \
&& node --version \
&& rm -rf /usr/local/lib/node_modules/npm /usr/local/lib/node_modules/corepack /usr/local/bin/npm /usr/local/bin/npx /usr/local/bin/corepack /usr/local/include/node \
&& rm -rf /tmp/*
# Only the `node` binary is used at runtime (the plugin transpiler subprocess). Note: the dev-only
# `create_channel_definitions_file` management command shells out to `npx prettier` to regenerate a
# checked-in file; it is not run in this image.
COPY --from=node-base /usr/local/bin/node /usr/local/bin/node
RUN ln -s /usr/local/bin/node /usr/local/bin/nodejs && node --version

# Install and use a non-root user.
# Pin uid/gid to a fixed, host-safe value (avoid 1000, which maps to ec2-user on the nodes).
Expand Down Expand Up @@ -446,8 +418,8 @@

# Validate the Playwright client library (used to drive the remote browserless service over CDP —
# no browser binary ships in this image).
RUN /python-runtime/bin/python -c "import playwright; print('Playwright package imported successfully')"

Check notice on line 421 in Dockerfile

View workflow job for this annotation

GitHub Actions / Lint changed Dockerfiles

Multiple consecutive `RUN` instructions. Consider consolidation.
RUN /python-runtime/bin/python -c "from playwright.sync_api import sync_playwright; print('Playwright sync API available')"

Check notice on line 422 in Dockerfile

View workflow job for this annotation

GitHub Actions / Lint changed Dockerfiles

Multiple consecutive `RUN` instructions. Consider consolidation.

# Setup ENV.
ENV NODE_ENV=production
Expand All @@ -459,5 +431,5 @@
EXPOSE 8001
# Root is needed only so bin/docker-server can drop the app to nobody with setpriv.
# nosemgrep: dockerfile.security.last-user-is-root.last-user-is-root
USER root

Check warning on line 434 in Dockerfile

View workflow job for this annotation

GitHub Actions / Lint changed Dockerfiles

Last USER should not be root
CMD ["./bin/docker"]
Loading