# syntax=docker/dockerfile:1.7 # Multi-stage image for xopc gateway and CLI. # The build stage uses full Debian for native packages; runtime uses slim and runs as non-root. ARG XOPC_NODE_IMAGE=docker.io/library/node:26.12.1-bookworm ARG XOPC_NODE_SLIM_IMAGE=docker.io/library/node:26.12.1-bookworm-slim ARG XOPC_PNPM_VERSION=11.9.0 FROM ${XOPC_NODE_IMAGE} AS build ARG XOPC_PNPM_VERSION ENV CI=true \ NODE_ENV=development \ ELECTRON_SKIP_BINARY_DOWNLOAD=1 \ PNPM_HOME=/pnpm \ PATH=/pnpm:$PATH WORKDIR /app RUN npm install --global "pnpm@${XOPC_PNPM_VERSION}" # Persist fetched packages in a normal layer, including on ephemeral CI builders. COPY pnpm-lock.yaml pnpm-workspace.yaml .npmrc ./ COPY patches ./patches COPY web/vendor ./web/vendor RUN pnpm fetch --config.store-dir=/pnpm/store \ --config.supportedArchitectures.os=linux \ --config.supportedArchitectures.cpu="$(node -p 'process.arch')" \ --config.supportedArchitectures.libc=glibc COPY . . RUN pnpm install --offline --frozen-lockfile \ --config.store-dir=/pnpm/store \ --config.supportedArchitectures.os=linux \ --config.supportedArchitectures.cpu="$(node -p 'process.arch')" \ --config.supportedArchitectures.libc=glibc RUN NODE_OPTIONS=--max-old-space-size=4096 pnpm run build:ci FROM build AS runtime-assets ARG XOPC_INSTALL_BROWSER="" ENV CI=true \ NODE_ENV=production \ PNPM_HOME=/pnpm \ PATH=/pnpm:$PATH RUN if [ -n "$XOPC_INSTALL_BROWSER" ]; then \ cp -LR node_modules/playwright-core /tmp/xopc-playwright-core; \ fi && \ rm -rf node_modules .pnpm && \ find extensions packages apps evals web -type d -name node_modules -prune -exec rm -rf '{}' + && \ pnpm install --offline --prod --frozen-lockfile \ --config.store-dir=/pnpm/store \ --config.supportedArchitectures.os=linux \ --config.supportedArchitectures.cpu="$(node -p 'process.arch')" \ --config.supportedArchitectures.libc=glibc && \ if [ -n "$XOPC_INSTALL_BROWSER" ]; then \ mkdir -p node_modules/playwright-core && \ cp -R /tmp/xopc-playwright-core/. node_modules/playwright-core/; \ fi && \ find dist -type f \( -name '*.map' -o -name '*.tsbuildinfo' \) -delete FROM ${XOPC_NODE_SLIM_IMAGE} AS runtime ARG XOPC_PNPM_VERSION ARG XOPC_APT_MIRROR="" LABEL org.opencontainers.image.source="https://github.com/xopcai/xopc" \ org.opencontainers.image.url="https://github.com/xopcai/xopc" \ org.opencontainers.image.licenses="MIT" \ org.opencontainers.image.title="xopc" \ org.opencontainers.image.description="xopc gateway and CLI runtime container image" ENV NODE_ENV=production \ HOME=/home/node \ XOPC_HOME=/home/node \ XOPC_STATE_DIR=/home/node/.xopc \ XOPC_CONFIG_PATH=/home/node/.xopc/xopc.json \ XOPC_WORKSPACE=/home/node/.xopc/workspace \ XOPC_LOG_DIR=/home/node/.xopc/logs \ TERM=xterm-256color WORKDIR /app RUN --mount=type=cache,id=xopc-bookworm-apt-cache,target=/var/cache/apt,sharing=locked \ --mount=type=cache,id=xopc-bookworm-apt-lists,target=/var/lib/apt,sharing=locked \ if [ -n "$XOPC_APT_MIRROR" ]; then \ sed -i "s|http://deb.debian.org/debian|$XOPC_APT_MIRROR|g; s|http://deb.debian.org/debian-security|$XOPC_APT_MIRROR-security|g" /etc/apt/sources.list.d/debian.sources; \ fi && \ apt-get -o Acquire::Retries=5 -o Acquire::http::Timeout=30 -o Acquire::https::Timeout=30 update && \ DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \ ca-certificates curl ffmpeg git hostname lsof openssl procps python3 tini && \ update-ca-certificates && \ rm -rf /var/lib/apt/lists/* COPY --from=runtime-assets --chown=node:node /app/dist ./dist COPY --from=runtime-assets --chown=node:node /app/.pnpm ./.pnpm COPY --from=runtime-assets --chown=node:node /app/node_modules ./node_modules COPY --from=runtime-assets --chown=node:node /app/package.json ./package.json COPY --from=runtime-assets --chown=node:node /app/pnpm-workspace.yaml ./pnpm-workspace.yaml COPY --from=runtime-assets --chown=node:node /app/skills ./skills COPY --from=runtime-assets --chown=node:node /app/docs ./docs COPY --from=runtime-assets --chown=node:node /app/extensions ./extensions COPY --from=runtime-assets --chown=node:node /app/packages ./packages RUN npm install --global "pnpm@${XOPC_PNPM_VERSION}" && \ ln -sf /app/dist/src/cli/bin.js /usr/local/bin/xopc && \ chmod 755 /app/dist/src/cli/bin.js && \ install -d -m 0700 -o node -g node \ /home/node/.xopc \ /home/node/.xopc/workspace \ /home/node/.xopc/logs \ /home/node/.config/xopc # Install additional system packages for skills/extensions when needed. # Example: docker build --build-arg XOPC_IMAGE_APT_PACKAGES="python3-pip wget" . ARG XOPC_IMAGE_APT_PACKAGES="" RUN --mount=type=cache,id=xopc-bookworm-apt-cache,target=/var/cache/apt,sharing=locked \ --mount=type=cache,id=xopc-bookworm-apt-lists,target=/var/lib/apt,sharing=locked \ if [ -n "$XOPC_IMAGE_APT_PACKAGES" ]; then \ apt-get -o Acquire::Retries=5 -o Acquire::http::Timeout=30 -o Acquire::https::Timeout=30 update && \ DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends $XOPC_IMAGE_APT_PACKAGES && \ rm -rf /var/lib/apt/lists/*; \ fi # Optionally preinstall Chromium for browser automation. # Example: docker build --build-arg XOPC_INSTALL_BROWSER=1 . ARG XOPC_INSTALL_BROWSER="" ENV PLAYWRIGHT_BROWSERS_PATH=/home/node/.cache/ms-playwright RUN --mount=type=cache,id=xopc-bookworm-apt-cache,target=/var/cache/apt,sharing=locked \ --mount=type=cache,id=xopc-bookworm-apt-lists,target=/var/lib/apt,sharing=locked \ if [ -n "$XOPC_INSTALL_BROWSER" ]; then \ apt-get -o Acquire::Retries=5 -o Acquire::http::Timeout=30 -o Acquire::https::Timeout=30 update && \ DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends xvfb && \ node /app/node_modules/playwright-core/cli.js install --with-deps chromium && \ chown -R node:node "$PLAYWRIGHT_BROWSERS_PATH" && \ rm -rf /var/lib/apt/lists/*; \ fi USER node EXPOSE 18790 HEALTHCHECK --interval=30s --timeout=5s --start-period=20s --retries=5 \ CMD node -e "fetch('http://127.0.0.1:18790/api/health').then((response)=>process.exit(response.ok?0:1)).catch(()=>process.exit(1))" ENTRYPOINT ["tini", "-s", "--"] CMD ["node", "dist/src/cli/bin.js", "gateway", "--bind", "lan", "--port", "18790"]