chown and chgrp: Changing Ownership

Every file and directory on a Linux system belongs to exactly one user (the owner) and one group. That ownership determines whose permission bits apply when someone tries to read, write, or execute the file. chown changes a file’s owner (and optionally its group) in one command, while chgrp changes only the group. Getting ownership right is essential for web servers, shared project directories, deployment scripts, and any system where more than one account touches the same files.

Overview: How Ownership Works

Internally, the kernel does not store ownership as text like root or www-data. Every file’s inode stores two numbers: a UID (user ID) and a GID (group ID). The files /etc/passwd and /etc/group map those numbers to human-readable names. When you run ls -l, the ls command looks up the UID and GID for each file and prints the matching names — but the raw filesystem data is just two integers. This is why you can delete a user’s account, leave their files behind, and later see a bare number instead of a name in ls -l output: the name-to-number mapping is gone, but the number on disk never changed.

Ownership matters because Linux permission bits are split into three sets: owner, group, and other (see the earlier lesson on permission bits). Which set applies to a given process depends entirely on whether that process’s user matches the file’s owner UID, whether it belongs to the file’s group GID, or neither. Change the owner or group, and you change which permission bits govern who can do what — without touching the rwx bits themselves.

Changing a file’s owner is a privileged operation: only root (or a process with the CAP_CHOWN capability) can do it, which is why every chown example that changes ownership needs sudo. This restriction exists so that an ordinary user cannot hand off a file they own to another account — if that were allowed, a user could dodge disk quotas by giving their large files to someone else, or trick another user into owning (and being blamed for) malicious content. Changing a file’s group, however, is less restricted: the file’s current owner can change its group to any group they themselves belong to, without sudo. In practice, most system administration is done as root via sudo anyway, so you’ll see sudo in front of nearly every chown and chgrp example.

Syntax

The general forms are:

chown [OPTION]... [OWNER][:[GROUP]] FILE...
chgrp [OPTION]... GROUP FILE...

For chown, the OWNER:GROUP argument is flexible:

  • chown alice file — changes only the owner to alice, leaves the group untouched.
  • chown alice:staff file — changes the owner to alice and the group to staff in one step.
  • chown :staff file — changes only the group (the leading colon with no owner before it), equivalent to chgrp staff file.
  • chown alice: file — changes the owner to alice and sets the group to alice‘s primary group.

Common options for both commands:

Option Meaning
-R, --recursive Apply the change to a directory and everything inside it, recursively.
-v, --verbose Print a line for every file whose ownership is changed (or attempted).
-c, --changes Like -v, but only report files that actually changed.
-h, --no-dereference Change the symbolic link itself, not the file it points to (the default follows the link).
--reference=RFILE Copy the owner (and group, for chown) from an existing file RFILE instead of naming one directly.
--from=OWNER:GROUP (chown only) Only change files currently owned by the given owner/group — useful as a safety check before a bulk change.

Examples

Example 1: Changing the owner of a single file

A file was created as root but needs to be served by the www-data user that runs the web server:

ls -l /var/www/html/index.html
sudo chown www-data /var/www/html/index.html
ls -l /var/www/html/index.html

Output:

-rw-r--r-- 1 root     root 512 Aug  3 10:15 /var/www/html/index.html
-rw-r--r-- 1 www-data root 512 Aug  3 10:15 /var/www/html/index.html

The first ls -l shows the file owned by root. After sudo chown www-data ..., the second ls -l shows the owner field changed to www-data. The group (root) was untouched because we didn’t specify one.

Example 2: Changing owner and group together

Now set both the owner and the group to www-data in a single command, using the colon form:

sudo chown www-data:www-data /var/www/html/index.html
ls -l /var/www/html/index.html

Output:

-rw-r--r-- 1 www-data www-data 512 Aug  3 10:15 /var/www/html/index.html

Both the owner and group columns now read www-data. This is the standard pattern for handing a file to a service account that should fully control it.

Example 3: Recursive ownership change on a deployment directory

A deploy script just unpacked a release as root, but the whole site should belong to a deploy user while staying readable by the www-data group:

sudo chown -Rv deploy:www-data /var/www/html

Output:

changed ownership of '/var/www/html/index.html' from root:root to deploy:www-data
changed ownership of '/var/www/html/assets' from root:root to deploy:www-data
changed ownership of '/var/www/html/assets/style.css' from root:root to deploy:www-data

The -R flag walks into every subdirectory and file under /var/www/html, applying the same owner and group to each one. The -v flag makes chown report every change it makes, which is invaluable for confirming a recursive change touched what you expected before you move on.

Example 4: Sharing a directory with a group using chgrp

Several developers need write access to a shared project directory, so it’s handed to a developers group instead of a single user:

sudo chgrp -R developers /srv/projects/app
ls -ld /srv/projects/app

Output:

drwxrwxr-x 6 root developers 4096 Aug  4 09:00 /srv/projects/app

chgrp only ever touches the group field, leaving the owner (root) alone. Combined with group write permission (rwx for group, as shown here), every member of developers can now read, write, and enter this directory.

How It Works Step by Step

When you run sudo chown deploy:www-data /var/www/html/index.html, several things happen in sequence:

  1. The shell parses the command line and finds the sudo program, which checks whether your user is allowed to run commands as root (via /etc/sudoers), prompts for your password if needed, and then executes chown with root privileges.
  2. chown resolves the names deploy and www-data to numeric UID and GID values by reading /etc/passwd and /etc/group (or whatever name service is configured via NSS).
  3. chown calls the chown(2) system call, passing the target path and the resolved UID/GID. The kernel checks that the calling process has the CAP_CHOWN capability (which root has), then updates the UID and GID fields stored in the file’s inode.
  4. On a journaling filesystem like ext4, this metadata change is written to the journal before being committed to the main filesystem structures, so an unexpected power loss mid-write won’t corrupt the ownership data.
  5. The next time any program (like ls) reads that file’s metadata, it sees the new UID/GID and again performs a name lookup through NSS to display deploy and www-data instead of raw numbers.

With -R, chown repeats steps 2–3 once for every file and directory it finds while recursively walking the directory tree, which is why a recursive chown on a large tree can take a noticeable amount of time — it is issuing one system call per file, not one call for the whole tree.

Common Mistakes

Mistake 1: Forgetting sudo

Changing an owner is a privileged operation. Without sudo, it fails outright:

$ chown www-data /var/www/html/index.html
chown: changing ownership of '/var/www/html/index.html': Operation not permitted

The fix is simply to run it with elevated privileges:

sudo chown www-data /var/www/html/index.html

Mistake 2: Forgetting the colon when you only mean the group

It’s tempting to write chown developers file when you actually want to change the group. But without a leading colon, chown reads developers as the new owner, not the group — and if no user named developers exists, it fails outright:

$ chown developers /srv/projects/app
chown: invalid user: 'developers'

Use a leading colon to target the group only, or use chgrp, which makes the intent explicit:

sudo chown :developers /srv/projects/app

Mistake 3: Recursive chown on the wrong path

Because -R silently walks every file under the path you give it, a typo in the path can rewrite ownership across the entire filesystem. Running the following as root is destructive and can leave a system unbootable, since core system files would end up owned by the wrong account:

sudo chown -R www-data:www-data /

Always double-check the path before adding -R, and scope it as narrowly as possible:

sudo chown -R www-data:www-data /var/www/html

Best Practices

  • Use -v or -c with recursive changes so you have a record of exactly what was modified, especially when scripting deployments.
  • Prefer the colon form (owner:group) over the older dot form (owner.group); the colon is unambiguous even if a username happens to contain a dot.
  • Use --from=OWNER:GROUP as a safety check when bulk-changing ownership, so files that don’t match your expectation are left alone instead of silently rewritten.
  • Prefer service accounts (like www-data or a dedicated deploy user) over personal accounts for files served by long-running processes, so ownership doesn’t depend on any one person’s account existing.
  • Double-check the target path before combining chown with -R — there is no undo, and a wrong path can affect far more files than intended.
  • Use group ownership (chgrp or chown :group) plus group permission bits to share access among a team, rather than making files world-writable.

Practice Exercises

  1. Create a file with touch ~/notes.txt. Check its owner and group with ls -l, then use chgrp to change its group to a group you belong to (see your groups with the groups command). Confirm the change with ls -l again.
  2. Imagine a CI pipeline just deployed a release to /opt/myapp while running as root, but the app should run as a dedicated myapp service user with group myapp. Write the single recursive chown command that fixes ownership for the whole directory tree, with verbose output enabled.
  3. You need to let everyone in the design group edit files under /srv/shared/assets without becoming the owner of each file. Work out which command(s) set the group to design and which permission bits (from the earlier chmod lesson) you’d also need so group members can write to it.

Summary

  • Every file has a numeric owner (UID) and group (GID); names shown by ls -l are just a lookup through /etc/passwd and /etc/group.
  • chown changes a file’s owner and, optionally, its group in one command using the owner:group syntax.
  • chgrp changes only the group, and is equivalent to chown :group.
  • Changing an owner requires root privileges (sudo); changing a group only requires that you own the file and belong to the target group.
  • -R applies the change recursively through a directory tree — always verify the path first, since there’s no undo.
  • -v/-c report exactly what changed, which is essential for auditing bulk ownership changes in scripts.