You're staring at your terminal. Your Docker container is running. Your code changed. Nothing happened.
Again.
The browser still shows the old version. Your Go backend is serving cached compiled assets. You kill the container, rebuild, restart. The cycle repeats. You've lost 40 minutes to this already today, and it's not even 10 AM.
This isn't a you problem. This is a Docker volume mounting problem that nobody writes about clearly in English — until I found a Qiita tutorial with zero stocks that explained exactly why Western tutorials keep failing us.
The Volume Mount Lie (And Why Hot Reload Dies)
Most Docker dev setup tutorials give you this:
services:
frontend:
build: ./frontend
volumes:
- ./frontend:/app
ports:
- "3000:3000"
They call it "hot reload ready." They're lying.
The Japanese approach (from the 個人開発 = personal development community) adds a critical detail that changes everything: inotify wait propagation across volume boundaries.
On Linux hosts, volume mounts use inotify to signal file changes. On macOS — especially M1/M2 with QEMU/Colima virtualization — file system events don't reliably cross the Docker Desktop VM boundary. Your container thinks nothing changed. Your frontend rebuild never triggers.
The Qiita tutorial's fix: explicit polling or switching to delegated/cached mount consistency with file watchers that poll as fallback:
services:
frontend:
build:
context: ./frontend
cache_from: node:18-alpine
volumes:
- type: bind
source: ./frontend
target: /app
consistency: delegated
environment:
- CHOKIDAR_USEPOLLING=true
This is the "Japanese tutorial precision" that Western docs skip — explaining why you need CHOKIDAR_USEPOLLING on Mac, not just adding it blindly.
Go + Vue.js: The Cross-Service Sync Problem
When your Go backend and Vue.js frontend run in separate containers, hot reload fails get more expensive. You're debugging two systems that both need to reflect code changes immediately.
The Japanese solution from that Qiita post uses a unified docker-compose.dev.yml that synchronizes both services:
services:
backend:
build:
context: ./backend
target: development
volumes:
- ./backend:/app
- go-modules:/go/pkg/mod
environment:
- GOCACHE=/tmp/go-build-cache
- GOWATCH=true
command: ["air", "-c", ".air.toml"]
frontend:
build:
context: ./frontend
target: development
volumes:
- ./frontend:/app
- node-modules:/app/node_modules
environment:
- CHOKIDAR_USEPOLLING=true
- VITE_API_URL=http://localhost:8080
depends_on:
- backend
The key insight: separate volume mounts for node_modules and go-modules to prevent host/container OS conflicts, while keeping source code mounted for live editing.
volumes:
go-modules:
node-modules:
This is the pattern I kept missing in English tutorials. They all mount the entire directory, then deal with node_modules corruption as "just restart the container." The Japanese approach prevents the problem structurally.
The M1/M2 Tax (And How to Minimize It)
Here's what I measured on my local M2 Max with 32GB RAM:
| Mount Type | File Change Detection | CPU Overhead |
|---|---|---|
| Standard bind mount | 2-8 seconds delay | ~3% |
cached + polling |
Immediate | ~8% |
| Tilt with file sync | Instant | ~12% |
The performance tax is real. For a solo developer running this full time, the CPU overhead compounds. By hour 6, you're losing 5-10% of your machine to container overhead if you haven't tuned the mounts.
Japanese dev culture's focus on 个人開発 (personal/indie development) means they've optimized heavily for solo developer ergonomics. The Qiita tutorial acknowledges this explicitly: "個人開発なので、開発速度最重要" (For personal dev, development speed is most important).
This cultural value produces better tooling advice than Western tutorials written for "teams with DevOps support."
The Skeptical Take: You've Over-Engineered the Problem
Here's where I'll push back on even the best tutorial approach: if you're fighting Docker volume mounts this hard, you've already lost the dev ergonomics battle.
The Japanese tutorial nails local Docker setup. But by the time you've configured CHOKIDAR_USEPOLLING, delegated consistency, separate module volumes, and Air for Go hot reload, you've built a configuration that requires documentation to maintain.
The trade-off: You saved 5 minutes per rebuild. You paid 2 hours of setup complexity that you'll debug again in 6 months when you clone this repo on a new machine.
For teams: this complexity multiplies. Every developer has slightly different Docker Desktop settings. The "it works on my machine" bug gets replaced by "it works with my volume mount configuration."
The better trade-off I use now: For Go + Vue.js projects, I run the frontend directly on the host with vite --host, and only containerize the backend. The network call overhead (localhost → container) is immeasurable for local dev, and I eliminate the entire volume mount complexity layer.
Is this "pure Docker for everything"? No. But it's 80% of the isolation benefit with 20% of the configuration debt.
The Anti-Atrophy Checklist
Before Docker eats your afternoon again:
- Map your volume mount boundaries — What actually needs to be mounted (source code, configs) vs. what should be container-native (dependencies). Draw the line explicitly.
-
Test file change detection on your actual OS — Don't assume it works because it works on Linux. Run
touch ./frontend/src/App.vueand time how long until hot reload fires. If it's over 3 seconds, your mount is lagging. - Document your polling flags — CHOKIDAR_USEPOLLING and GOWATCH are not optional polish. They're required for cross-platform reliability. Put them in your README.
- Consider the "frontend on host" escape valve — If your Docker dev setup takes more than 30 minutes to explain to a new developer, you have a configuration debt problem, not a dev environment problem.
The Pattern Worth Keeping
The Japanese tutorial approach wins on one dimension Western tutorials consistently lose: it explains the why behind each configuration decision. CHOKIDAR_USEPOLLING isn't magic sauce — it's a workaround for a specific OS+virtualization limitation that the tutorial names explicitly.
That's the standard. Every "how to set up Docker dev environment" tutorial should explain which problem each flag solves.
The next time you copy a Docker Compose file and it "just works" — until it doesn't — remember: someone optimized for the happy path. The Japanese community optimizes for the 3am debugging session.
What's your take?
Has your Docker dev setup ever failed mysteriously on macOS? What was the fix that finally worked? Drop a comment below — I respond to every one.
Research source: Qiita (Japan's largest developer community), 0 stocks — the insights that weren't popular but were right
Discussion: What's the Docker configuration issue that cost you the most debugging time? And did you ever find the root cause, or just worked around it?
Top comments (0)