# Optimized Dockerfile - builds database at build time, minimal runtime image # Stage 1: Dependencies (includes n8n for building) FROM node:20-alpine AS deps WORKDIR /app COPY package*.json ./ # Configure npm for reliability RUN npm config set fetch-retries 5 && \ npm config set fetch-retry-mintimeout 20000 && \ npm config set fetch-retry-maxtimeout 120000 && \ npm config set fetch-timeout 300000 # Install all dependencies including n8n packages RUN npm ci # Stage 2: Builder (compiles TypeScript) FROM node:20-alpine AS builder WORKDIR /app COPY package*.json ./ COPY --from=deps /app/node_modules ./node_modules COPY . . # Build TypeScript RUN npm run build # Stage 3: Database Builder (extracts all node info and builds database) FROM builder AS db-builder WORKDIR /app # Clone n8n-docs for documentation (if available) RUN apk add --no-cache git && \ git clone https://github.com/n8n-io/n8n-docs.git /tmp/n8n-docs || true ENV N8N_DOCS_PATH=/tmp/n8n-docs # Build the complete database with source code RUN mkdir -p data && \ node dist/scripts/rebuild-optimized.js # Stage 4: Minimal Runtime (no n8n packages) FROM node:20-alpine AS runtime WORKDIR /app # Install only essential runtime tools RUN apk add --no-cache curl && \ rm -rf /var/cache/apk/* # Create package.json with only runtime dependencies RUN echo '{ \ "name": "n8n-mcp-runtime", \ "version": "1.0.0", \ "private": true, \ "dependencies": { \ "@modelcontextprotocol/sdk": "^1.12.1", \ "better-sqlite3": "^11.10.0", \ "sql.js": "^1.13.0", \ "express": "^5.1.0", \ "dotenv": "^16.5.0" \ } \ }' > package.json # Install only runtime dependencies RUN npm config set fetch-retries 5 && \ npm config set fetch-retry-mintimeout 20000 && \ npm install --production --no-audit --no-fund # Copy built application COPY --from=builder /app/dist ./dist # Copy pre-built database with all source code COPY --from=db-builder /app/data/nodes.db ./data/ # Copy minimal required files COPY src/database/schema-optimized.sql ./src/database/ COPY .env.example ./ # Add container labels LABEL org.opencontainers.image.source="https://github.com/czlonkowski/n8n-mcp" LABEL org.opencontainers.image.description="n8n MCP Server - Optimized Version" LABEL org.opencontainers.image.licenses="Sustainable-Use-1.0" LABEL org.opencontainers.image.title="n8n-mcp-optimized" # Create non-root user RUN addgroup -g 1001 -S nodejs && \ adduser -S nodejs -u 1001 && \ chown -R nodejs:nodejs /app # Create optimized entrypoint RUN echo '#!/bin/sh\n\ set -e\n\ \n\ # Validate AUTH_TOKEN in HTTP mode\n\ if [ "$MCP_MODE" = "http" ] && [ -z "$AUTH_TOKEN" ]; then\n\ echo "ERROR: AUTH_TOKEN is required when running in HTTP mode"\n\ exit 1\n\ fi\n\ \n\ # Check if database exists\n\ if [ ! -f "/app/data/nodes.db" ]; then\n\ echo "ERROR: Pre-built database not found at /app/data/nodes.db"\n\ echo "This optimized image requires the database to be built at Docker build time"\n\ exit 1\n\ fi\n\ \n\ # Validate database\n\ node -e "\n\ const Database = require(\"better-sqlite3\");\n\ try {\n\ const db = new Database(\"/app/data/nodes.db\", { readonly: true });\n\ const count = db.prepare(\"SELECT COUNT(*) as count FROM nodes\").get();\n\ console.log(\"Database validated: \" + count.count + \" nodes found\");\n\ db.close();\n\ } catch (error) {\n\ console.error(\"Database validation failed:\", error);\n\ process.exit(1);\n\ }\n\ "\n\ \n\ # Start the application\n\ exec "$@"\n\ ' > /usr/local/bin/docker-entrypoint.sh && \ chmod +x /usr/local/bin/docker-entrypoint.sh # Switch to non-root user USER nodejs # Expose HTTP port EXPOSE 3000 # Health check HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \ CMD curl -f http://127.0.0.1:3000/health || exit 1 # Optimized entrypoint ENTRYPOINT ["/usr/local/bin/docker-entrypoint.sh"] CMD ["node", "dist/mcp/index.js"]