Docker Bind Mount Is Empty: Why Image Files Do Not Copy to the Host
You bind-mount an empty host folder to edit the image’s config, and the files inside the container disappear. Where did they go, and how do you get a copy onto the host?

- A bind mount exposes the host path at the container destination, hiding files already at that destination.
- Automatic initial copying applies to an empty Docker volume by default, not an empty bind-mounted host directory.
- Inspect the actual mount, resolve the Compose source path, then copy image defaults separately if you need them on the host.
Why the host directory stays empty
Mounting an empty host directory over an image's populated directory does not export the image files. The container sees the host directory at that destination, so the files previously visible there are obscured. The mount itself has not deleted those files from the image.
A January 2024 Docker forum question describes the confusing part well: an empty host directory, /tmp/myapp, was mounted at /app. Files already in the image did not appear on the host, but files moved into that path afterward inside the container did. Those files went into the mounted host directory; the initial image contents had never been copied there.
That matches Docker's documented behavior for mounting over existing data. A bind mount supplies the host's contents at the destination. It does not combine both directories or continuously synchronize two independent copies. Mounting over all of /app can therefore hide more than your intended configuration file, including other files the image placed beneath that directory.
What an empty volume does differently
A Docker-managed volume has a different initialization rule. When an empty volume is mounted over a directory containing files, Docker copies those existing files into the volume by default. The volume documentation also provides volume-nocopy to skip that initial copying.
| Mounted storage | What becomes visible | Initial image files |
|---|---|---|
| Empty host directory, bind mount | The empty host directory. | Hidden; not automatically exported. |
| Populated host directory, bind mount | The host directory's contents. | Hidden rather than combined with host files. |
| Empty Docker volume | Existing destination files copied into it by default. | Used to populate the volume unless copying is disabled. |
| Populated Docker volume | The volume's existing contents. | Hidden by the mounted volume. |
The empty-volume condition also explains an upgrade surprise. A volume that already contains configuration is no longer empty when attached to a newer image. Initial population is therefore not a mechanism for refreshing that configuration on each image update. Incorporate new settings through the application's upgrade procedure while preserving the values already in use.
Bind mounts suit source files or configuration you want to edit directly from the host. Volumes suit data generated and managed by containers without tying the application to a particular host directory layout.
Find the source the container is actually using
Inspect an existing container before changing its storage configuration. Replace app with its actual name or ID. This command reads its mount information without restarting it.
docker inspect app --format '{{json .Mounts}}' For a bind mount, Source is the host path and Destination is where it appears inside the container. Type distinguishes bind mounts from volumes. RW reports whether the mount is writable, although normal filesystem permissions can still prevent a process from writing.
If Source is /srv/demo/config and Destination is /app/config, editing /home/user/config will not change what that mount supplies. If Type is volume instead of bind, inspect the volume name and the configuration that created the container: it is not using the host-directory arrangement you expected.
The source belongs to the Docker daemon's host. With a remote daemon, that means the remote server, not the laptop where you typed the command. Docker Desktop has mechanisms for sharing local files with its VM, but an arbitrary remote Docker engine cannot read your laptop's path merely because you passed its name.
Stop a mistyped file path from becoming a directory
Missing sources behave differently across syntaxes. Docker's -v bind syntax creates a missing source as a directory, even when you intended a file. A bind mount declared with --mount instead errors on a missing source by default. That distinction assumes you have not enabled a separate source-creation option.
Compose's short volume syntax also creates missing host directories for compatibility. Its long syntax can disable that behavior using bind.create_host_path: false. This makes a misspelled source fail instead of silently creating an empty directory that can later conflict with a file destination.
The example below mounts one existing configuration file. Replace the image and target with the application's actual image version and configuration path. For a local Docker engine using this single Compose file, the relative source is resolved from the Compose file's parent directory; create the correct file there before starting the service. This relative-path example is not intended for deployment to a non-local platform.
services:
app:
image: my-app:1.0
volumes:
- type: bind
source: ./config/settings.yml
target: /app/config/settings.yml
read_only: true
bind:
create_host_path: false Mounting only the file leaves the surrounding image directory available. Read-only mode prevents the container from changing the file through this mount. If the application writes back to its configuration at startup, this example needs adapting to its supported configuration and data paths rather than assuming every application accepts read-only settings.
Render the effective Compose model to see resolved paths and variable substitutions. If you use explicit -f files to run the service, supply the same files for this inspection.
docker compose config Compose config merges and resolves the configuration; it does not update an existing container. Compare its source and target with the inspected Mounts. A changed Compose file and an older container can describe different mounts.
Successful configuration rendering also does not establish that the intended host file exists or contains the settings the application needs. Inspect that source on the daemon host before recreating the service. The output may also include resolved environment values, so share only the necessary mount excerpt when asking for help.
Copy image defaults without replacing the running container
If your goal is to put the image's default configuration on the host, copy it explicitly. Create a separate inspection container from the same image without the troublesome bind mount, and copy into a new local directory. Leave the existing application container available while collecting and comparing the files.
The following assumes the image is available locally and contains /app/config. Substitute the real image version and path. Choose unused names for both the inspection container and destination directory, and continue only after each command succeeds.
docker create --name image-files-check my-app:1.0
mkdir image-defaults
docker cp image-files-check:/app/config/. ./image-defaults/ Docker create makes a container without starting its process. Consequently, files produced only by an entrypoint or first-run application setup will not be generated by this procedure. It retrieves files already provided by the image, not every file a running installation might eventually contain.
Docker cp works with stopped containers. The source suffix /. copies the directory's contents into the destination instead of adding another config directory beneath it. A fresh destination avoids overwriting an existing host configuration with same-named defaults. Inspect the resulting files before using them.
Adapt the copied settings to your installation, then place the intended file at the host source declared in Compose. Use your existing service procedure to recreate the application container with that mount. Preserve any needed changes that exist only in the old container first; a fresh inspection container cannot supply those changes.
Once the copied files are secured, remove only the inspection container created above:
docker rm -v image-files-check The -v option also removes attached anonymous volumes, including any created for this inspection container; named volumes are not removed by that option. Use the new inspection name, not the existing application's container name.
Where subsequent writes will be found
After mounting, the application writes into the attached storage at that path. Reusing the same bind source or volume lets a replacement container see those files. Attaching a different empty directory or another volume name supplies different contents, so an apparently fresh application is a reason to compare the old and new mount identities.
Volume contents exist outside an individual container’s lifecycle, whereas files in its writable layer are lost when that container is removed. Before replacing a container, locate the files you intend to keep and preserve any that are not already in the mounted storage. Extracting image defaults again will not bring back documents or configuration edits created after the application started.