chown -R user:group /path changes ownership of a directory and everything beneath it, and the reason it deserves care is that Unix keeps no record of what the ownership was before, so there is no undo.
That single fact shapes everything else. A recursive chown against the wrong path is not an error you correct; it is a state you reconstruct, and reconstruction on a system directory can be genuinely difficult.
So this covers the normal safe usage, the two traps that make it dangerous when you did not expect it to be, and what recovery actually looks like.
Table of contents
- The normal case
- The two traps
- Recording the state before you change it
- When it has already gone wrong
- Reducing how often you need this at all
- How this fits the rest of the stack
- FAQ
The normal case
The everyday use is handing a deployment directory to the user that runs the application.
# Owner and group together.
sudo chown -R deploy:www-data /var/www/example.com
# Owner only, leaving the group as it is.
sudo chown -R deploy /var/www/example.com
# Group only. Note the leading colon.
sudo chown -R :www-data /var/www/example.com
# Show what changed, which is worth doing on anything unfamiliar.
sudo chown -Rc deploy:www-data /var/www/example.com
The -c flag prints only the files it actually changed, which is a cheap way to confirm the scope was what you intended. On a large tree it also gives you a record.
Ownership is one half; the permission bits are the other. The conventional pairing for a web directory:
# Directories need execute to be traversable; files do not.
sudo find /var/www/example.com -type d -exec chmod 755 {} +
sudo find /var/www/example.com -type f -exec chmod 644 {} +
Use find with a type filter rather than chmod -R 755, which sets the execute bit on every file including ones that should not have it.
The two traps
Recursive chown is safe until the tree contains something that is not a plain file or directory, and then it is not.
Symbolic links. By default chown -R does not follow symlinks, which is the safe behaviour. But -L makes it follow them, and a symlink pointing outside your tree means the command escapes into a directory you did not intend to touch. A link to /etc inside a directory you are chowning is exactly how a routine command becomes an incident.
The related flag worth knowing is -h, which changes the ownership of the link itself rather than its target. On a tree containing links, that is usually what you want.
Mount points. A recursive chown descends into anything mounted beneath your path. A network share, a bind mount, or a data volume mounted inside a directory you are chowning all get rewritten. Use -x to stay on one filesystem:
# Do not cross filesystem boundaries.
sudo chown -Rx deploy:www-data /var/www
Before running anything recursive against an unfamiliar path, look at what is actually in it:
# Any symlinks, and where do they point?
find /var/www/example.com -type l -exec ls -l {} +
# Any mount points beneath here?
findmnt --submounts --target /var/www/example.com
Recording the state before you change it
This is the habit that turns an unrecoverable mistake into a two-minute restore, and it takes one command.
# Capture current ownership and permissions for the whole tree.
find /var/www/example.com -printf '%U:%G %m %p\n' > /root/ownership-backup.txt
# Or use getfacl, which also captures access control lists.
getfacl -R /var/www/example.com > /root/acl-backup.txt
Restoring from the getfacl output is a single command, which is the reason to prefer it:
setfacl --restore=/root/acl-backup.txt
It is worth making this reflexive before any recursive ownership or permission change on a system you care about. The cost is one command and a few kilobytes; the alternative is reconstructing state from memory.
When it has already gone wrong
If a recursive chown has hit a system path, the symptoms are immediate and varied: services refusing to start, sudo complaining, SSH rejecting keys, package managers failing.
Recovery depends on what was affected.
- If you captured state beforehand, restore it with setfacl and you are done.
- If the affected paths came from packages, the package manager knows the correct ownership and can reset it. On Debian and Ubuntu, reinstalling the affected packages restores their file ownership. On RHEL-family systems,
rpm --setugidsresets ownership for a package without reinstalling, andrpm --setpermsresets the modes. - For home directories, the correct ownership is the user, so a targeted chown per home directory fixes it.
- For anything remaining, a machine of the same distribution and version is your reference. Compare and correct.
# RHEL family: reset ownership and permissions for a package's files.
sudo rpm --setugids openssh-server
sudo rpm --setperms openssh-server
# Find files whose ownership does not match their package (Debian family).
# dpkg does not store ownership, so a reinstall is the practical route:
sudo apt-get install --reinstall openssh-server
Two specific things to check early because they lock you out: SSH refuses keys if ~/.ssh or authorized_keys are group or world writable, and sudo refuses to run at all if /etc/sudoers is not owned by root. If you still have an open root session, do not close it until both are verified.
Reducing how often you need this at all
Most recursive chown usage is a symptom of ownership drifting, and the drift usually has a cause worth fixing.
- Deploy as the right user. If your deploy process runs as root and the application runs as another user, you will chown after every deploy forever. Run the deploy as the application user instead.
- Set the group sticky bit on shared directories so new files inherit the directory’s group automatically:
chmod g+s /path. - Set a sensible umask in the deploy user’s environment so new files are created with the permissions you want rather than corrected afterwards.
- Keep uploads out of the code tree. Writable directories inside a directory that should be read-only is what creates the mixed-ownership mess in the first place.
The broader point: file ownership is a property of a long-lived server that accumulates history. On a platform where each deploy produces a fresh artefact from a repository, there is no accumulated ownership state to correct, because the filesystem is built the same way every time. The GitHub deployment docs cover that shape on RunxBuild.
How this fits the rest of the stack
Server maintenance of this kind is not difficult, it is just permanent, and the honest way to compare a self-managed setup against a managed one is to include the recurring hours rather than only the instance price. The RunxBuild hosting calculator prices the managed side, where the filesystem is rebuilt from the repository on each deploy and ownership drift has nowhere to accumulate.
Useful related references:
- chown Command in Linux: Change Owner, Group, and Recursive
- How to Change File Permissions Linux: chmod, chown, and umask
- Change the Owner of a Directory in Linux: chown, -R, and the Mistakes That Hurt
- Services on RunxBuild
FAQ
How do I change ownership of a directory and everything inside it?
Use chown with the -R flag: chown -R user:group /path. Add -c to print only the files that actually changed, which is a useful confirmation that the scope was what you intended before you trust it on a larger tree.
Can I undo a recursive chown?
Not directly, because Unix keeps no record of previous ownership. Recovery means reconstructing state: from a getfacl backup if you made one, from the package manager for package-owned files, or by comparison with an identical system. Capturing ownership with getfacl before the change is the two-second habit that avoids all of it.
Does chown -R follow symbolic links?
Not by default, which is the safe behaviour. The -L flag makes it follow them, and a link pointing outside your tree will then take the command with it. Use -h to change the ownership of links themselves rather than their targets when the tree contains links.
How do I stop chown from crossing into mounted filesystems?
Use the -x flag, which restricts the operation to a single filesystem. Without it, a recursive chown descends into any network share, bind mount or data volume mounted beneath the target path. Check with findmnt —submounts before running anything recursive on an unfamiliar path.
What permissions should web files have?
Conventionally 755 for directories and 644 for files, owned by your deploy user with the web server’s group able to read. Use find with a type filter to set them separately, since chmod -R 755 would mark every file executable. Avoid 777 entirely; it makes files writable by any process on the machine.