Dockerfile style builds fail if container definition file is in .dockerignore (and not `Dockerfile`)

Many container definition files contain something like

WORKDIR /app
COPY . .

which is normally great, but actually copies too many files into the image, .git, any local build results, etc. Adding these generated or otherwise unwanted files to .dockerfile lets one easily ignore files from that COPY without having the list all the files that do need to be copied.

One of the things that seems pointless inside the container is the Containerfile itself (I’m not using Docker so I try to avoid product-specific naming). However, adding that line to ignores flyctl deploy --remote-only when using [build] dockerfile = "Containerfile":

$ echo FROM nginx >Containerfile
$ echo /Containerfile >.dockerignore
$ flyctl launch --dockerfile Containerfile --generate-name --now
[...]
Remote builder fly-builder-hidden-field-6135 ready
==> Creating build context
--> Creating build context done
==> Building image with Docker
--> docker host: 20.10.12 linux x86_64
Sending build context to Docker daemon     135B
[+] Building 0.0s (2/2) FINISHED
 => [internal] load remote build context                                               0.0s
 => copy /context /                                                                    0.0s
Error error building: failed to solve with frontend dockerfile.v0: failed to read dockerfile: open /data/docker/tmp/buildkit-mount747134324/Containerfile: no such file or directory

It seems the filename Dockerfile is somehow special cased:

$ echo FROM nginx >Dockerfile
$ echo /Dockerfile >.dockerignore
$ flyctl launch --dockerfile Dockerfile --generate-name --now
[...]
Remote builder fly-builder-hidden-field-6135 ready
==> Creating build context
--> Creating build context done
==> Building image with Docker
--> docker host: 20.10.12 linux x86_64
Sending build context to Docker daemon     168B
[+] Building 1.1s (5/5) FINISHED
 => [internal] load remote build context                                               0.0s
 => copy /context /                                                                    0.0s
 => [internal] load metadata for docker.io/library/nginx:latest                        1.1s
 => CACHED [1/1] FROM docker.io/library/nginx@sha256:859ab6768a6f26a79bc42b2316641113  0.0s
 => exporting to image                                                                 0.0s
 => => exporting layers                                                                0.0s
 => => writing image sha256:d4606287fe3e437253e4aac2e3f4d4b1447992df158c14359b3d097d9  0.0s
 => => naming to registry.fly.io/quiet-wave-8583:deployment-1651607874                 0.0s
--> Building image done
==> Pushing image to fly
The push refers to repository [registry.fly.io/quiet-wave-8583]
b6812e8d56d6: Mounted from purple-water-1551
[...]

It seems something about Fly’s remote build infrastructure is using .dockerignore in a role it’s not meant for.

(There’s also a typo in the output, Using dockefile Dockerfile.)

Not entirely sure what you mean … For my Fly apps, I have a Dockerfile and .dockerignore. And they work as expected, with the ignore file doing its job of telling the builder which files to include. You are correct in that without a .dockerignore file, you will get all kinds of files you don’t want copied into the image, like .git and whatnot. So you do need one. But the builder should (does) respect the .dockerignore.

I haven’t looked through all your command output, but could it be the .dockerignore content isn’t valid or perhaps not structured to exclude the files you expect it to? Hmm.

If you like you could post the content of your Dockerfile and .dockerignore to see if anyone can spot any issue with them?

This appears to be a specific Docker feature which treats Dockerfile differently when it’s ignored. From the docs:

You can even use the .dockerignore file to exclude the Dockerfile and .dockerignore files. These files are still sent to the daemon because it needs them to do its job. But the ADD and COPY instructions do not copy them to the image.

The Docker daemon running on our remote hosts needs the Dockerfile to run the build against the uploaded context. But it should not show up in the final image. Docker doesn’t appear to support this feature using a filename other than Dockerfile.

Oh man, Docker never fails to have weird boobytraps.

That rule really should be about the file I requested it to use, not the literal name Dockerfile. Even docker build -f will fail otherwise.

Otherwise, it’ll break for people using e.g. Dockerfile.webapp and Dockerfile.redis, even outside of Fly.

Yeah, agreed there. If you are really picky about those files not being in the final image, you can use a multistage build and remove them before the final stage.

Edit: I tested this with docker build -f which seems to work as expected. So this is probably something to do with how we run the build. I don’t think we’d prioritize fixing this, but it’s good to know about it.