Group ownership is one of those Linux permission concepts that seems simple until you’re troubleshooting why a whole team can’t write to a shared directory, and it turns out one nested file somewhere got created with the wrong group. chgrp is the tool for fixing exactly that, and once you understand how Linux’s group permission model actually works, it becomes a precise, predictable instrument rather than a command you run and hope for the best.
Understanding Group Ownership First
Every file and directory on a Linux system has both a user owner and a group owner. Permissions are evaluated in three tiers — owner, group, and others — and the group tier is what lets you grant a specific set of users shared access to a file without opening it up to everyone on the system. chgrp (change group) is the dedicated tool for changing which group owns a given file or directory, separate from chown, which changes the user owner (and can optionally change the group too, in the same command).
I confirmed the exact option set on my system:
chgrp --help
Usage: chgrp [OPTION]... GROUP FILE...
or: chgrp [OPTION]... --reference=RFILE FILE...
Change the group of each FILE to GROUP.
With --reference, change the group of each FILE to that of RFILE.
-c, --changes like verbose but report only when a change is made
-f, --silent, --quiet suppress most error messages
-v, --verbose output a diagnostic for every file processed
--dereference affect the referent of each symbolic link (this is
the default), rather than the symbolic link itself
-h, --no-dereference affect symbolic links instead of any referenced file
--no-preserve-root do not treat '/' specially (the default)
--preserve-root fail to operate recursively on '/'
--reference=RFILE use RFILE's group rather than specifying a GROUP.
-R, --recursive operate on files and directories recursively
Basic Syntax
chgrp [options] GROUP FILE...
Basic Usage
sudo chgrp developers /srv/project/
This changes the group owner of /srv/project/ to the developers group. I verified this works exactly as expected on a test file:
sudo chgrp testgroup1 /tmp/testfile.txt
ls -l /tmp/testfile.txt
-rw-r--r-- 1 root testgroup1 0 Jul 31 01:38 /tmp/testfile.txt
You can see the group column changed from whatever it was originally to testgroup1, confirming the operation applied correctly.
Who Can Change Group Ownership
This trips people up sometimes: unlike chown (which requires root to change user ownership), a regular user can change a file’s group ownership with chgrp, but only to a group they themselves are a member of, and only for files they own. Root, of course, can change any file’s group to any group regardless of membership.
chgrp mygroup myfile.txt
If mygroup isn’t a group you belong to, you’ll get:
chgrp: changing group of 'myfile.txt': Operation not permitted
Recursive Group Changes
For directory trees, applying group ownership recursively to every file and subdirectory inside:
sudo chgrp -R developers /srv/project/
This is one of the most common real-world uses — setting up a shared project directory where an entire team needs group-level access, and every existing file inside needs to be brought in line with that group, not just the top-level directory.
Verbose and Change-Reporting Modes
sudo chgrp -v developers /srv/project/*.txt
-v prints a line for every file processed, whether or not the group actually changed:
changed group of 'file1.txt' from root to developers
changed group of 'file2.txt' from root to developers
sudo chgrp -c developers /srv/project/*.txt
-c (--changes) is quieter — it only reports files where the group ownership actually changed, silently skipping files that already had the correct group. I prefer -c for routine maintenance scripts, since it gives a clean audit trail of what was actually modified rather than a noisy line-per-file regardless of outcome.
Suppressing Errors
sudo chgrp -f nonexistentgroup *.txt
-f (--silent/--quiet) suppresses most error messages, useful in scripts where you’re deliberately trying an operation that might legitimately fail on some files (like permission-restricted ones) and don’t want that to halt or clutter output — though I use this cautiously, since silently swallowed errors can also hide genuine problems you’d want to know about.
Using –reference to Match Another File’s Group
Instead of specifying a group name directly, you can copy the group ownership from an existing reference file:
sudo chgrp --reference=/srv/project/reference-file.txt /srv/project/new-file.txt
I use this pattern in deployment scripts constantly — rather than hardcoding a group name that might differ between environments, I match new files to whatever group an existing, correctly-configured file already has.
Symbolic Link Behavior
By default, chgrp follows symbolic links and changes the group of the file the link points to, not the link itself:
chgrp developers mysymlink
If you specifically want to change the group of the symlink itself, rather than its target:
chgrp -h developers mysymlink
-h (--no-dereference) is the flag for this. This distinction matters more than it might seem — on most Linux filesystems, symlinks themselves don’t really have meaningful independent permissions in practice (the target’s permissions are what’s actually enforced), but the ownership metadata on the link itself still exists and can matter for certain security auditing tools or specific filesystem behaviors.
Protecting Against Accidental Root-Level Recursion
chgrp --preserve-root -R developers /
--preserve-root (the default behavior, actually) refuses to operate recursively on / itself, as a safety net against a catastrophic typo. You’d have to explicitly pass --no-preserve-root to override this protection, which I’ve genuinely never had a legitimate reason to do — if you find yourself needing it, it’s worth pausing and double-checking the command you’re about to run.
Combining with find for Selective Recursive Changes
Sometimes you don’t want to blanket-apply group ownership to an entire tree, but only to specific file types or patterns within it:
find /srv/project -type f -name "*.log" -exec chgrp appgroup {} +
This changes group ownership only for .log files throughout the tree, leaving everything else untouched. I use this pattern regularly when a directory has mixed ownership requirements — application logs owned by one group, source files by another, for instance.
Real-World Use Case: Setting Up a Shared Team Directory
Here’s a workflow I actually use when setting up a new shared project space for a team:
# 1. Create the group if it doesn't already exist
sudo groupadd projectteam
# 2. Add team members to the group
sudo usermod -aG projectteam alice
sudo usermod -aG projectteam bob
# 3. Create the shared directory
sudo mkdir -p /srv/shared/projectx
# 4. Set group ownership recursively
sudo chgrp -R projectteam /srv/shared/projectx
# 5. Set group permissions and the setgid bit so new files inherit the group automatically
sudo chmod -R g+rwX /srv/shared/projectx
sudo chmod g+s /srv/shared/projectx
That last step, setting the setgid bit (g+s) on the directory, is what makes this setup actually maintainable long-term — without it, every new file created inside the directory inherits the creating user’s primary group by default, not projectteam, which means you’d be running chgrp -R repeatedly forever to keep things consistent. With setgid set on the directory, new files and subdirectories automatically inherit the directory’s group, solving the problem at its root.
chgrp vs chown
chown can do everything chgrp does and more — it changes user ownership, and optionally group ownership in the same command:
sudo chown alice:developers myfile.txt
This changes both user owner (to alice) and group owner (to developers) in one call, equivalent to running chown alice myfile.txt followed by chgrp developers myfile.txt separately. In practice, I use chown user:group when I need to set both at once during initial provisioning, and reach for standalone chgrp specifically when I only need to touch group ownership without risking any accidental change to user ownership — it’s a more precise, single-purpose tool for that narrower case, and its more limited scope makes intent clearer when reading back through shell history or scripts later.
Troubleshooting
“Operation not permitted” as a non-root user — you’re either not a member of the target group, or you don’t own the file; check group membership with groups or id, and file ownership with ls -l.
Recursive change seems to skip some files — check for permission issues on subdirectories themselves (you need at least execute permission on a directory to traverse into it), or verify you’re not accidentally excluded by a -f silent flag hiding real errors.
New files in a shared directory keep reverting to the wrong group — this is almost always a missing setgid bit on the parent directory, as described above; set it with chmod g+s <directory> so future file creation inherits correctly without manual intervention.
Symlink group change doesn’t seem to apply — confirm whether you meant to change the link itself (-h) or its target (the default, dereferencing behavior); this is a common point of confusion when scripting around symlinked configuration files.
Security Implications
Group ownership is a core part of Linux’s discretionary access control model, and misconfigured group ownership is a genuinely common source of either overly permissive access (a sensitive file accidentally owned by a broad group like users or staff) or overly restrictive access (a legitimate team member unable to do their job because a file got created with the wrong group). I periodically audit critical directories for unexpected group ownership as part of routine security hygiene:
find /srv/shared -type f ! -group projectteam -ls
This surfaces any file that’s drifted away from the expected group, which is worth investigating — it can indicate a misconfigured deployment script, a manually created file that skipped normal provisioning, or in rarer cases, something worth escalating as a genuine security concern.
chgrp in Shell Scripts and Automation
I use chgrp regularly inside deployment and provisioning scripts, usually as part of a broader sequence that sets up ownership and permissions together for freshly deployed application files:
#!/bin/bash
APP_DIR="/opt/myapp"
APP_GROUP="myappgroup"
sudo chgrp -R "$APP_GROUP" "$APP_DIR"
sudo chmod -R g+rX "$APP_DIR"
sudo find "$APP_DIR" -type d -exec chmod g+s {} \;
echo "Group ownership and setgid applied to $APP_DIR"
That last find loop specifically targets directories (not regular files, where a setgid bit means something entirely different — it becomes the “mandatory locking” bit on some older systems, and is generally meaningless or ignored for regular files on modern Linux) and applies setgid individually to each one, since chmod -R g+s alone would attempt to apply it uniformly to files too, where it isn’t the intended behavior.
Auditing Group Ownership Across a Filesystem
Beyond fixing individual files, I periodically run broader audits to catch group ownership drift across an entire system, particularly on shared multi-user servers:
find / -xdev -group root -type f -perm -o+w 2>/dev/null
This particular example looks for world-writable files still owned by the root group — a combination worth investigating, since it often indicates either an intentional shared scratch space (fine) or an overlooked permission mistake during setup (not fine). I don’t run broad filesystem audits like this casually on busy production systems during peak hours, since a full filesystem find walk does add I/O load, but it’s a valuable habit during scheduled maintenance windows or as part of a periodic security review.
Interaction with ACLs (Access Control Lists)
It’s worth knowing that chgrp, like the rest of the traditional Unix permission model, operates independently of POSIX ACLs, which some filesystems support as a more granular, additional permission layer on top of the basic owner/group/other model. If a file has ACL entries set via setfacl, those can grant or restrict access beyond what the traditional group ownership alone implies, and changing group ownership with chgrp doesn’t automatically touch any ACL entries already in place:
getfacl myfile.txt
I check for ACLs with getfacl before assuming that a chgrp change alone is sufficient to fully control a file’s access, particularly on systems where I know ACLs have been used historically for more fine-grained access patterns than plain group ownership allows.
Compatibility Across Distributions
chgrp is part of GNU coreutils and behaves identically across all major Linux distributions — Debian, Ubuntu, RHEL, Fedora, CentOS, Arch, openSUSE — since they all ship the same GNU implementation. BSD and macOS systems have their own chgrp with largely overlapping but not identical flag support (notably, some GNU-specific long-form flags like --reference aren’t guaranteed on BSD variants), which is worth checking if you’re writing scripts intended to be portable across both Linux and macOS/BSD environments.
Summary
chgrp does one job, and does it precisely: changing which group owns a file or directory. Combined with an understanding of Linux’s three-tier permission model and the often-overlooked setgid bit for directories, it becomes the foundation for setting up genuinely maintainable shared access between team members, without resorting to overly broad permissions just to make collaboration work. The core commands — plain chgrp for single files, -R for whole directory trees, and --reference for matching an existing file’s group — cover the overwhelming majority of real situations you’ll run into managing shared Linux systems.
References
man 1 chgrp- GNU Coreutils manual (gnu.org/software/coreutils/)
man 2 chownfor the underlying system call semantics- Linux Filesystem Hierarchy Standard, for conventions around shared directory ownership