Post

🐳 Speeding Up Docker: Avoiding Slowness in Host-to-Container Syncing

Docker makes it easy to develop in isolated environments, but file syncing between host and container can introduce painful slowness — especially on macOS and Windows. This issue becomes critical for large projects like Node.js apps, where frequent file access (like node_modules) and rebuilds can drastically degrade performance.

In this article, we’ll explore:

  • Differences between Docker volumes and bind mounts
  • Why syncing gets slow
  • Tools like docker-sync
  • Alternative techniques to regain performance in local development

🔍 Volume vs. Bind Mounts: What’s the Difference?

Docker provides two main mechanisms to access files inside a container:

1. Bind Mounts

1
2
volumes:
  - .:/app

This maps a directory from the host directly into the container. It’s useful for live reloads during development, but:

  • Host file system is accessed frequently
  • On macOS/Windows, this means a hypervisor layer, which is slow
  • Worst for things like node_modules, which have many small files

2. Docker Volumes

1
2
volumes:
  - /app/node_modules

Docker volumes live inside the Docker engine and are not backed by the host filesystem. They’re much faster and ideal for directories that don’t need to sync with the host, like node_modules.


💡 Strategy: Split Bind Mounts and Volumes

A good pattern is:

1
2
3
volumes:
  - .:/app            # Sync code
  - /app/node_modules # Isolate dependencies

This lets you edit code live, but keeps heavy folders in the container.

For even more performance:

  • Use .dockerignore to skip syncing node_modules, .cache, .next, etc.
  • Avoid installing dependencies in a bind-mounted directory

🧰 Tool: docker-sync (macOS only)

docker-sync is a tool specifically built to speed up Docker on macOS by decoupling host-container sync using a performant rsync or native OS tool.

How It Works

  • Sets up a background sync service (rsync/unison/native)
  • Keeps the sync isolated from bind mount slowness
  • Syncs your files efficiently into a Docker volume

Typical Setup

docker-sync.yml

1
2
3
4
5
6
7
8
version: "2"
options:
  verbose: true
syncs:
  myapp-sync:
    src: './'
    sync_strategy: 'native_osx'
    sync_excludes: ['node_modules', '.git']

docker-compose.yml

1
2
3
volumes:
  - myapp-sync:/app:nocopy
  - /app/node_modules

Then run:

1
2
docker-sync start
docker-compose up

📌 Note: Use docker-sync only on macOS — it’s not useful or needed on Linux.


⚡ Alternative Tools & Tactics

1. Mutagen (cross-platform, fast sync)

  • Commercial-grade alternative to docker-sync
  • Used by tools like Lando, Colima, and Tilt
  • Integrates directly with Docker Desktop via extensions

🔗 https://mutagen.io/


2. Build Inside the Container

Rather than relying on bind mounts, do everything inside the container:

1
2
COPY . .
RUN npm ci

Pros:

  • No sync at all
  • Great for CI or staging

Cons:

  • Need rebuilds for every code change
  • Not ideal for active development

3. Use WSL 2 (Windows only)

If you’re on Windows, WSL 2 can drastically improve file I/O speeds compared to Docker Desktop’s default setup. Mount your project from inside the Linux filesystem (/home/user/project) instead of from C:\.


4. Use Dev Containers or Nix/Nixpacks

Advanced dev environments like GitHub Codespaces or Devbox provide isolated, reproducible environments that avoid local syncing entirely.


🧪 Benchmarks (approximate)

SetupCold Start (s)File Access Speed
Bind mount w/ node_modules10–20+🐢 Very slow
Docker volume only2–5⚡ Fast
docker-sync (macOS)3–7🚀 Fast
Build-in-container (no sync)1–3🚀 Very fast

🧭 Final Recommendations

Use CaseBest Strategy
Live dev, file reloadBind-mount code only, volume for deps
macOS devAdd docker-sync
CI or stagingBuild in container, no mount
WindowsUse WSL 2 and run inside Linux FS
Large monorepoSelective mount only essential folders

📦 Template: docker-compose.yml

1
2
3
4
5
6
7
8
services:
  app:
    build: .
    volumes:
      - .:/app
      - /app/node_modules
    ports:
      - "3000:3000"

🧠 Conclusion

Docker’s bind mounts offer convenience, but come at the cost of performance. By intelligently splitting volumes, excluding heavy directories, or using tools like docker-sync, you can restore a smooth, fast developer experience — even on Mac and Windows.


Got a different setup or optimization trick? Share it with me — the Docker dev workflow is always evolving!

This post is licensed under CC BY 4.0 by the author.