# DockerfileExtra - Combined Windmill Extra Services # # This image extends windmill-slim with three optional Windmill services: # - LSP (Language Server Protocol) - Port 3001 # - Multiplayer (y-websocket) - Port 3002 # - Debugger (DAP WebSocket) - Port 3003 # # Each service can be enabled/disabled via environment variables: # - ENABLE_LSP=true (default: true) # - ENABLE_MULTIPLAYER=true (default: true) # - ENABLE_DEBUGGER=true (default: true) # # Build: # docker build -f docker/DockerfileExtra -t windmill-extra . # # Run: # docker run -p 3001:3001 -p 3002:3002 -p 3003:3003 windmill-extra # ============================================================================ # Build final extra services image from windmill-ee-slim (includes nsjail) # ============================================================================ FROM ghcr.io/windmill-labs/windmill-ee-slim:latest AS final ARG APP=/usr/src/app # Install Node.js 22 (needed for LSP and multiplayer) RUN apt-get update && apt-get install -y --no-install-recommends \ gnupg \ && mkdir -p /etc/apt/keyrings \ && curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg \ && echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_22.x nodistro main" > /etc/apt/sources.list.d/nodesource.list \ && apt-get update \ && apt-get install -y nodejs \ && rm -rf /var/lib/apt/lists/* # Install additional system dependencies # - shellcheck: for bash LSP RUN apt-get update && apt-get install -y --no-install-recommends \ shellcheck \ && apt-get clean \ && rm -rf /var/lib/apt/lists/* # Install Go for gopls (Go LSP) RUN set -eux; \ arch="$(dpkg --print-architecture)"; arch="${arch##*-}"; \ case "$arch" in \ 'amd64') targz='go1.26.0.linux-amd64.tar.gz' ;; \ 'arm64') targz='go1.26.0.linux-arm64.tar.gz' ;; \ 'armhf') targz='go1.26.0.linux-armv6l.tar.gz' ;; \ *) echo >&2 "error: unsupported architecture '$arch'"; exit 1 ;; \ esac; \ wget "https://golang.org/dl/$targz" -nv && tar -C /usr/local -xzf "$targz" && rm "$targz" ENV PATH="${PATH}:/usr/local/go/bin" ENV GOBIN=/usr/local/go/bin # Install gopls for Go LSP RUN /usr/local/go/bin/go install -v golang.org/x/tools/gopls@latest # Copy Deno for Deno LSP COPY --from=denoland/deno:2.2.1 --chmod=755 /usr/bin/deno /usr/bin/deno # ============================================================================ # LSP Setup # ============================================================================ ENV PIPENV_VENV_IN_PROJECT=1 ENV XDG_CACHE_HOME=/pyls/.cache # Install Python packages for LSP using uv RUN uv pip install --system --break-system-packages pipenv tornado python-lsp-jsonrpc ruff==0.16.0 Cython # Install Node-based language servers RUN npm install -g diagnostic-languageserver pyright # Setup LSP working directory WORKDIR /pyls COPY lsp/Pipfile . RUN pipenv install COPY lsp/pyls_launcher.py . # Setup Monaco temp directory for LSP RUN mkdir -p /tmp/monaco && chmod -R 777 /tmp/monaco RUN cd /tmp/monaco && npm install --save-dev windmill-client RUN mkdir -p /pyls/.cache # ============================================================================ # Debugger Setup # ============================================================================ WORKDIR /debugger # Copy debugger files COPY debugger/dap_debug_service.ts . COPY debugger/dap_websocket_server_bun.ts . COPY debugger/env_passthrough.ts . COPY debugger/dap_websocket_server.py . COPY debugger/registry_config.ts . COPY debugger/nsjail.debug.config.proto . # Install Python debugger dependencies using uv RUN uv pip install --system --break-system-packages websockets debugpy # ============================================================================ # Multiplayer Setup (y-websocket with connection logging) # ============================================================================ WORKDIR /multiplayer # Copy multiplayer server files COPY multiplayer/package.json . COPY multiplayer/server.mjs . COPY multiplayer/gateway.mjs . # Install dependencies RUN npm install # ============================================================================ # Entrypoint Setup # ============================================================================ WORKDIR /app # Copy entrypoint script COPY docker/entrypoint-extra.sh /entrypoint.sh RUN chmod +x /entrypoint.sh # Non-root 'windmill' user with UID/GID 1000 to match the app image, so # `runAsUser: 1000` resolves to a real account with a writable $HOME. # No USER directive: the image still starts as root by default. RUN addgroup --gid 1000 windmill && \ adduser --disabled-password --gecos "" --uid 1000 --gid 1000 windmill # The root-run installs above write into the base image's UV_CACHE_DIR # (/tmp/windmill/cache/uv) after the base already made it world-writable, leaving # root-owned 0755 dirs that make uv fail EACCES for a non-root UID. /pyls/.cache # (XDG_CACHE_HOME, incl. DENO_DIR) and /tmp/monaco are written at runtime too. RUN chmod -R a+rX /usr/local /pyls /debugger /multiplayer /tmp/monaco && \ chmod -R a+rw /tmp/windmill /pyls/.cache && \ find /tmp/windmill /pyls/.cache /tmp/monaco -type d -exec chmod 777 {} + # Expose all service ports EXPOSE 3000 3001 3002 3003 # Environment variables for service control ENV ENABLE_LSP=true ENV ENABLE_MULTIPLAYER=true ENV ENABLE_DEBUGGER=true # nsjail sandboxing for debugger (requires --privileged, off by default) ENV ENABLE_GATEWAY=true ENV GATEWAY_PORT=3000 ENV ENABLE_NSJAIL=false # LSP port ENV LSP_PORT=3001 # Multiplayer port and host ENV MULTIPLAYER_PORT=3002 ENV HOST=0.0.0.0 # Debugger port ENV DEBUGGER_PORT=3003 # Windmill base URL for debugger token verification ENV WINDMILL_BASE_URL="" ENTRYPOINT ["/entrypoint.sh"]