Docker

Docker Container Slow Performance on macOS (Docker Desktop)

Slow Docker performance specifically on macOS, especially with heavy file I/O like Node.js dependency installs or hot-reloading dev servers, comes down to how Docker Desktop on Mac runs containers inside a lightweight virtual machine — file operations crossing the boundary between the Mac filesystem and that VM carry real overhead that doesn't exist on native Linux.

The Problem

A project that runs fast natively, or fast in Docker on a Linux machine, feels noticeably sluggish inside Docker Desktop on macOS — slow file watching, slow npm install, or a dev server that takes far longer to pick up changes than expected. Sometimes it presents as a hanging build step with no clear error, other times you see explicit timeouts:

Error: ETIMEDOUT
context deadline exceeded

Why It Happens

Unlike Linux, where Docker runs containers natively on the host kernel, Docker Desktop on macOS runs a lightweight Linux VM under the hood, and containers run inside that VM. Common causes of the resulting slowness:

  • Bind-mounted volumes (mapping a folder on your Mac into the container) require file operations to cross the VM boundary, and this synchronization has real, measurable overhead — especially noticeable with projects containing many small files, like node_modules
  • The VM's allocated CPU and memory resources are set too low for the workload, either at Docker Desktop's defaults or through prior manual configuration that no longer matches your actual project needs
  • File watching mechanisms (used by dev servers for hot reload) sometimes don't propagate change events efficiently across the bind-mount boundary, causing either missed updates or excessive polling overhead
  • Using the older, less efficient file sharing implementation in Docker Desktop's settings, rather than the newer virtualization framework and file sharing options that later versions introduced specifically to address this class of performance issue

The Fix

First, confirm you're on a reasonably recent version of Docker Desktop, and check its file sharing implementation setting under Settings > General — newer options (like VirtioFS on Apple Silicon Macs) are significantly faster than the older gRPC FUSE implementation for bind-mounted file access:

# Docker Desktop > Settings > General > File sharing implementation
# Choose VirtioFS if available for your setup

Check and increase the VM's allocated resources under Settings > Resources, since Docker Desktop's defaults are conservative and may not reflect what your actual machine can spare:

# Settings > Resources > CPUs / Memory
# Increase toward what your workload genuinely needs, leaving headroom for macOS itself

For directories with a very large number of small files that don't need to be actively edited from the Mac side (most commonly node_modules), avoid bind-mounting them at all — use a named Docker volume instead, which lives entirely inside the VM and avoids the cross-boundary sync overhead entirely:

services:
  app:
    volumes:
      - .:/app
      - /app/node_modules  # anonymous volume - keeps node_modules inside the container/VM

This pattern bind-mounts your actual source code (which you do need synced for live editing) while excluding node_modules from that sync, letting it live natively inside the container's own filesystem where access is fast.

If a dev server's hot reload feels sluggish or misses changes intermittently, check whether it's configured to use polling-based file watching, which is sometimes more reliable (if less efficient) across the VM boundary than native filesystem events:

# Example: webpack dev server
watchOptions: {
  poll: 1000,
}

Still Not Working?

If performance is still poor after addressing file sharing settings and resource allocation, benchmark specifically to confirm whether the bottleneck really is file I/O versus something else entirely — CPU-bound work inside a properly resourced container generally performs close to native speed on macOS, so if a compute-heavy task (not filesystem-heavy) is still slow, the actual cause is more likely to be insufficient CPU allocation to the VM rather than the file sharing mechanism at all. Running docker stats while reproducing the slow operation shows real-time CPU and memory usage per container, clarifying which resource is actually being exhausted:

docker stats