# Multi-stage build: compile the wasm client + the server in one Rust # image, then copy the artifacts into a tiny Debian runtime. FROM rust:1-bookworm AS builder # Pin wasm-bindgen-cli to the same version we use during development. If # this number drifts from Cargo.lock the wasm load will fail. ARG WASM_BINDGEN_VERSION=0.2.122 # Install the wasm32 target and wasm-bindgen-cli. RUN rustup target add wasm32-unknown-unknown && \ cargo install wasm-bindgen-cli --version ${WASM_BINDGEN_VERSION} --locked WORKDIR /build # Copy the whole project. The .dockerignore keeps target/ out so we get # a clean release build inside the container. COPY . . # Build the wasm client (release) and run wasm-bindgen to emit the # JS glue + bg.wasm into web/. RUN cargo build --target wasm32-unknown-unknown --release --lib && \ wasm-bindgen --target web --out-dir web --no-typescript \ target/wasm32-unknown-unknown/release/voxel_game.wasm # Build the multiplayer server. RUN cd server && cargo build --release # ---- Runtime image ---- FROM debian:bookworm-slim AS runtime # ca-certificates lets the server speak HTTPS if it ever needs to (it # doesn't yet, but it's tiny and avoids surprises). tini handles signal # forwarding so `docker stop` is clean. RUN apt-get update && \ apt-get install -y --no-install-recommends ca-certificates tini && \ rm -rf /var/lib/apt/lists/* WORKDIR /app COPY --from=builder /build/server/target/release/voxel-server /usr/local/bin/voxel-server COPY --from=builder /build/web /app/web ENV STATIC_DIR=/app/web ENV PORT=8080 EXPOSE 8080 # Run as non-root for safety. RUN useradd --create-home --shell /bin/false --uid 10001 voxel && \ chown -R voxel:voxel /app USER voxel ENTRYPOINT ["/usr/bin/tini", "--"] CMD ["/usr/local/bin/voxel-server"]