History

Optimize self-host Docker image size

Reduce the self-host runtime image by splitting production deps into a parallel stage, using a distroless cc runtime with a copied Bun binary, moving web-only host dependencies to devDependencies, and copying/pruning only runtime-needed files. Verification: DOCKER_BUILDKIT=1 docker build --no-cache --progress=plain -f apps/host-selfhost/Dockerfile -t executor-selfhost-pr:latest . docker run smoke: /api/health returned OK

Rhys Sullivan committed 4 months ago

Commit 01a6d751 parent d5b7c462 files · +132 −19Browse files

Unified · Split

2 files
  1. apps/host-selfhost/Dockerfile +124 −11
  2. apps/host-selfhost/package.json +8 −8
apps/host-selfhost/Dockerfile+124−11
@@ -12,25 +12,122 @@ · 11 unmodified lines
12# SQLite (libSQL, file:/data/...) lives in /data, QuickJS + MCP run in-process —12# SQLite (libSQL, file:/data/...) lives in /data, QuickJS + MCP run in-process —
13# so there's no postgres/worker/proxy to orchestrate.13# so there's no postgres/worker/proxy to orchestrate.
14 14
15# ── Production deps: installs only what the runtime image needs ─────────────
16FROM oven/bun:1 AS prod-deps
17WORKDIR /app
18COPY . .
19# This stage is independent from the SPA build stage, so BuildKit can run the
20# slow production install in parallel with the dev install and Vite build.
21# --ignore-scripts: the root `prepare` hook runs a dev-only tool
22# (effect-language-service); runtime deps are prebuilt JS with no postinstall.
23RUN bun install --frozen-lockfile --production --ignore-scripts --filter @executor-js/host-selfhost \
24 && find node_modules/.bun -maxdepth 1 -name '*linux-x64-musl*' -exec rm -rf {} + \
25 && rm -rf \
26 node_modules/.bun/@ant-design+* \
27 node_modules/.bun/@base-ui+* \
28 node_modules/.bun/@cloudflare+workers-types@* \
29 node_modules/.bun/@electric-sql+pglite@* \
30 node_modules/.bun/@emotion+react@* \
31 node_modules/.bun/@emoji-mart+* \
32 node_modules/.bun/@esbuild+* \
33 node_modules/.bun/@floating-ui+react@* \
34 node_modules/.bun/@dnd-kit+* \
35 node_modules/.bun/@hookform+* \
36 node_modules/.bun/@lobehub+* \
37 node_modules/.bun/@mermaid-js+* \
38 node_modules/.bun/@pierre+diffs@* \
39 node_modules/.bun/@pierre+theme@* \
40 node_modules/.bun/@primer+octicons@* \
41 node_modules/.bun/@rc-component+* \
42 node_modules/.bun/@rolldown+binding-* \
43 node_modules/.bun/@reduxjs+* \
44 node_modules/.bun/@shikijs+* \
45 node_modules/.bun/@splinetool+* \
46 node_modules/.bun/@tanstack+start-plugin-core@* \
47 node_modules/.bun/@tanstack+react-router@* \
48 node_modules/.bun/@tanstack+router-core@* \
49 node_modules/.bun/@types+* \
50 node_modules/.bun/ahooks@* \
51 node_modules/.bun/antd@* \
52 node_modules/.bun/better-sqlite3@* \
53 node_modules/.bun/bun-types@* \
54 node_modules/.bun/caniuse-lite@* \
55 node_modules/.bun/chroma-js@* \
56 node_modules/.bun/cytoscape*@* \
57 node_modules/.bun/d3@* \
58 node_modules/.bun/date-fns*@* \
59 node_modules/.bun/drizzle-kit@* \
60 node_modules/.bun/dompurify@* \
61 node_modules/.bun/emoji-mart@* \
62 node_modules/.bun/framer-motion@* \
63 node_modules/.bun/jiti@* \
64 node_modules/.bun/katex@* \
65 node_modules/.bun/layout-base@* \
66 node_modules/.bun/leva@* \
67 node_modules/.bun/lightningcss-* \
68 node_modules/.bun/lit-html@* \
69 node_modules/.bun/lucide-react@* \
70 node_modules/.bun/mermaid@* \
71 node_modules/.bun/motion@* \
72 node_modules/.bun/motion-* \
73 node_modules/.bun/polished@* \
74 node_modules/.bun/prettier@* \
75 node_modules/.bun/react-day-picker@* \
76 node_modules/.bun/react-hook-form@* \
77 node_modules/.bun/react@* \
78 node_modules/.bun/react-dom@* \
79 node_modules/.bun/react-dropzone@* \
80 node_modules/.bun/recharts@* \
81 node_modules/.bun/rolldown@* \
82 node_modules/.bun/shiki@* \
83 node_modules/.bun/solid-js@* \
84 node_modules/.bun/source-map@* \
85 node_modules/.bun/tailwind-merge@* \
86 node_modules/.bun/tsx@* \
87 node_modules/.bun/@upsetjs+venn.js@* \
88 node_modules/.bun/victory-vendor@* \
89 node_modules/.bun/virtua@* \
90 node_modules/.bun/typeorm@* \
91 node_modules/.bun/vite@* \
92 node_modules/.bun/vitest@* \
93 && find node_modules -type f \( -name '*.map' -o -name '*.d.ts' -o -name '*.d.mts' -o -name '*.d.cts' -o -name '*.ts' -o -name '*.tsx' -o -name '*.md' -o -name '*.markdown' \) -delete \
94 && find node_modules -type d \( -iname test -o -iname tests -o -iname __tests__ -o -iname fixtures -o -iname fixture -o -iname examples \) -prune -exec rm -rf {} + \
95 && find packages apps/host-selfhost -type f \( -name '*.map' -o -name '*.d.ts' -o -name '*.test.ts' -o -name '*.test.tsx' -o -name '*.md' -o -name '*.markdown' \) -delete \
96 && rm -rf \
97 packages/app \
98 packages/react \
99 packages/plugins/*/fixtures \
100 packages/plugins/*/src/react \
101 packages/plugins/*/src/**/__snapshots__ \
102 packages/plugins/*/test \
103 packages/plugins/*/testing \
104 packages/*/*/CHANGELOG.md \
105 packages/*/*/tsconfig.json \
106 packages/*/*/tsup.config.ts \
107 packages/*/*/vitest.config.ts \
108 packages/plugins/*/vitest.config.ts \
109 apps/host-selfhost/CHANGELOG.md \
110 apps/host-selfhost/Dockerfile \
111 apps/host-selfhost/docker-compose.yml \
112 apps/host-selfhost/scripts \
113 apps/host-selfhost/vitest.config.ts \
114 apps/host-selfhost/web \
115 apps/host-selfhost/tsconfig.json \
116 apps/host-selfhost/tsr.routes.ts \
117 apps/host-selfhost/vite.config.ts
118
15# ── Build stage: install the workspace + build the SPA ──────────────────────119# ── Build stage: install the workspace + build the SPA ──────────────────────
16FROM oven/bun:1 AS build120FROM oven/bun:1 AS build
17WORKDIR /app121WORKDIR /app
18COPY . .122COPY . .
19# Full install (dev deps included — vite/turbo/plugins are needed to build).123# Full install (dev deps included, vite/turbo/plugins are needed to build).
20RUN bun install --frozen-lockfile124RUN bun install --frozen-lockfile
21# Builds @executor-js/vite-plugin (via turbo) then the self-host SPA into125# Builds @executor-js/vite-plugin (via turbo) then the self-host SPA into
22# apps/host-selfhost/dist.126# apps/host-selfhost/dist.
23RUN cd apps/host-selfhost && bun run build127RUN cd apps/host-selfhost && bun run build
24# Reinstall PRODUCTION deps only, so the runtime image excludes the build/dev
25# toolchain pulled by the full workspace install — vite, turbo, wrangler →
26# miniflare → sharp/libvips (~800MB), astro, vitest, etc. The built SPA lives in
27# apps/host-selfhost/dist (outside node_modules), so it survives the reinstall.
28# --ignore-scripts: the root `prepare` hook runs a dev-only tool
29# (effect-language-service); runtime deps are prebuilt JS with no postinstall.
30RUN rm -rf node_modules && bun install --frozen-lockfile --production --ignore-scripts
31 128
32# ── Runtime stage: serve the built app under Bun ────────────────────────────129# ── Runtime stage: serve the built app under Bun ────────────────────────────
33FROM oven/bun:1 AS runtime130FROM gcr.io/distroless/cc-debian12 AS runtime
34WORKDIR /app131WORKDIR /app
35LABEL org.opencontainers.image.source="https://github.com/RhysSullivan/executor" \132LABEL org.opencontainers.image.source="https://github.com/RhysSullivan/executor" \
36 org.opencontainers.image.description="Single-container self-hosted Executor" \133 org.opencontainers.image.description="Single-container self-hosted Executor" \
@@ -39,9 +136,25 @@ ENV NODE_ENV=production \ · 2 unmodified lines
39 EXECUTOR_HOST=0.0.0.0 \136 EXECUTOR_HOST=0.0.0.0 \
40 PORT=4788 \137 PORT=4788 \
41 EXECUTOR_DATA_DIR=/data138 EXECUTOR_DATA_DIR=/data
42COPY --from=build /app /app139COPY --from=prod-deps /usr/local/bin/bun /usr/local/bin/bun
140COPY --from=prod-deps /app/node_modules /app/node_modules
141COPY --from=prod-deps /app/packages/core/api /app/packages/core/api
142COPY --from=prod-deps /app/packages/core/config /app/packages/core/config
143COPY --from=prod-deps /app/packages/core/execution /app/packages/core/execution
144COPY --from=prod-deps /app/packages/core/fumadb /app/packages/core/fumadb
145COPY --from=prod-deps /app/packages/core/sdk /app/packages/core/sdk
146COPY --from=prod-deps /app/packages/hosts/mcp /app/packages/hosts/mcp
147COPY --from=prod-deps /app/packages/kernel/core /app/packages/kernel/core
148COPY --from=prod-deps /app/packages/kernel/runtime-quickjs /app/packages/kernel/runtime-quickjs
149COPY --from=prod-deps /app/packages/plugins/encrypted-secrets /app/packages/plugins/encrypted-secrets
150COPY --from=prod-deps /app/packages/plugins/google /app/packages/plugins/google
151COPY --from=prod-deps /app/packages/plugins/graphql /app/packages/plugins/graphql
152COPY --from=prod-deps /app/packages/plugins/mcp /app/packages/plugins/mcp
153COPY --from=prod-deps /app/packages/plugins/microsoft /app/packages/plugins/microsoft
154COPY --from=prod-deps /app/packages/plugins/openapi /app/packages/plugins/openapi
155COPY --from=prod-deps /app/apps/host-selfhost /app/apps/host-selfhost
156COPY --from=build /app/apps/host-selfhost/dist /app/apps/host-selfhost/dist
43WORKDIR /app/apps/host-selfhost157WORKDIR /app/apps/host-selfhost
44RUN mkdir -p /data
45VOLUME ["/data"]158VOLUME ["/data"]
46EXPOSE 4788159EXPOSE 4788
47# Readiness probe against the public /api/health endpoint (a trivial DB ping).160# Readiness probe against the public /api/health endpoint (a trivial DB ping).
apps/host-selfhost/package.json+8−8
@@ -19,10 +19,8 @@ · 18 unmodified lines
19 },19 },
20 "dependencies": {20 "dependencies": {
21 "@better-auth/api-key": "^1.6.11",21 "@better-auth/api-key": "^1.6.11",
22 "@effect/atom-react": "catalog:",
23 "@effect/platform-bun": "catalog:",22 "@effect/platform-bun": "catalog:",
24 "@executor-js/api": "workspace:*",23 "@executor-js/api": "workspace:*",
25 "@executor-js/app": "workspace:*",
26 "@executor-js/execution": "workspace:*",24 "@executor-js/execution": "workspace:*",
27 "@executor-js/fumadb": "workspace:*",25 "@executor-js/fumadb": "workspace:*",
28 "@executor-js/host-mcp": "workspace:*",26 "@executor-js/host-mcp": "workspace:*",
@@ -32,18 +30,14 @@ · 3 unmodified lines
32 "@executor-js/plugin-mcp": "workspace:*",30 "@executor-js/plugin-mcp": "workspace:*",
33 "@executor-js/plugin-microsoft": "workspace:*",31 "@executor-js/plugin-microsoft": "workspace:*",
34 "@executor-js/plugin-openapi": "workspace:*",32 "@executor-js/plugin-openapi": "workspace:*",
35 "@executor-js/react": "workspace:*",
36 "@executor-js/runtime-quickjs": "workspace:*",33 "@executor-js/runtime-quickjs": "workspace:*",
37 "@executor-js/sdk": "workspace:*",34 "@executor-js/sdk": "workspace:*",
38 "@libsql/client": "catalog:",35 "@libsql/client": "catalog:",
39 "@libsql/kysely-libsql": "catalog:",36 "@libsql/kysely-libsql": "catalog:",
40 "@modelcontextprotocol/sdk": "^1.29.0",37 "@modelcontextprotocol/sdk": "^1.29.0",
41 "@tanstack/react-router": "catalog:",
42 "better-auth": "^1.6.11",38 "better-auth": "^1.6.11",
43 "drizzle-orm": "catalog:",39 "drizzle-orm": "catalog:",
44 "effect": "catalog:",40 "effect": "catalog:"
45 "react": "catalog:",
46 "react-dom": "catalog:"
47 },41 },
48 "devDependencies": {42 "devDependencies": {
49 "@effect/vitest": "catalog:",43 "@effect/vitest": "catalog:",
@@ -58,6 +52,12 @@ · 8 unmodified lines
58 "bun-types": "catalog:",52 "bun-types": "catalog:",
59 "typescript": "catalog:",53 "typescript": "catalog:",
60 "vite": "catalog:",54 "vite": "catalog:",
61 "vitest": "catalog:"55 "vitest": "catalog:",
56 "@effect/atom-react": "catalog:",
57 "@executor-js/app": "workspace:*",
58 "@executor-js/react": "workspace:*",
59 "@tanstack/react-router": "catalog:",
60 "react": "catalog:",
61 "react-dom": "catalog:"
62 }62 }
63}63}