# multi-stage build: rust backend + node frontend -> slim runtime image # --- stage 1: build frontend --- FROM node:22-alpine AS frontend WORKDIR /app/frontend COPY frontend/package*.json ./ RUN npm ci --legacy-peer-deps COPY frontend/ ./ RUN npm run build # --- stage 2: build backend --- FROM rust:1.85-bookworm AS backend WORKDIR /app COPY Cargo.toml Cargo.lock ./ COPY cruciverb-core/ cruciverb-core/ COPY cruciverb-cli/ cruciverb-cli/ COPY cruciverb-server/ cruciverb-server/ COPY data/banned_words.txt data/banned_words.txt RUN cargo build --release -p cruciverb-server # --- stage 3: runtime --- FROM debian:bookworm-slim RUN apt-get update && apt-get install -y --no-install-recommends \ ca-certificates curl nodejs npm \ build-essential libcairo2-dev libpango1.0-dev libjpeg-dev libgif-dev librsvg2-dev \ && rm -rf /var/lib/apt/lists/* WORKDIR /app # copy the compiled binary COPY --from=backend /app/target/release/cruciverb-server /app/cruciverb-server # copy the frontend build COPY --from=frontend /app/frontend/dist /app/static # copy data files (wordlists, themes, clue databases, definitions) COPY data/ /app/data-default/ # daily image generation script (uses node-canvas, no browser needed) COPY scripts/generate-daily-image.js /app/scripts/generate-daily-image.js RUN cd /app/scripts && npm init -y && npm install canvas # entrypoint script to copy default data on first run RUN mkdir -p /app/data RUN printf '#!/bin/sh\n\ if [ ! -f /app/data/wordlists/enable.txt ]; then\n\ echo "first run - copying default data..."\n\ cp -rn /app/data-default/* /app/data/ 2>/dev/null || true\n\ fi\n\ exec /app/cruciverb-server\n' > /app/entrypoint.sh && chmod +x /app/entrypoint.sh ENV PORT=8080 ENV DATA_DIR=/app/data ENV STATIC_DIR=/app/static ENV RUST_LOG=info ENV INSTANCE_URL=http://localhost:8080 EXPOSE 8080 VOLUME ["/app/data"] CMD ["/app/entrypoint.sh"]