Buildx: Error: cannot copy to non-directory with buildx only

Created on 25 Sep 2019  Â·  12Comments  Â·  Source: docker/buildx

I use the following Dockerfile:

FROM alpine

COPY /root/ /

where /root/ contains a single file in root/var/spool/cron/crontabs/root.

If I run docker build ., it builds successfully. However, if I use docker buildx build .

failed to solve with frontend dockerfile.v0: failed to build LLB: cannot copy to non-directory: /tmp/buildkit-mount656587830/var/spool/cron/crontabs

buildkit

Most helpful comment

The non-directory is in the destination, already inside the image. Eg. if it is not a symlink your case is equivalent to

/tmp # mkdir -p src/services/nginx
/tmp # mkdir -p dest
/tmp # echo aa > dest/services
/tmp # cp -a src/. dest/
cp: target 'dest/./services' is not a directory

All 12 comments

@joshuaavalon is that a symbolic link?

@FernandoMiguel No. Actually, you can see here.

The build log is here

Also, I have tried if /root/ only has /root/a.json, it builds fine. May be it has something to do with nested directories.

Ah! Someone already made an action to get buildx. Ahahaha
I should have done the same.... I building from source

I'm going to be offline all day so can't test this.
But I'll try to reproduce the nested use case

This is actually the same error I get when I try the same command with regular cp. "cp: target '/var/spool/./cron/crontabs' is not a directory" . It's a bit weird case as cron/crontabs is a symlink to a directory (not a file what you might expect from this error). I think it is fair to consider the old behavior as a bug as it may have very unexpected results. I verified that the behavior is consistent with fileop and dockerfile-copy implementations.

The issue appears because in alpine base image, /var/spool/cron/crontabs is a symlink to /etc/crontabs, but in your context you have a directory with matching path. So the correct way to write this instruction would be:

COPY /root/var/spool/cron/crontabs /var/spool/cron/crontabs

or

COPY /root/var/spool/cron/crontabs /etc/crontabs

or just rename the file to /etc/crontabs/root under your context if you want to copy full root.

@tiborvass

I expected as much from overriding a symlink. didnt had the time to test it.
file-ops over symlinks are confusing and tend to fail

Ok, so the rationale for the cp command's design is that the user shall be guaranteed that the copy operation puts files exclusively under the destination directory. Symlinks can point outside of the destination which would break that guarantee. So I'm fine with qualifying the legacy builder's behavior as incorrectly lenient.

In this specific case, I would probably go with the 3rd workaround: renaming the crontab to /etc/crontabs/root in the context.

I'm closing this as buildkit/buildx behaves as intended. Feel free to continue discussing if need be.

@tiborvass any plan to treat the behaviour of the old Docker build as a bug and fix it?
Might break a few people implementations

I have no symlink but have the same issue. The old builder is working as expected, but this does not work:

COPY docker/manager-app/manager/etc/. /etc/

There is no symlinks. The output is:

=> ERROR [builder 4/7] COPY docker/manager-app/manager/etc/ /etc/
------
 > [builder 4/7] COPY docker/manager-app/manager/etc/ /etc/:
------
failed to solve with frontend dockerfile.v0: failed to build LLB: cannot copy to non-directory: /var/lib/docker/aufs/mnt/mzdig9xzkf3pg13mr1grvfnvg/etc/service

My folders are organized the following:

â–¾ docker/ â–¾ manager-app/ â–¾ manager/ â–¾ etc/ â–¾ nginx/ nginx.conf â–¾ php7/ â–¾ conf.d/ custom.ini php-fpm.conf â–¾ service/ â–¾ nginx/ run* â–¾ php-fpm/ run* Dockerfile

So everything should be ok... But it's not.

The non-directory is in the destination, already inside the image. Eg. if it is not a symlink your case is equivalent to

/tmp # mkdir -p src/services/nginx
/tmp # mkdir -p dest
/tmp # echo aa > dest/services
/tmp # cp -a src/. dest/
cp: target 'dest/./services' is not a directory

Oh, indeed, the services file was existing inside the image, that's why it failed. The old builder was overriding this...

At least if somebody has this kind of issue, he can find an answer here now. Thanks :). (and wow, that quick answer)

Was this page helpful?
0 / 5 - 0 ratings