FROM node:20-bookworm-slim AS build WORKDIR /app RUN apt-get update && apt-get install -y --no-install-recommends python3 make g++ \ && rm -rf /var/lib/apt/lists/* # The lockfile pins what CI tested; a lockless `npm install` re-resolves the whole tree and, since October 2026, crashes npm 10 ("Cannot read properties of null (reading 'edgesOut')"). COPY package.json package-lock.json ./ RUN npm ci COPY tsconfig.json ./ COPY src ./src RUN npm run build RUN cp src/db/schema.sql dist/db/schema.sql && cp src/db/schema.postgres.sql dist/db/schema.postgres.sql FROM node:20-bookworm-slim WORKDIR /app # unrar and 7z unpack RAR/7z downloads (services/archiveExtract.ts; ZIP is handled in Node). # unrar is freeware that permits redistribution but lives in Debian's non-free component, so that # is enabled first — in the deb822 debian.sources this image ships, or a classic sources.list. RUN for f in /etc/apt/sources.list.d/debian.sources /etc/apt/sources.list; do \ [ -f "$f" ] || continue; \ sed -i -E -e '/^Components:/{/[[:space:]]non-free([[:space:]]|$)/!s/$/ non-free/}' \ -e '/^deb[[:space:]]/{/[[:space:]]non-free([[:space:]]|$)/!s/$/ non-free/}' "$f"; \ done \ && apt-get update && apt-get install -y --no-install-recommends python3 python3-pip make g++ ca-certificates curl ffmpeg gnupg \ p7zip-full unrar \ && rm -rf /var/lib/apt/lists/* # ffsubsync re-aligns a downloaded subtitle's timing against the video's own audio (voice-activity # detection, no external API) — used as a best-effort pass on subtitles the picker didn't already # consider a high-confidence match (see subtitleSync.ts). --break-system-packages is required on # Debian bookworm's externally-managed system Python; acceptable here since this container has no # other Python workload to conflict with. RUN pip install --no-cache-dir --break-system-packages ffsubsync # postgresql-client from Debian bookworm is v15, which refuses to pg_dump a newer (16/17) server — # pulling the latest client from PGDG's own repo instead, since a newer pg_dump can dump any older # server but not vice versa. RUN install -d /usr/share/postgresql-common/pgdg \ && curl -o /usr/share/postgresql-common/pgdg/apt.postgresql.org.asc --fail https://www.postgresql.org/media/keys/ACCC4CF8.asc \ && . /etc/os-release \ && echo "deb [signed-by=/usr/share/postgresql-common/pgdg/apt.postgresql.org.asc] https://apt.postgresql.org/pub/repos/apt ${VERSION_CODENAME}-pgdg main" > /etc/apt/sources.list.d/pgdg.list \ && apt-get update && apt-get install -y --no-install-recommends postgresql-client-17 \ && rm -rf /var/lib/apt/lists/* # yt-dlp is fetched as a standalone binary (not via pip) to avoid pulling in a whole Python # packaging toolchain just for one tool — used by the "ytdlp" download client type. RUN curl -L https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp -o /usr/local/bin/yt-dlp \ && chmod +x /usr/local/bin/yt-dlp # gosu lets the entrypoint start as root (needed to chown /config and create a matching # user/group for PUID/PGID) and then drop the node process to that user before exec'ing it. RUN GOSU_ARCH="$(dpkg --print-architecture)" \ && curl -L "https://github.com/tianon/gosu/releases/download/1.17/gosu-${GOSU_ARCH}" -o /usr/local/bin/gosu \ && chmod +x /usr/local/bin/gosu ARG BUILD_TIME="" ARG GIT_COMMIT="" ENV NODE_ENV=production # A file call stuck on a dead mount holds one libuv thread for as long as the mount hangs; the default of 4 # would let a few of them stall DNS, async fs and zlib for the whole process. ENV UV_THREADPOOL_SIZE=16 ENV AONARR_CONFIG_DIR=/config ENV AONARR_BUILD_TIME=$BUILD_TIME ENV AONARR_COMMIT=$GIT_COMMIT ENV AONARR_IMAGE_TAG=server ENV PUID=99 ENV PGID=100 COPY package.json package-lock.json ./ RUN npm ci --omit=dev COPY --from=build /app/dist ./dist COPY CHANGELOG.md ./ # Without --build-arg BUILD_TIME the image still gets a build time (config.ts reads this file): the update # check and the database copy taken before migrations are keyed on it. RUN if [ -z "$BUILD_TIME" ]; then date -u +%Y-%m-%dT%H:%M:%SZ > /app/.build-time; fi COPY entrypoint.sh /entrypoint.sh RUN chmod +x /entrypoint.sh VOLUME ["/config"] EXPOSE 8989 HEALTHCHECK --interval=30s --timeout=5s --start-period=15s --retries=3 \ CMD curl -f http://localhost:8989/api/health || exit 1 ENTRYPOINT ["/entrypoint.sh"]