Skip to Content

Linux Bind Mounts Explained: One Directory, Two Paths

How bind mounts expose the same files in different locations—and why they matter for containers, chroots, and filesystem isolation.

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.

PropertySymlinkBind mount
Kernel objectspecial filesystem fileVFS mount
Visible with ls -lyes, shows -> targetlooks like normal dir/file
Target pathname storedyesno
Can become danglingyesgenerally no in the same sense
Crosses chroot/container boundaries easilyoften problematicvery useful
Seen by applications as a symlinkyesno
Can expose directoriesyesyes
Can expose individual filesyesyes
Requires mount privilegenormally yesno
Appears in /proc/*/mountinfonoyes

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.

Proton Mail Bridge on KDE Plasma
How a Proton VPN package dependency caused a keyring conflict—and how to fix it

To install this Web App in your iPhone/iPad press and then Add to Home Screen.