DEV Community

Roland HUON
Roland HUON

Posted on

It built on Windows, failed on Mac and Linux, and the Dockerfile was fine

I asked Claude Opus 5 for a Dockerfile and a Compose file for a Spring Boot project. Multi-stage build, dependencies cached in their own layer, a healthcheck on Postgres, the app waiting for the database before starting. I read it, it looked right, I ran it on my Windows machine. It built. It ran. I merged it.

On Mac and Linux, the build died on the first RUN:

/bin/sh: 1: ./mvnw: Permission denied
Enter fullscreen mode Exit fullscreen mode

Here are the lines involved:

COPY .mvn/ .mvn/
COPY mvnw pom.xml ./
RUN ./mvnw dependency:go-offline -B
Enter fullscreen mode Exit fullscreen mode

There is nothing wrong with them. That's the interesting part.

Where the bug actually was

Not in the Dockerfile. In git:

$ git ls-files -s mvnw
100644 bd8896bf2217b46faa0291585e01ac1a3441a958 0   mvnw
Enter fullscreen mode Exit fullscreen mode

100644 means a regular, non-executable file. A script you're meant to run should be 100755. The Maven wrapper had been committed without its executable bit — most likely because the first commit came from Windows, where git doesn't track that bit by default.

On Mac and Linux, checking out the repo gives you an mvnw with mode 644. COPY preserves that mode inside the image. ./mvnw refuses to run.

Why Windows hid it

Windows files don't carry Unix permissions. When you build from a Windows client against a Linux engine, Docker has nothing to copy over, so every file in the build context arrives in the image as rwxr-xr-x. Docker has long printed a warning about exactly this when building from Windows.

So on Windows, mvnw became executable by accident, and the build passed. The one test I ran was structurally incapable of catching the bug.

It wasn't just Docker, either: ./mvnw spring-boot:run failed on Mac and Linux for the same reason. The whole project only worked on Windows.

The red herring

The repo had this .gitattributes:

/mvnw text eol=lf
*.cmd text eol=crlf
Enter fullscreen mode Exit fullscreen mode

It's easy to read that and conclude mvnw is taken care of. It isn't. That rule handles line endings. File mode is a completely separate thing, and nothing in the repo was handling it.

The fix

At the source, so every clone gets it right:

git update-index --chmod=+x mvnw
git commit -m "Make mvnw executable"
Enter fullscreen mode Exit fullscreen mode

And belt and braces in the Dockerfile, so a copy that lost the bit — a downloaded zip, say — still builds:

RUN chmod +x mvnw && ./mvnw dependency:go-offline -B
Enter fullscreen mode Exit fullscreen mode

Or skip the question entirely, since sh doesn't need the executable bit:

RUN sh mvnw dependency:go-offline -B
Enter fullscreen mode Exit fullscreen mode

The same applies to gradlew and any shell script you expect people to run.

Check your own repos

git ls-files -s | grep -E '(mvnw|gradlew|\.sh)$'
Enter fullscreen mode Exit fullscreen mode

Anything in that list showing 100644 is waiting for someone on another OS.

This isn't a rare edge case. Out of curiosity I checked another public Spring Boot project from a different author: same wrapper file, byte for byte, same 100644. Its Docker build was fine, because it used the official Maven image instead of the wrapper. Its local build was not.

What I take from it

The generated Dockerfile was correct. The diff was correct. The review, had there been one, would have looked at those lines and approved them. The bug lived in the relationship between the file and the repository's metadata, which a diff doesn't show — and it only appeared on machines the author never used.

When the code in front of you is right, the questions worth asking are about everything around it: which platform was this tested on, what does the file mode say, what happens on the second machine?


I run a small weekly code-review challenge built around bugs like this one, where every line is correct and something is still broken. This was the first challenge of the season: https://www.linkedin.com/feed/update/urn:li:activity:7510232443304714240. Would you have merged it?

Top comments (0)