# syntax=docker/dockerfile:1 # Spendlight: one image, one data folder. Builds for whatever architecture # the Docker host runs (arm64 on the Pi, amd64 elsewhere). FROM node:24-bookworm-slim AS base WORKDIR /app ENV npm_config_update_notifier=false \ npm_config_fund=false \ npm_config_audit=false # All dependencies, for building FROM base AS deps COPY package.json package-lock.json .npmrc ./ COPY shared/package.json shared/ COPY server/package.json server/ COPY web/package.json web/ RUN npm ci FROM deps AS build ARG APP_VERSION=dev ENV VITE_APP_VERSION=${APP_VERSION} COPY . . RUN npm run build # Only what the server needs at runtime FROM base AS prod-deps COPY package.json package-lock.json .npmrc ./ COPY shared/package.json shared/ COPY server/package.json server/ COPY web/package.json web/ RUN npm ci --omit=dev --workspace server FROM base AS runtime ARG APP_VERSION=dev ENV NODE_ENV=production \ APP_VERSION=${APP_VERSION} \ PORT=3000 \ DATA_DIR=/data \ BACKUP_DIR=/backups \ WEB_DIST=/app/web/dist COPY --from=prod-deps /app/node_modules ./node_modules COPY --from=build /app/server/dist ./server/dist COPY --from=build /app/server/drizzle ./server/drizzle COPY --from=build /app/web/dist ./web/dist COPY deploy/spendlight-cli.sh /usr/local/bin/spendlight RUN chmod 755 /usr/local/bin/spendlight \ && mkdir -p /data /backups \ && chown node:node /data /backups USER node EXPOSE 3000 VOLUME ["/data", "/backups"] HEALTHCHECK --interval=30s --timeout=5s --start-period=20s --retries=3 \ CMD node -e "fetch('http://127.0.0.1:3000/api/v1/health').then(r=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))" LABEL org.opencontainers.image.title="Spendlight" \ org.opencontainers.image.description="Private, self-hosted household budgeting" \ org.opencontainers.image.version="${APP_VERSION}" CMD ["node", "server/dist/index.js"]