# Multi-stage build: install deps + compile TypeScript in the builder, then # copy only what's needed for runtime. Final image is node:22-alpine with the # compiled dist/, src/public/ (pico.css + htmx are vendored), and production # dependencies including the better-sqlite3 native binding. FROM node:22-alpine AS builder WORKDIR /app # Native deps for better-sqlite3 + sharp RUN apk add --no-cache python3 make g++ vips-dev COPY package.json package-lock.json* ./ RUN npm install --include=dev COPY tsconfig.json tsconfig.build.json ./ COPY src ./src RUN npm run build # --------------------------------------------------------------------------- FROM node:22-alpine AS runtime WORKDIR /app # Runtime deps for sharp (libvips). better-sqlite3 doesn't need any. RUN apk add --no-cache vips ENV NODE_ENV=production ENV GS_DB_PATH=/data/ghoststandard.sqlite COPY package.json package-lock.json* ./ RUN npm install --omit=dev && npm cache clean --force COPY --from=builder /app/dist ./dist # Make both `gs` and `ghoststandard` available on $PATH so # `docker compose exec app gs ` Just Works. RUN chmod +x dist/cli.js \ && ln -s /app/dist/cli.js /usr/local/bin/gs \ && ln -s /app/dist/cli.js /usr/local/bin/ghoststandard VOLUME ["/data"] EXPOSE 8080 CMD ["node", "dist/server.js"]