# syntax=docker/dockerfile:1.7 # # SCOPENET admin panel — a single static binary + the web UI on `scratch`. # Multi-arch (amd64/arm64) is cross-compiled with cargo-zigbuild on the # build machine's native arch, so no slow QEMU emulation is needed. # ---- 1. Web UI --------------------------------------------------------------- FROM --platform=$BUILDPLATFORM node:22-alpine AS web WORKDIR /src/panel/web COPY panel/web/package.json panel/web/package-lock.json ./ RUN npm ci --no-audit --no-fund COPY panel/web/ ./ RUN npm run build # ---- 2. Server binary -------------------------------------------------------- FROM --platform=$BUILDPLATFORM rust:1-slim-bookworm AS build ARG TARGETARCH # zig (via the cargo-zigbuild wheel) is the cross C toolchain for musl targets. RUN apt-get update && apt-get install -y --no-install-recommends python3-pip \ && rm -rf /var/lib/apt/lists/* \ && pip install --no-cache-dir --break-system-packages cargo-zigbuild ziglang RUN case "${TARGETARCH:-$(uname -m)}" in \ amd64|x86_64) echo x86_64-unknown-linux-musl ;; \ arm64|aarch64) echo aarch64-unknown-linux-musl ;; \ *) echo "unsupported arch ${TARGETARCH:-$(uname -m)}" >&2; exit 1 ;; \ esac > /rust-target \ && rustup target add "$(cat /rust-target)" WORKDIR /src COPY . . # No --locked: release builds stamp the version into Cargo.toml. Dependencies # stay pinned by Cargo.lock either way (CI enforces --locked). RUN --mount=type=cache,target=/usr/local/cargo/registry,id=cargo-registry,sharing=locked \ --mount=type=cache,target=/src/target,id=scopenet-target-$TARGETARCH \ cargo zigbuild --release -p scopenet-panel --target "$(cat /rust-target)" \ && cp "target/$(cat /rust-target)/release/scopenet-panel" /scopenet-panel # Data dir owned by the non-root runtime user. RUN mkdir -p /out/data && chown 65532:65532 /out/data # ---- 2b. Vantage generator (optional) ---------------------------------------- # The live map renders with Vantage. Point VANTAGE_URL at a static Linux # release archive (tar.gz or zip) for the target arch to bake it into the image; # leave it empty to mount your own binary at /opt/vantage/vantage instead. FROM --platform=$BUILDPLATFORM alpine:3 AS vantage ARG VANTAGE_URL="" RUN apk add --no-cache curl tar unzip && mkdir -p /out/vantage /out/assets \ && if [ -n "$VANTAGE_URL" ]; then \ curl -fsSL "$VANTAGE_URL" -o /tmp/vantage.pkg \ && (tar -xzf /tmp/vantage.pkg -C /out/vantage 2>/dev/null || unzip -q /tmp/vantage.pkg -d /out/vantage) \ && find /out/vantage -type f -name vantage -exec chmod 755 {} + \ && (f=$(find /out/vantage -type f -name vantage | head -n1) && [ -n "$f" ] && [ "$f" != /out/vantage/vantage ] && mv "$f" /out/vantage/vantage || true); \ fi # ---- 3. Runtime -------------------------------------------------------------- FROM scratch LABEL org.opencontainers.image.title="SCOPENET Panel" \ org.opencontainers.image.description="Admin panel for the SCOPENET Minecraft launcher" \ org.opencontainers.image.source="https://github.com/scopeddlol/SCOPENET-MC" COPY --from=build /scopenet-panel /scopenet-panel COPY --from=web /src/panel/web/dist /web COPY --from=build --chown=65532:65532 /out/data /data COPY --from=vantage /out/vantage /opt/vantage ENV SCOPENET_BIND=0.0.0.0:8080 \ SCOPENET_DATA_DIR=/data \ SCOPENET_WEB_DIR=/web \ SCOPENET_VANTAGE_BIN=/opt/vantage/vantage \ SCOPENET_VANTAGE_ASSETS=/data/livemap/assets \ RUST_LOG=info USER 65532:65532 VOLUME ["/data"] EXPOSE 8080 HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 CMD ["/scopenet-panel", "healthcheck"] ENTRYPOINT ["/scopenet-panel"]