Copying Files¶
copyFileToContainer, copyContentToContainer, and copyFileFromContainer move files into or
out of an already-running container — the Testcontainers-style runtime operations, distinct
from withCopyFileToContainer's start-time mount.
Runtime copy vs. start-time mount¶
withCopyFileToContainer(file, guestPath) is configured before start() and takes effect at
boot: a read-only-by-default host mount, present from the moment the container comes up. See
Files & Memory.
The three methods on this page are different: they act on a container that is already running,
any time after start(). Reach for them when the file to copy doesn't exist yet at boot time —
generated during the test, produced by an earlier step, or read back out after the workload has
written it.
withCopyFileToContainer |
copyFileToContainer / copyContentToContainer / copyFileFromContainer |
|
|---|---|---|
| When | Configured before start() |
Any time after start() |
| Container state | Not yet running | Running |
| Direction | Host → guest only | Both directions |
| Typical use | Seed data known ahead of time | Generated fixtures, extracting artifacts/debug dumps |
API¶
val c = GenericContainer("alpine:3.19").withCommand("sleep", "120")
c.start()
// host file/dir -> container
c.copyFileToContainer(Path.of("/host/seed.sql"), "/seed/seed.sql")
// in-memory bytes or string -> container
c.copyContentToContainer("SELECT 1;", "/seed/probe.sql")
c.copyContentToContainer(byteArrayOf(1, 2, 3), "/seed/payload.bin")
// container file/dir -> host
c.copyFileFromContainer("/var/log/app.log", Path.of("/host/out/app.log"))
All three:
- require the container to be running — a never-started or stopped container throws
IllegalStateException(the same "not running" errorexecInContainer/getMappedPortuse), before any backend call; - require an absolute container path — a relative one throws
NonAbsoluteContainerPathExceptionbefore any backend call, since bothdocker cpandmsb copyrequire aNAME:/abs/pathshape; - create missing parent directories automatically — a copy-in creates the destination's
parent directory in the guest (
mkdir -p) first; a copy-out creates the host destination's parent directory first. Neither side needs to be pre-created by the caller; - raise
ContainerCopyExceptionon a failed transfer — carrying the underlying tool's stderr (a missing source file in the guest, a permission error, etc.). A failed copy is never a silent success.
Directory semantics¶
Each method accepts a file or a directory as its source — there's no separate "directory"
variant. Semantics follow docker cp/msb copy themselves: copying a directory to a destination
that doesn't yet exist creates that destination as a copy of the source directory's contents,
not as a subdirectory named after the source:
// hostDir contains: hostDir/a.txt, hostDir/nested/b.txt
c.copyFileToContainer(hostDir, "/dst")
// guest now has: /dst/a.txt, /dst/nested/b.txt — NOT /dst/hostDir/a.txt
The same cp -r-style naming applies to copy-out.
copyContentToContainer¶
The in-memory convenience over copyFileToContainer: writes content (a ByteArray or a
String, UTF-8 encoded) to a temp file — mode 0600 where the host filesystem supports POSIX
permissions — delegates to copyFileToContainer, then removes the temp file whether the copy
succeeded or not. There's no streaming protocol; for anything large enough that buffering the
whole payload in memory first is a problem, write it to a host file yourself and use
copyFileToContainer directly.
Reuse caveat¶
Runtime copy is just an ordinary operation against a running sandbox — it works the same way on
a reuse container as on any other. It is not part of reuse identity: copying a
file into (or out of) a reused sandbox mutates its shared state, and that mutation is invisible
to the identity hash. If two equivalent containers are meant to adopt the same sandbox, don't
rely on a runtime copy to distinguish them — use withCopyFileToContainer (which the identity
hash does cover) if the file content should be part of what makes two containers "the same".