Nov 14, 2021

Solving the unexecutable executable

Sometimes when building software, I run into problems that are extremely weird to grasp, and usually connected to something much more simple that wasn't immediately obvious. I think we often deal with small frustrations and once we have a fix, we move on, ignoring the educational value: Let me raise the recent case of a Docker container failing to launch a built binary.

I was preparing to deploy a Go-based service similar to an existing one, where building meant compiling the Go source to an executable, which would become the entry point for our Docker image. Since the service would spin up an HTTP endpoint, I fittingly named the binary server.

# This Dockerfile is simplified for the purpose of this post
COPY . .
RUN go build -o server
ENTRYPOINT server

The build succeeded, and everything seemed fine until I attempted to launch the service. When starting up, the container would fail with a permission denied error.

I went back to the existing service to compare the Dockerfile with my new one, which I copied and edited where necessary, the difference of which were merely names. As far as I was concerned, I couldn't see why the same Dockerfile would build for one service, but not the other, since everything should be the same, right?

First, I tried to make the executable, well, executable using chmod. Unfortunately, nothing changed, the error persisted.

Next up, I checked which files were presented after building, and to my surprise, in my terminal, the binaries were highlighted in green in the container that would work, and blue in the container that wasn't. The colors could already give you a hint to the actual problem, but I didn't think much of it.

After a period of trying to find the issue, I reached out to my colleagues, and a couple of minutes later, my coworker Jean got back with the issue: We had a name clash between the built binaries and existing directories. Ugh. I renamed the binary to server_bin and tada, the service launched.

Thinking back, I should have followed the file system more closely, what did the binary look like? Which permissions did it have and which one was it missing? Which information did the color codes contain? In the rush of the moment, I simply tried to get the issue fixed as fast as possible, not taking my time to debug more thoroughly.

To conclude, the source of the issue was that the Go CLI will not fail if you specify an output name that is a directory, instead, it will compile the source code and move the executable into the specified directory.

I think the real lesson of this story is to take your time to debug thoroughly, keep a clear head, maybe step out and do something else to clear your mind, then get back to it.