# syntax=docker/dockerfile:1.4 # ============================================================================= # Dockhand Docker Image - Baseline Build (Alpine/musl, amd64 only) # ============================================================================= # For older x86_64 hardware without AVX2/SSE4.2 (TrueNAS, older Intel Atom/Celeron) # Uses node:24-alpine (musl libc) compiled conservatively for all x86_64 CPUs. # The Wolfi/glibc build crashes with SIGILL on CPUs that don't support the # microarchitecture level Wolfi packages are compiled for. # ============================================================================= # ----------------------------------------------------------------------------- # Stage 1: Application Builder (Alpine - musl-compatible native addons) # ----------------------------------------------------------------------------- # IMPORTANT: Must use alpine builder so native addons (better-sqlite3) are # compiled against musl libc, not glibc. Cross-ABI copies would not work. FROM node:24-alpine AS app-builder WORKDIR /app # Install build dependencies RUN apk add --no-cache git curl python3 make g++ gcc musl-dev # Build getrandom shim for old kernels (< 3.17) that lack the syscall COPY shims/getrandom-shim.c /tmp/ RUN gcc -shared -fPIC -O2 -o /tmp/libgetrandom-shim.so /tmp/getrandom-shim.c # Copy package files and install dependencies (--ignore-scripts blocks malicious postinstall hooks) COPY package.json package-lock.json ./ RUN npm ci --ignore-scripts \ && npm rebuild better-sqlite3 argon2 # Copy source code and build COPY . . RUN npm run build # Production dependencies only # Preserve better-sqlite3 native addon (no prebuilds exist for Node 24 ABI 137) RUN cp -r node_modules/better-sqlite3/build /tmp/better-sqlite3-build \ && rm -rf node_modules \ && npm ci --omit=dev --ignore-scripts \ && cp -r /tmp/better-sqlite3-build node_modules/better-sqlite3/build \ && rm -rf node_modules/@types /tmp/better-sqlite3-build # ----------------------------------------------------------------------------- # Stage 2: Go Collector Builder # ----------------------------------------------------------------------------- FROM golang:1.25.8 AS go-builder WORKDIR /app COPY collector/ ./collector/ RUN cd collector && CGO_ENABLED=0 go build -o /app/bin/collection-worker . # ----------------------------------------------------------------------------- # Stage 3: Final Image (Alpine-based runtime) # ----------------------------------------------------------------------------- FROM node:24-alpine # Install runtime packages RUN apk add --no-cache \ ca-certificates \ tzdata \ docker-cli \ docker-compose \ docker-cli-buildx \ sqlite \ postgresql-client \ git \ openssh \ curl \ tini \ su-exec \ libstdc++ # Create docker compose plugin symlink (skip if package already installed it there) RUN mkdir -p /usr/libexec/docker/cli-plugins \ && [ -x /usr/libexec/docker/cli-plugins/docker-compose ] \ || ln -sf /usr/bin/docker-compose /usr/libexec/docker/cli-plugins/docker-compose # Create dockhand user and group RUN addgroup -g 1001 dockhand \ && adduser -u 1001 -G dockhand -h /home/dockhand -D dockhand WORKDIR /app # Set up environment variables ENV SSL_CERT_FILE=/etc/ssl/certs/ca-certificates.crt \ NODE_ENV=production \ PORT=3000 \ HOST=0.0.0.0 \ DATA_DIR=/app/data \ HOME=/home/dockhand \ PUID=1001 \ PGID=1001 \ LD_PRELOAD=/usr/lib/libgetrandom-shim.so # Copy application files with correct ownership COPY --from=app-builder --chown=dockhand:dockhand /app/node_modules ./node_modules COPY --from=app-builder --chown=dockhand:dockhand /app/package.json ./ COPY --from=app-builder --chown=dockhand:dockhand /app/build ./build COPY --from=app-builder --chown=dockhand:dockhand /app/server.js ./ # Copy Go collector binary COPY --from=go-builder --chown=dockhand:dockhand /app/bin/collection-worker ./bin/collection-worker # Copy database migrations COPY --chown=dockhand:dockhand drizzle/ ./drizzle/ COPY --chown=dockhand:dockhand drizzle-pg/ ./drizzle-pg/ # Copy legal documents COPY --chown=dockhand:dockhand LICENSE.txt PRIVACY.txt ./ # Copy getrandom shim for old kernels (Synology DS1513+ with kernel 3.10.x) COPY --from=app-builder /tmp/libgetrandom-shim.so /usr/lib/libgetrandom-shim.so # Copy entrypoint script COPY docker-entrypoint-node.sh /usr/local/bin/docker-entrypoint.sh RUN chmod +x /usr/local/bin/docker-entrypoint.sh # Copy emergency scripts COPY --chown=dockhand:dockhand scripts/emergency/ ./scripts/ RUN chmod +x ./scripts/*.sh ./scripts/**/*.sh 2>/dev/null || true # Create data directories RUN mkdir -p /home/dockhand/.dockhand/stacks /app/data \ && chown dockhand:dockhand /app/data /home/dockhand /home/dockhand/.dockhand /home/dockhand/.dockhand/stacks EXPOSE 3000 HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \ CMD curl -f http://localhost:${PORT:-3000}/ || exit 1 ENTRYPOINT ["/sbin/tini", "--", "/usr/local/bin/docker-entrypoint.sh"] CMD []