What are Docker COPY's rules about symlinks / how can I preserve symlinks?
Asked Answered
S

1

11

From this trial, it looks like Docker's COPY command doesn't preserve symlinks -- rather, it "follows" symlinks and copies the target file(?):

$ ls -l
total 4
lrwxrwxrwx 1 user domain users  1 Mar 26 09:37 a -> b
-rw-r--r-- 1 user domain users  0 Mar 26 09:37 b
lrwxrwxrwx 1 user domain users  1 Mar 26 09:41 c -> d
-rw-r--r-- 1 user domain users  0 Mar 26 09:41 d
-rw-r--r-- 1 user domain users 54 Mar 26 09:39 Dockerfile
$
# Dockerfile
FROM alpine:3.7 as base

COPY [ "./*", "/foo/bar/" ]
$ docker build -t foo:tmp . && docker run -it foo:tmp
[+] Building 1.1s (7/7) FINISHED
 => [internal] load build definition from Dockerfile                                                                       0.0s
 => => transferring dockerfile: 116B                                                                                       0.0s
 => [internal] load .dockerignore                                                                                          0.0s
 => => transferring context: 2B                                                                                            0.0s
 => [internal] load metadata for docker.io/library/alpine:3.7                                                              0.8s
 => [internal] load build context                                                                                          0.0s
 => => transferring context: 12.52kB                                                                                       0.0s
 => CACHED [1/2] FROM docker.io/library/alpine:3.7@sha256:8421d9a84432575381bfabd248f1eb56f3aa21d9d7cd2511583c68c9b7511d10 0.0s
 => [2/2] COPY [ ./*, /foo/bar/ ]                                                                                          0.1s
 => exporting to image                                                                                                     0.1s
 => => exporting layers                                                                                                    0.0s
 => => writing image sha256:81531080243eedcb443c7fe0c9e85df92515a6cf3f997c549414cae9bf6ca925                               0.0s
 => => naming to docker.io/library/foo:tmp                                                                                 0.0s
/ # ls -l /foo/bar/
total 4
-rw-r--r--    1 root     root            67 Mar 26 16:43 Dockerfile
-rw-r--r--    1 root     root             0 Mar 26 16:37 a
-rw-r--r--    1 root     root             0 Mar 26 16:37 b
-rw-r--r--    1 root     root             0 Mar 26 16:41 c
-rw-r--r--    1 root     root             0 Mar 26 16:41 d
/ #

This behavior appears to be the same whether I'm copying from my context or from another Docker image layer.

Is there some way I can get Docker copy to preserve symlinks i.e. instead of it "following" them and creating hard files? Or is there some convention to work around this situation?

The context behind this question is that a base layer I'm copying from has (a lot of) a mix of files and symlinks that point to them. Example: libfoo.so -> libfoo.so.1 -> libfoo.so.1.0.0


I spent a while searching online for this topic, but found surprisingly few hits. Most "close" questions were about symlinks to directories, which isn't the same as my situation. The closest hit I got was this unfortunately unanswered question: https://forums.docker.com/t/copying-symlinks-into-image/39521

Swivet answered 26/3, 2021 at 16:59 Comment(2)
Does copying the directory COPY ./ /foo/bar/ work better than copying individual files/symlinks? (This would be consistent with /bin/cp; it's not well-documented in the Dockerfile documentation.)Champaigne
@DavidMaze - that does work; thank you. At first, I thought my real-world case would be trickier to do this than my MCVE because the source and destination directory structures are different...but it didn't matter: as long as I specify the full source path (ending in /) and the full destination path, this works.Swivet
C
7

This works if you try to copy the entire directory as a unit, rather than trying to copy the files in the directory:

COPY ./ /foo/bar/

Note that there are some subtleties around copying directories: the Dockerfile COPY documentation notes that, if you COPY a directory,

NOTE: The directory itself is not copied, just its contents.

This is fine for your case where you're trying to copy the entire build context. If you have a subdirectory you're trying to copy, you need to make sure the subdirectory name is also on the right-hand side of COPY and that the directory name ends with /.

Champaigne answered 26/3, 2021 at 20:5 Comment(3)
Can you help explain why COPY foo foo is different than COPY foo/ foo/ in this context?Thermopile
The / at the end of the destination name is syntactically significant.Champaigne
Can you explain how Docker's compile process will behave differently given the presence or absence of /?Thermopile

© 2022 - 2024 — McMap. All rights reserved.