The branch was 12 commits behind dev and GitHub reported it CONFLICTING, which is
why the real CI workflow had not run since 2026-08-10 - a conflicting PR has no
mergeable ref, so pull_request-triggered workflows are skipped. Only the
lightweight checks (semantic title, CodeQL, CodeRabbit) were still reporting,
which made the PR look green while nothing was actually being tested.
Three conflicts, all of them this branch's barrel-diet work meeting dev's new
What's Up Docker integration. In every case the branch's structure is kept and
WUD is absorbed into it rather than the reverse:
integrations/base/creator.ts keep the lazy loader map; add wud as
() => import(...) rather than dev's static import,
which would have undone the lazy creator
integrations/src/index.ts keep the slim barrel; re-export WudStats and
WudContainerUpdate as `export type`, which is
erased at build and so adds no runtime module
widgets/src/index.tsx stays deleted - this was the barrel that put
every widget in the initial client graph
Registering WUD in the slimmed structures then needed three more entries that the
merge could not infer: base/kind-map.ts (type-only kind -> class map),
widgets/src/import.ts (type-only WidgetImports map, both the import and the
member). Each was found by typecheck rather than guessed at.
Verified: 37/37 packages typecheck, 1866/1881 unit tests pass. The 3 failures are
home-assistant and pi-hole, both testcontainers-based; both pass 11/11 in
isolation and fail only under 150-file parallel load with the container's payload
half-initialised.
120 lines
5.4 KiB
Docker
120 lines
5.4 KiB
Docker
# syntax=docker/dockerfile:1.25
|
|
|
|
FROM node:24.18.0-alpine AS base
|
|
|
|
FROM base AS builder
|
|
ARG TARGETPLATFORM
|
|
WORKDIR /app
|
|
# Native dependencies normally use prebuilds, but the build must remain
|
|
# reproducible when a prebuild download times out and compilation is required.
|
|
RUN apk add --no-cache libc6-compat curl bash python3 make g++
|
|
|
|
RUN corepack enable pnpm
|
|
COPY .npmrc pnpm-lock.yaml pnpm-workspace.yaml package.json ./
|
|
COPY patches ./patches
|
|
COPY --parents ./apps/*/package.json ./packages/*/package.json ./tooling/*/package.json ./
|
|
# @homarr/definitions generates documentation types during install.
|
|
COPY --parents ./packages/definitions/src ./
|
|
# Workaround for pnpm/pnpm#5268: pnpm fetch crashes when patchedDependencies
|
|
# are configured with nodeLinker: hoisted. The applyPatchToDir function tries
|
|
# to chdir into node_modules/<pkg> which doesn't exist during fetch (only the
|
|
# content-addressable store is populated). By temporarily switching to the
|
|
# isolated linker, patches apply inside the virtual store (node_modules/.pnpm/...)
|
|
# which IS created by pnpm fetch. The original hoisted linker is restored
|
|
# before the install step so the final node_modules layout stays flat.
|
|
RUN --mount=type=cache,id=pnpm-store,target=/pnpm/store \
|
|
pnpm config set store-dir /pnpm/store && \
|
|
sed -i 's/nodeLinker: hoisted/nodeLinker: isolated/' pnpm-workspace.yaml && \
|
|
pnpm fetch --ignore-scripts && \
|
|
sed -i 's/nodeLinker: isolated/nodeLinker: hoisted/' pnpm-workspace.yaml
|
|
|
|
# Install only from the fetched, committed lockfile so local and Docker builds
|
|
# resolve the same dependency graph. Serial lifecycle builds avoid esbuild's
|
|
# atomic binary replacement racing across the hoisted workspace (pnpm/pnpm#8200).
|
|
RUN --mount=type=cache,id=pnpm-store,target=/pnpm/store \
|
|
npm_config_nodedir=/usr/local pnpm install --recursive --offline --frozen-lockfile --child-concurrency=1
|
|
|
|
COPY . .
|
|
|
|
ARG SKIP_ENV_VALIDATION='true'
|
|
ARG CI='true'
|
|
ARG DISABLE_REDIS_LOGS='true'
|
|
ARG TARGETPLATFORM
|
|
# Opt-in React profiling build + browser source maps, for React DevTools' Profiler
|
|
# and for readable names in heap snapshots. See next.config.ts.
|
|
ARG HOMARR_PROFILING='false'
|
|
ENV HOMARR_PROFILING=${HOMARR_PROFILING}
|
|
|
|
# React ships a separate profiling renderer; the production one omits the hooks
|
|
# DevTools needs, which is what makes it report "Profiling not supported". Next only
|
|
# swaps it via a webpack alias (create-compiler-aliases.js), so under Turbopack both
|
|
# `next build --profile` and `reactProductionProfiling` silently produce a normal
|
|
# build. Point the vendored client entry at the profiling bundle instead — the same
|
|
# swap webpack's alias performs, done where Turbopack cannot miss it.
|
|
RUN if [ "$HOMARR_PROFILING" = "true" ]; then \
|
|
set -e; \
|
|
entry=node_modules/next/dist/compiled/react-dom/client.js; \
|
|
grep -q "react-dom-client.production.js" "$entry" || { echo "profiling patch: unexpected $entry"; exit 1; }; \
|
|
sed -i "s#./cjs/react-dom-client.production.js#./cjs/react-dom-profiling.profiling.js#" "$entry"; \
|
|
grep -q "react-dom-profiling.profiling.js" "$entry" || { echo "profiling patch failed"; exit 1; }; \
|
|
echo "profiling patch applied to $entry"; \
|
|
fi
|
|
|
|
RUN --mount=type=secret,id=TURBO_API,env=TURBO_API \
|
|
--mount=type=secret,id=TURBO_TEAM,env=TURBO_TEAM \
|
|
--mount=type=secret,id=TURBO_TOKEN,env=TURBO_TOKEN \
|
|
--mount=type=secret,id=TURBO_REMOTE_CACHE_SIGNATURE_KEY,env=TURBO_REMOTE_CACHE_SIGNATURE_KEY \
|
|
--mount=type=cache,id=homarr-next-build-${TARGETPLATFORM},target=/app/apps/nextjs/.next/cache,sharing=locked \
|
|
TURBO_PLATFORM="${TARGETPLATFORM:-linux/amd64}" \
|
|
pnpm turbo build --filter=@homarr/nextjs... --filter=@homarr/cli
|
|
|
|
FROM base AS runner
|
|
WORKDIR /app
|
|
|
|
# gettext is required for envsubst, openssl for generating AUTH_SECRET, su-exec for running application as non-root
|
|
RUN apk add --no-cache redis nginx bash gettext su-exec openssl
|
|
RUN mkdir /appdata
|
|
VOLUME /appdata
|
|
|
|
# Enable homarr cli
|
|
COPY --from=builder /app/packages/cli/cli.cjs /app/apps/cli/cli.cjs
|
|
RUN echo $'#!/bin/bash\ncd /app/apps/cli && node ./cli.cjs "$@"' > /usr/bin/homarr
|
|
RUN chmod +x /usr/bin/homarr
|
|
|
|
# Don't run production as root
|
|
RUN mkdir -p /var/cache/nginx && \
|
|
mkdir -p /var/log/nginx && \
|
|
mkdir -p /var/lib/nginx && \
|
|
touch /run/nginx/nginx.pid && \
|
|
mkdir -p /etc/nginx/templates /etc/nginx/ssl/certs
|
|
|
|
COPY --from=builder /app/apps/nextjs/next.config.ts .
|
|
COPY --from=builder /app/apps/nextjs/package.json .
|
|
COPY --from=builder /app/pnpm-workspace.yaml ./pnpm-workspace.yaml
|
|
|
|
COPY --from=builder /app/node_modules/better-sqlite3/build/Release/better_sqlite3.node /app/build/better_sqlite3.node
|
|
|
|
COPY --from=builder /app/packages/db/migrations ./db/migrations
|
|
|
|
# Automatically leverage output traces to reduce image size
|
|
# https://nextjs.org/docs/advanced-features/output-file-tracing
|
|
COPY --from=builder /app/apps/nextjs/.next/standalone ./
|
|
COPY --from=builder /app/apps/nextjs/.next/static ./apps/nextjs/.next/static
|
|
COPY --from=builder /app/apps/nextjs/public ./apps/nextjs/public
|
|
COPY scripts/run.sh ./run.sh
|
|
COPY --chmod=755 scripts/entrypoint.sh ./entrypoint.sh
|
|
COPY packages/redis/redis.conf /app/redis.conf
|
|
COPY nginx.conf /etc/nginx/templates/nginx.conf
|
|
|
|
|
|
ENV DB_URL='/appdata/db/db.sqlite'
|
|
ENV DB_DIALECT='sqlite'
|
|
ENV DB_DRIVER='better-sqlite3'
|
|
ENV AUTH_PROVIDERS='credentials'
|
|
ENV REDIS_IS_EXTERNAL='false'
|
|
ENV NODE_ENV='production'
|
|
|
|
EXPOSE 7575
|
|
ENTRYPOINT [ "/app/entrypoint.sh" ]
|
|
CMD ["sh", "run.sh"]
|