A bind mount in Linux makes an existing file or directory appear at another path in the filesystem hierarchy.
The key idea is:
A bind mount creates a second pathname to the same mounted filesystem object/tree, using the kernel’s VFS mount mechanism.
It is not a copy, and unlike a symlink, pathname resolution does not “jump through a link file.” The kernel treats the bind-mounted location as a mount point.
For example:
mkdir -p /mnt/project mount --bind /srv/project /mnt/project
After this:
/srv/project/file.txt /mnt/project/file.txt
refer to the same underlying file. Writing through either path changes the same data.
1. What actually happens
Linux has a Virtual Filesystem layer, or VFS, that presents one unified directory tree:
/ ├── home ├── proc ├── sys ├── srv └── mnt
Normally, mount attaches a filesystem tree somewhere in that hierarchy:
mount /dev/sdb1 /mnt/data
Conceptually:
/dev/sdb1 filesystem | v /mnt/data
A bind mount is different because its source is already part of the VFS namespace:
mount --bind /srv/project /mnt/project
Conceptually:
same filesystem subtree | +------+------+ | | v v /srv/project /mnt/project
There is no duplicated directory tree.
Both paths lead into essentially the same subtree.
2. Bind mount versus symlink
Suppose:
/data/photos/
exists.
You can expose it elsewhere with a symlink:
ln -s /data/photos /home/alice/photos
or with a bind mount:
mount --bind /data/photos /home/alice/photos
They may look similar from a shell:
ls /home/alice/photos
works in both cases.
But internally they are very different.
| Property | Symlink | Bind mount |
| Kernel object | special filesystem file | VFS mount |
| Visible with ls -l | yes, shows -> target | looks like normal dir/file |
| Target pathname stored | yes | no |
| Can become dangling | yes | generally no in the same sense |
| Crosses chroot/container boundaries easily | often problematic | very useful |
| Seen by applications as a symlink | yes | no |
| Can expose directories | yes | yes |
| Can expose individual files | yes | yes |
| Requires mount privilege | normally yes | no |
| Appears in /proc/*/mountinfo | no | yes |
The most important distinction is pathname resolution.
Symlink
With:
ln -s /data/photos /home/alice/photos
the filesystem literally contains a symlink:
/home/alice/photos -> /data/photos
When a process opens:
/home/alice/photos/a.jpg
the kernel encounters the symlink and resolves its target:
/data/photos/a.jpg
You can see this:
ls -ld /home/alice/photos
perhaps:
lrwxrwxrwx ... photos -> /data/photos
Bind mount
With:
mount --bind /data/photos /home/alice/photos
/home/alice/photos is a mount point.
There is no symlink object saying:
-> /data/photos
To an application, /home/alice/photos looks like an ordinary directory.
For example:
ls -ld /home/alice/photos
will generally show something like:
drwxr-xr-x ...
not:
lrwxrwxrwx ...
This difference is extremely important for containers, chroots, sandboxing, and filesystem isolation.
3. Why bind mounts exist
They solve a problem that symlinks cannot solve cleanly:
“Make this existing part of the filesystem appear as a real filesystem subtree somewhere else.”
A classic example is a chroot.
Imagine:
/ ├── home │ └── alice └── jail └── home
You want /home/alice available inside the jail as:
/home/alice
A symlink inside the jail:
/jail/home/alice -> /home/alice
does not necessarily do what you want because after chroot("/jail"), the process's / is now:
/jail
so the absolute symlink:
/home/alice
means:
/jail/home/alice
from the process's perspective.
A bind mount solves this cleanly:
mount --bind /home/alice /jail/home/alice
Inside the chroot:
/home/alice
is now a real mount point.
This same property is heavily used by containers.
4. Bind mounts are central to containers
For example, with Docker:
docker run \ --mount type=bind,src=/home/alice/project,dst=/app \ ubuntu
Conceptually the container gets:
host: /home/alice/project | | bind mounted v container: /app
Inside the container:
cat /app/config.yaml
is accessing:
/home/alice/project/config.yaml
on the host.
A symlink would usually not work because the container may not even have the host path /home/alice/project in its filesystem namespace.
5. A bind mount does not copy anything
Suppose:
mkdir /source /target echo hello > /source/a.txt mount --bind /source /target
Now:
cat /target/a.txt
prints:
hello
Then:
echo world > /target/a.txt
and:
cat /source/a.txt
prints:
world
The file was not synchronized or duplicated.
There is one underlying inode/data object being reached through two paths.
Conceptually:
/source/a.txt ------+ | +---- inode 1234 ---- file contents | /target/a.txt ------+
This diagram is somewhat simplified because bind mounts operate on mount/dentry trees rather than creating another hard link to every inode, but it captures the observable result.
6. What happens to the original contents of the destination?
This is an important gotcha.
Suppose:
/target/ └── old.txt
Then:
mount --bind /source /target
Afterward:
ls /target
shows the contents of /source.
old.txt hasn't been deleted.
It is simply hidden underneath the mount.
Conceptually:
Before:
/target └── old.txt
After:
/source subtree | v /target mountpoint -------- | old.txt hidden
If you unmount:
umount /target
then old.txt becomes visible again.
The same behavior occurs with ordinary filesystem mounts.
7. Bind mounting individual files
Bind mounts are not limited to directories.
For example:
touch /tmp/my-hosts mount --bind /etc/hosts /tmp/my-hosts
Now:
/tmp/my-hosts
refers to /etc/hosts.
Containers use this pattern frequently for configuration files.
One warning: the destination must generally already exist and have the appropriate type.
For a file:
touch /target/file mount --bind /source/file /target/file
For a directory:
mkdir /target/dir mount --bind /source/dir /target/dir
8. How the kernel sees it
At a more technical level, Linux pathname lookup involves things like:
mount namespace | +-- mount | +-- filesystem superblock | +-- dentry | +-- inode
A bind mount creates another mount attachment referencing an existing filesystem subtree.
Very approximately:
mount A: path /srv/project | v dentry X | v inode/tree mount B: path /mnt/project | v same subtree rooted at dentry X
So bind mounts are fundamentally part of the mount namespace machinery, not the filesystem's directory-entry mechanism used for symlinks.
That distinction explains many of their special properties.
9. Mount namespaces
Bind mounts become especially powerful when combined with mount namespaces.
Different processes can have different views of mounts.
For example:
Host mount namespace: / ├── home ├── srv └── data Container mount namespace: / ├── app <-- bind mounted from host /srv/myapp ├── proc └── tmp
The host might see:
/srv/myapp
while the container sees the same files as:
/app
The container does not have to know the original host pathname.
This is one of the foundations of Linux containers.
Relevant system call:
unshare(CLONE_NEWNS)
or shell:
unshare --mount
creates a new mount namespace.
10. mount --bind vs mount --rbind
There is an important subtlety involving nested mounts.
Suppose:
/source ├── dir └── nested ^ | separate filesystem mounted here
For example:
mount /dev/sdb1 /source/nested
Then:
mount --bind /source /target
binds /source, but nested mount handling can be significant depending on what you expect.
Linux also provides:
mount --rbind /source /target
where rbind means recursive bind.
It recursively replicates the mount structure beneath the source.
Conceptually:
--bind /source | +-- nested mount | v /target
versus:
--rbind /source | +-- nested mount | v /target | +-- nested mount
--rbind is frequently used when constructing container or chroot filesystem trees.
For example:
mount --rbind /dev /chroot/dev
11. Making a bind mount read-only
This is another slightly surprising area.
Traditionally, one often does:
mount --bind /source /target mount -o remount,bind,ro /target
The first command creates the bind mount.
The second changes the bind mount to read-only.
Then:
/source
may remain writable while:
/target
is read-only.
This demonstrates an important concept:
Some mount properties belong to the mount, not to the underlying filesystem object.
So two paths to the same filesystem tree can have different mount-level restrictions.
Modern mount tooling can also express this more directly depending on util-linux/kernel capabilities.
12. Bind mount versus a hard link
It is also useful to distinguish bind mounts from hard links.
Hard link:
ln file1 file2
Conceptually:
file1 ---+ | +---- inode | file2 ---+
Both directory entries directly reference the same inode.
But hard links have constraints:
- normally cannot hard-link directories;
- cannot cross filesystem boundaries;
- operate at the inode/directory-entry level.
A bind mount:
mount --bind /dir1 /dir2
works at the VFS mount level and can expose entire directory trees.
So:
hard link: directory entries -> inode symlink: directory entry -> stored pathname -> another lookup bind mount: mount point -> existing VFS subtree
That is perhaps the most useful mental model.
13. An interesting consequence: ..
Suppose:
mount --bind /srv/project /mnt/project
and you do:
cd /mnt/project cd ..
You get:
/mnt
not:
/srv
Why?
Because you are traversing the mounted tree through the /mnt/project mount point.
This highlights an important fact:
/srv/project
and:
/mnt/project
contain the same mounted subtree, but their placement in the namespace is different.
It is not simply textual path aliasing.
14. How to see bind mounts
mount can show them, but on Linux the most precise source usually is:
cat /proc/self/mountinfo
or:
findmnt
For example:
findmnt /mnt/project
You might see something like:
TARGET SOURCE FSTYPE OPTIONS /mnt/project /dev/nvme0n1p2[...] ext4 rw,relatime
The representation can initially look strange because the kernel describes the underlying mount/filesystem plus the subtree being exposed.
To inspect mount relationships:
findmnt -R /
is particularly useful.
15. Unmounting does not delete anything
If you do:
umount /mnt/project
the bind relationship disappears.
The original:
/srv/project
remains completely intact.
Again:
bind mount ≠ copy bind mount ≠ move bind mount ≠ link file
It is a VFS namespace operation.
16. Persistent bind mounts
You can put them in /etc/fstab.
For example:
/srv/project /mnt/project none bind 0 0
Then it can be mounted normally during boot:
mount /mnt/project
or as part of:
mount -a
17. Why not just use a symlink?
For ordinary user-level directory organization, a symlink is often simpler:
ln -s ~/projects ~/work
Use a bind mount when the distinction matters to the filesystem namespace.
Typical cases include:
- containers;
- chroots;
- exposing selected host directories into sandboxes;
- changing filesystem layout without changing applications;
- making the same directory visible at multiple mount locations;
- applying mount-level restrictions such as read-only exposure;
- constructing custom mount namespaces;
- presenting a filesystem tree where symlinks would escape or fail because the target pathname is unavailable.
For normal convenience:
"I want ~/foo to point at ~/bar"
a symlink is usually better.
For filesystem isolation:
"I want this process to see /srv/app as /app"
a bind mount is usually the right abstraction.
18. A practical container-style example
Imagine your host:
/home/me/project/ ├── app.py ├── config.yaml └── data/
Create an isolated mount namespace:
sudo unshare --mount /bin/bash
Then:
mkdir -p /tmp/root/app mount --bind /home/me/project /tmp/root/app
The VFS now contains:
/ ├── home │ └── me │ └── project │ ├── app.py │ └── config.yaml │ └── tmp └── root └── app ├── app.py └── config.yaml
But the bottom files are not copies.
It's essentially:
project filesystem subtree / \ / \ v v /home/me/project /tmp/root/app
This mechanism is very close to what container runtimes build upon, combined with namespaces, pivot_root, cgroups, capabilities, etc.
The compact mental model
Think of the three mechanisms like this:
SYMLINK ======= /a/link | | contains string "/b/dir" v /b/dir Path indirection. HARD LINK ========= /a/file ----+ +---- inode /b/file ----+ Inode aliasing. BIND MOUNT ========== filesystem subtree ^ / \ / \ /a/dir /b/dir VFS namespace aliasing.
The deepest distinction is:
A symlink says:
“When someone accesses me, resolve this other pathname.”
A hard link says:
“I am another directory entry for this inode.”
A bind mount says:
“Attach this already-mounted filesystem subtree at another place in this mount namespace.”
That last definition is the one to keep in mind when reasoning about Linux containers, chroot, Kubernetes volume mounts, Docker --mount type=bind, and mount namespaces.