Every file on Linux belongs to one user and one group. When a web server cannot write to an upload folder, or files copied as root are suddenly read-only for your app, the fix is usually a change of owner rather than looser permissions. This guide covers chown and chgrp, recursive changes, symbolic links and typical web root setups.
Before you start
- A Linux VPS with Ubuntu 22.04 or 24.04, Debian 12, or AlmaLinux or Rocky Linux 8 or 9.
- An SSH session with sudo. Only root can give a file to another user.
- The user and group names you want to use. Check them with
id deployorgetent group www-data.
See the current owner
ls -l /var/www/example.com-rw-r--r-- 1 root root 1024 Nov 9 10:02 index.php
drwxr-xr-x 2 deploy deploy 4096 Nov 9 10:02 uploadsThe third column is the owner and the fourth is the group.
Change the owner
sudo chown deploy index.phpThe group stays as it was.
Change owner and group together
Separate them with a colon:
sudo chown deploy:www-data index.phpVariations:
chown deploy: file: ownerdeploy, group set to deploy's primary group.chown:www-data file: change only the group.
Change only the group with chgrp
chgrp does just the group, and a normal user may use it to move their own file into any group they belong to:
chgrp www-data index.phpChange ownership recursively
-R applies the change to a folder and everything under it:
sudo chown -R deploy:www-data /var/www/example.comWarning: Double-check the path before pressing Enter. A recursive chown on the wrong folder, such as /, /etc or /usr, can break sudo, SSH and system services, and is very hard to undo by hand. GNU chown refuses to work on / itself, but a typo such as an extra space can still point it at the wrong place.
Add -v to list every change, or -c to list only files that actually changed.
How symbolic links are handled
By default, chown on a symlink changes the file the link points to, not the link itself:
sudo chown deploy currentTo change the link itself, use -h:
sudo chown -h deploy currentWith -R, chown does not follow symlinks to folders by default, which is the safe behaviour. It changes the link, not its target. The options -L (follow all links) and -H (follow links given on the command line) change that. Avoid -L unless you are sure where every link leads, because a link to /etc would hand system files to your user.
Copy ownership from another file
To match a known-good file:
sudo chown --reference=index.php new-page.phpThis sets the same owner and group in one step.
Typical web root ownership
A common, safe layout is:
- Files owned by your deploy user, so you can update the site over SSH or SFTP.
- Group set to the web server group, so it can read them.
- Write access for the web server only on folders that need it, such as uploads or cache.
On Ubuntu and Debian the web server runs as www-data. On AlmaLinux and Rocky it is nginx or apache.
sudo chown -R deploy:www-data /var/www/example.com
sudo chmod -R u=rwX,g=rX,o= /var/www/example.com
sudo chmod -R g+w /var/www/example.com/uploadsThe web server can read the whole site and write only to uploads. If the site is compromised, the attacker cannot change your code. App-specific advice, such as for WordPress, is in Set correct web root permissions on Linux.
Change files with a numeric ID
You can use numeric IDs instead of names, which helps after deleting a user or when restoring files from another server:
sudo chown 1001:1001 data.dbTo hand every file of an old ID to a new account:
sudo find /var/www -xdev -uid 1001 -exec chown deploy {} +Check it works
stat -c '%U:%G %n' /var/www/example.com/index.phpdeploy:www-data /var/www/example.com/index.phpThen test from the app's point of view. For example, check the web server can read a file:
sudo -u www-data cat /var/www/example.com/index.php > /dev/null && echo OKTroubleshooting
- "Operation not permitted" without sudo: only root can change a file's owner. Run the command with
sudo. - "invalid user" or "invalid group": the name does not exist. Check the spelling with
getent passwdorgetent group. - Ownership resets after a deploy: the deploy tool extracts files as root or as another user. Run it as the right user, or add a chown step at the end.
- App still cannot write after chown: the folder may lack write permission for the group, or a parent folder lacks
x. Check every level withnamei -l /path/to/folder. - "Operation not permitted" even as root: the file is immutable. Check with
lsattrand remove the flag withchattr -i.
Next steps
- Adjust what each owner can do: chmod: change file permissions in Linux.
- Fix permissions on a whole uploaded tree: Set different permissions for files and folders recursively.
- Solve the errors that send you here: Fix "Permission denied" on Linux.
