# syntax=docker/dockerfile:1.4 # ======================================== # Stage 1: Frontend Application Build # ======================================== FROM node:24.17.0-slim AS frontend-compiler # Production build configuration ENV NODE_ENV=production ENV VITE_BUILD_MEMORY_LIMIT=4096 ENV NODE_OPTIONS="--max-old-space-size=4096" ENV PNPM_HOME="/usr/local/share/pnpm" ENV PATH="$PNPM_HOME/bin:$PNPM_HOME:$PATH" WORKDIR /app/ui # Install build essentials and enable pnpm via corepack RUN apt-get update && apt-get install -y \ ca-certificates \ tzdata \ gcc \ g++ \ make \ git \ && corepack enable # GraphQL schema for code generation COPY ./backend/pkg/graph/schema.graphqls ../backend/pkg/graph/ # Application source code COPY frontend/ . # Install dependencies RUN --mount=type=cache,target=/root/.local/share/pnpm/store \ pnpm install --frozen-lockfile # Generate license report for frontend dependencies RUN pnpm add -g license-checker && \ mkdir -p /licenses/frontend && \ license-checker --production --json > /licenses/frontend/licenses.json && \ license-checker --production --csv > /licenses/frontend/licenses.csv # Build frontend with optimizations and parallel processing RUN pnpm run build -- \ --mode production \ --minify esbuild \ --outDir dist \ --emptyOutDir \ --sourcemap false \ --target es2020 # ======================================== # Stage 2: Backend Services Compilation # ======================================== FROM golang:1.26-bookworm AS api-builder # Version injection arguments # "ce" is not a release number, so GetBinaryVersion reads it as "no release" and reports # the edition alone. The default is a word rather than the empty string because # scripts/check-version-rule.sh requires the builder to stamp something: an empty # PackageVer reaching the binary is the one case it must never mistake for a release. ARG PACKAGE_VER=ce ARG PACKAGE_REV= ARG PACKAGE_EDITION=ce # Static binary compilation settings ENV CGO_ENABLED=0 ENV GO111MODULE=on # Install compilation toolchain and dependencies RUN apt-get update && apt-get install -y \ ca-certificates \ tzdata \ gcc \ g++ \ make \ git \ musl-dev WORKDIR /app/backend COPY backend/ . # Fetch Go module dependencies (cached for faster rebuilds) RUN --mount=type=cache,target=/go/pkg/mod \ go mod download && go mod verify # Install go-licenses tool for license extraction RUN --mount=type=cache,target=/go/pkg/mod \ go install github.com/google/go-licenses@latest # Generate license reports for backend dependencies RUN mkdir -p /licenses/backend && \ go list -m all > /licenses/backend/dependencies.txt && \ GOROOT=$(go env GOROOT) GOTOOLCHAIN=auto go-licenses csv ./cmd/pentagi > /licenses/backend/licenses.csv 2>/dev/null || true # Compile main application binary with embedded version metadata RUN go build -trimpath \ -ldflags "\ -X pentagi/pkg/version.PackageName=pentagi \ -X pentagi/pkg/version.PackageVer=${PACKAGE_VER} \ -X pentagi/pkg/version.PackageRev=${PACKAGE_REV} \ -X pentagi/pkg/version.Edition=${PACKAGE_EDITION}" \ -o /pentagi ./cmd/pentagi # Build ctester utility RUN go build -trimpath \ -ldflags "\ -X pentagi/pkg/version.PackageName=ctester \ -X pentagi/pkg/version.PackageVer=${PACKAGE_VER} \ -X pentagi/pkg/version.PackageRev=${PACKAGE_REV} \ -X pentagi/pkg/version.Edition=${PACKAGE_EDITION}" \ -o /ctester ./cmd/ctester # Build ftester utility RUN go build -trimpath \ -ldflags "\ -X pentagi/pkg/version.PackageName=ftester \ -X pentagi/pkg/version.PackageVer=${PACKAGE_VER} \ -X pentagi/pkg/version.PackageRev=${PACKAGE_REV} \ -X pentagi/pkg/version.Edition=${PACKAGE_EDITION}" \ -o /ftester ./cmd/ftester # Build etester utility RUN go build -trimpath \ -ldflags "\ -X pentagi/pkg/version.PackageName=etester \ -X pentagi/pkg/version.PackageVer=${PACKAGE_VER} \ -X pentagi/pkg/version.PackageRev=${PACKAGE_REV} \ -X pentagi/pkg/version.Edition=${PACKAGE_EDITION}" \ -o /etester ./cmd/etester # ======================================== # Stage 3: Production Runtime Environment # ======================================== FROM alpine:3.23.5 # Establish non-privileged execution context with docker socket access RUN addgroup -g 998 docker && \ addgroup -S pentagi && \ adduser -S pentagi -G pentagi && \ addgroup pentagi docker # Install required packages RUN apk --no-cache add ca-certificates openssl openssh-keygen shadow ADD scripts/entrypoint.sh /opt/pentagi/bin/ RUN sed -i 's/\r//' /opt/pentagi/bin/entrypoint.sh && \ chmod +x /opt/pentagi/bin/entrypoint.sh RUN mkdir -p \ /root/.ollama \ /opt/pentagi/bin \ /opt/pentagi/ssl \ /opt/pentagi/fe \ /opt/pentagi/logs \ /opt/pentagi/data \ /opt/pentagi/conf && \ chmod 777 /root/.ollama COPY --from=api-builder /pentagi /opt/pentagi/bin/pentagi COPY --from=api-builder /ctester /opt/pentagi/bin/ctester COPY --from=api-builder /ftester /opt/pentagi/bin/ftester COPY --from=api-builder /etester /opt/pentagi/bin/etester COPY --from=frontend-compiler /app/ui/dist /opt/pentagi/fe COPY --from=api-builder /licenses/backend /opt/pentagi/licenses/backend COPY --from=frontend-compiler /licenses/frontend /opt/pentagi/licenses/frontend # Copy provider configuration files COPY examples/configs/atlas.provider.yml /opt/pentagi/conf/ COPY examples/configs/azure-openai.provider.yml /opt/pentagi/conf/ COPY examples/configs/bedrock-glm-flash.provider.yml /opt/pentagi/conf/ COPY examples/configs/custom-openai.provider.yml /opt/pentagi/conf/ COPY examples/configs/deepinfra.provider.yml /opt/pentagi/conf/ COPY examples/configs/deepseek.provider.yml /opt/pentagi/conf/ COPY examples/configs/hcnsec.provider.yml /opt/pentagi/conf/ COPY examples/configs/moonshot.provider.yml /opt/pentagi/conf/ COPY examples/configs/novita.provider.yml /opt/pentagi/conf/ COPY examples/configs/nvidia-glm-5.1.provider.yml /opt/pentagi/conf/ COPY examples/configs/ollama-cloud.provider.yml /opt/pentagi/conf/ COPY examples/configs/ollama-llama318b-instruct.provider.yml /opt/pentagi/conf/ COPY examples/configs/ollama-llama318b.provider.yml /opt/pentagi/conf/ COPY examples/configs/ollama-qwen332b-fp16-tc.provider.yml /opt/pentagi/conf/ COPY examples/configs/ollama-qwq32b-fp16-tc.provider.yml /opt/pentagi/conf/ COPY examples/configs/opencode.provider.yml /opt/pentagi/conf/ COPY examples/configs/openrouter.provider.yml /opt/pentagi/conf/ COPY examples/configs/orcarouter.provider.yml /opt/pentagi/conf/ COPY examples/configs/vllm-mixed.provider.yml /opt/pentagi/conf/ COPY examples/configs/vllm-qwen3.5-27b-fp8-no-think.provider.yml /opt/pentagi/conf/ COPY examples/configs/vllm-qwen3.5-27b-fp8.provider.yml /opt/pentagi/conf/ COPY examples/configs/vllm-qwen3.6-27b-fp8-no-think.provider.yml /opt/pentagi/conf/ COPY examples/configs/vllm-qwen3.6-27b-fp8.provider.yml /opt/pentagi/conf/ COPY examples/configs/vllm-qwen3.8-27b-fp8-no-think.provider.yml /opt/pentagi/conf/ COPY examples/configs/vllm-qwen3.8-27b-fp8.provider.yml /opt/pentagi/conf/ COPY examples/configs/vllm-qwen3.6-35b-a3b-fp8-no-think.provider.yml /opt/pentagi/conf/ COPY examples/configs/vllm-qwen3.6-35b-a3b-fp8.provider.yml /opt/pentagi/conf/ COPY examples/configs/vllm-qwen332b-fp16.provider.yml /opt/pentagi/conf/ COPY examples/configs/xai.provider.yml /opt/pentagi/conf/ COPY LICENSE /opt/pentagi/LICENSE COPY NOTICE /opt/pentagi/NOTICE COPY EULA.md /opt/pentagi/EULA COPY EULA.md /opt/pentagi/fe/EULA.md RUN chown -R pentagi:pentagi /opt/pentagi WORKDIR /opt/pentagi USER pentagi ENTRYPOINT ["/opt/pentagi/bin/entrypoint.sh", "/opt/pentagi/bin/pentagi"] # Mirrors GetBinaryVersion in backend/pkg/version: "", ".h", # "-" or "-.h". The "h" keeps the revision an # alphanumeric semver identifier. The binary is stamped from the same args in # the builder stage, so pass them and nothing else: PACKAGE_VERSION_FULL is where this # expression puts its result, and overriding it labels the image with a string the binary # never says. # # PACKAGE_VER is a release number. The words "develop", "ce" and "ee" are not valid values # for it: an ARG default can branch on empty but cannot compare strings, so this expression # would read one as a release and label the image ce-ce where the binary reports ce. Leave # PACKAGE_VER empty for a build with no release behind it — scripts/version.sh and # scripts/version.ps1 clear a tag of that name, the CI jobs do not, and # scripts/check-version-rule.sh measures the agreement for a release tag or no tag. ARG PACKAGE_VER ARG PACKAGE_REV ARG PACKAGE_EDITION=ce ARG VER_PART=${PACKAGE_VER:+${PACKAGE_VER}-}${PACKAGE_EDITION} ARG PACKAGE_VERSION_FULL=${VER_PART}${PACKAGE_REV:+.h${PACKAGE_REV}} LABEL com.pentagi.version="${PACKAGE_VERSION_FULL}" # Image Metadata LABEL org.opencontainers.image.source="https://github.com/vxcontrol/pentagi" LABEL org.opencontainers.image.description="Fully autonomous AI Agents system capable of performing complex penetration testing tasks" LABEL org.opencontainers.image.authors="PentAGI Development Team" LABEL org.opencontainers.image.licenses="MIT License"