Baike.dev
All toolsAI codingTrendingOpen sourceNewsSubmit
Log in
Back to tool/Back to issues
#2448·encore

TypeScript: `encore build docker` bundles full workspace (devDeps + non-runtime files) with no opt-out

Author: vendoshiCreated May 14, 2026Updated May 14, 2026

Summary

For TypeScript apps, encore build docker bundles the entire workspace into /workspace in the image, with only .git excluded. There is no public configuration to filter what gets included — node_modules ships as-is (devDependencies + dev tooling), and arbitrary repo files (docs/, scripts/, README.md, tsconfig.tsbuildinfo, …) ship too.

This inflates image size dramatically and slows down build / push / Cloud Run cold-start, with no warning to the user.

Reproduce

  1. git clone any TS Encore app that has heavy devDependencies (e.g. typescript, vitest, @biomejs/biome, promptfoo, firebase-tools).
  2. pnpm install (or npm install — both pull devDeps by default).
  3. encore build docker --base node:24-bookworm app:test.
  4. docker run --rm --entrypoint sh app:test -c 'ls /workspace && du -sh /workspace/node_modules'.

Observed

  • /workspace/node_modules contains every devDependency.
  • /workspace/ contains every non-.git file from the repo, regardless of whether it's needed at runtime.

In our case the image carried ~50+ MB of pure devDeps (typescript 31 MB, vitest, @biomejs/biome, firebase-tools, etc.) plus a 26 MB promptfoo. Verified inside an image: pnpm why typescript --prod returns empty in the host repo, yet /workspace/node_modules/typescript exists in the image.

Expected

For TS apps, either:

  • Default to production-only installs (npm/pnpm/yarn install --prod), or
  • Provide a documented opt-out via encore.app, e.g.:
    jsonc
    {
      "build": {
        "docker": {
          "exclude_paths": ["node_modules/.cache", "docs", "scripts"],
          "prod_only_node_modules": true
        }
      }
    }

Source references

  • pkg/dockerbuild/spec.go → DetermineIncludes — copies node_modules and package.json from every ancestor between workspace root and app root.
  • cli/daemon/export/export.go — if buildSettings.Docker.BundleSource || appLang == appfile.LangTS forces source bundling for TS apps; ExcludeSource is hardcoded to [".git"].

So the user-facing bundle_source flag in encore.app has no effect for TS apps — the bundling happens regardless.

Real-world impact

We observed a ~2× deploy-time regression on Cloud Run after a normal dependency-bump cycle:

Phase Before After
Encore compile 67 s 142 s
"saving image to local daemon" 36 s 102 s
Final layer push to GCR 4 s 61 s
Cloud Run revision rollout 40 s 124 s
Total deploy ~4.5 min ~10 min

Root cause was a single new 26 MB devDependency silently shipping to production. A user has no signal this would happen.

Workaround

Run `pnpm install --prod --frozen-lockfile` (or `npm ci --omit=dev`) in CI before `encore build docker`, so the workspace's `node_modules` only contains production deps when Encore copies it. Confirmed effective.

Ask

  1. Document the current bundling behavior for TS apps in docs/ts/self-host/build.
  2. Either default to prod-only installs, or expose `exclude_paths` / `prod_only` in `encore.app` build config.

Encore version: v1.57.4 Host: macOS 25.3.0 (arm64), GitHub Actions `ubuntu-24.04` runner Package manager: pnpm 10

Source: encoredev/encore

View original on GitHubView discussion on GitHub