groups Command: Tutorial & Examples
Print every group a user belongs to — and learn why your permissions are the union of all of them.
What It Is
groups answers one small question with surprising depth: which groups does this user belong to? Type it with no arguments and you get a single line — the groups you're in. Type a username after it and you get theirs. That's the whole command. There are exactly two flags (--help and --version), and you'll never need either. It is, by some distance, the simplest tool we'll cover.
So why a whole page? Because the question it answers is one of the most important on a Linux box, and almost nobody who's new to servers truly understands the answer. Groups are how Linux decides who's allowed to do what — read this file, restart that service, talk to the Docker daemon, run sudo. Your power on a machine isn't a single setting; it's the sum of every group you're in. Get that wrong and you'll spend an afternoon staring at a Permission denied that makes no sense — or, worse, hand someone far more power than you meant to. By the end of this page you'll read a groups line the way a veteran does: instantly, and with a quiet little alarm that goes off when something's not right.
Your First Look
Run it with no arguments and it prints the groups the current user belongs to, space-separated, on one line:
groups
arndt cdrom floppy sudo audio dip video plugdev users netdev scanner bluetooth lpadmin
That's a normal desktop-Linux user. Ask about a specific user instead, and you get their groups in the user : groups form:
groups root
root : root
root belongs only to the root group — it doesn't need any others, because the superuser bypasses permission checks entirely. (More on that lovely asymmetry later.)
One line, no ceremony. But every word on that line is a capability, and learning to read them is the point.
How I Read It
When I land on a fresh server and want to know what a user can actually do, groups is one of my first three commands. Here's the exact order of glances in my head — it takes about two seconds.
First, I scan for the power groups. A handful of group names mean "this account is dangerous," and I look for them before anything else:
sudo(orwheelon Red Hat / Arch family) — this user can become root. Full stop. Everything else on the line is a footnote next to this one.docker— can talk to the Docker daemon, which runs as root. This is root in a trench coat; we'll come back to why it's the most underestimated entry on the whole line.adm— can read the system logs in/var/logwithout being root. Harmless-looking, quietly powerful.
Second, I read the rest as a story about what the machine is for. cdrom, floppy, audio, video, bluetooth, scanner, plugdev — that's a desktop. A bare server user is usually a short, boring line, and boring is exactly what you want. A server account carrying audio and bluetooth is a small smell: either it's a workstation pretending to be a server, or someone copied a desktop user template onto a box where it makes no sense.
Third — and this is the move that took me embarrassingly long to internalize — I remember that the first group is special. It's the user's primary group, and it's not the same kind of thing as the rest. Everything after it is supplementary. The distinction looks pedantic until the day it bites you, so let's make it crystal clear once and for all.
Primary vs Supplementary: The One Thing Everyone Muddles
Every user has exactly one primary group and any number of supplementary groups, and they come from two completely different files. This is the single fact that turns groups from confusing to obvious, so let's nail it.
Your primary group is stored in /etc/passwd, right alongside your user ID:
grep '^arndt:' /etc/passwd
arndt:x:1000:1000:Holger Arndt,,,:/home/arndt:/bin/bash
Read the colon-separated fields: username, password placeholder (x — the real hash lives in /etc/shadow), then user ID 1000, then primary group ID 1000. That fourth field is the whole story: my primary group is GID 1000, which happens to be a group also named arndt. On most modern Linux systems each user gets their own personal group like this — it's called the user private group scheme, and it's why you'll see a arndt group that contains only arndt.
Your supplementary groups live in the other file, /etc/group, where each line lists a group and the users dropped into it:
grep -E '^(sudo|cdrom):' /etc/group
cdrom:x:24:arndt
sudo:x:27:arndt
So cdrom (GID 24) and sudo (GID 27) both have arndt in their member list. That's what makes them supplementary groups of mine. Notice the direction: the primary group is recorded on the user (in passwd), while supplementary memberships are recorded on the group (in group). Same relationship, stored from opposite ends — and that asymmetry is exactly why two different commands exist to look it up, which we'll get to.
Why does the primary group matter beyond bookkeeping? New files you create are owned by your primary group. When you run touch report.txt, the file's group is your primary group, not some average of all of them. That's the practical payoff of the distinction, and the cause of a classic head-scratcher we'll meet in the gotchas.
Note
The primary group is the only group you're guaranteed to be in. A user with zero supplementary groups is perfectly valid — they just get a one-word
groupsline. Service accounts (www-data,postgres,nginx) usually look exactly like that, and minimal is good.
The Magic: Your Permissions Are the Union
Here's the insight that makes the whole system click, and it's genuinely worth pausing on.
When the kernel decides whether you may touch a file, it doesn't pick one of your groups and check it. It checks the file's group against every group you're in, all at once. Your effective permissions are the union of all your memberships — you get the most-permissive outcome any single group grants you. Being in sudo and docker and adm doesn't make you "mostly one of them"; it makes you all three simultaneously, every second you're logged in.
This is why the groups line is the right thing to read: it's not a list of roles you can switch between — it's a list of powers you hold at the same time. And it's why adding someone to one extra group is never a small, contained change. You're not slotting them into a box; you're adding a capability to the pile they already carry. The union only ever grows.
And here's the part that delights me every time. You can watch the kernel assemble that union live. The id command — groups' richer sibling — shows it with the numbers attached:
id
uid=1000(arndt) gid=1000(arndt) groups=1000(arndt),24(cdrom),25(floppy),27(sudo),29(audio),30(dip),44(video),46(plugdev),100(users),101(netdev),102(scanner),106(bluetooth),108(lpadmin)
Look at the structure: there's uid (who you are), a single gid (your primary group — note it's listed separately, because it's special), and then groups=, the full union. groups the command is, almost literally, just the names from that groups= field printed on their own. The kernel stamps that entire set onto every process you launch, and carries it along to every permission check that process ever makes. Read id once and the whole identity model of Linux is sitting right there in one line.
The Login-Session Trap (the big one)
Now for the gotcha that costs more new admins an hour than any other on this page — and the beautiful, slightly maddening reason behind it.
You add yourself to the docker group so you can stop typing sudo in front of every docker command:
sudo usermod -aG docker alice
You run docker ps. Permission denied. You run groups. It doesn't list docker either. You added the membership, the file says it's there — and the machine acts like nothing happened. Are you losing your mind?
No. You've just run head-first into how Unix sessions work, and it's worth understanding because it explains a dozen other mysteries too.
Your group memberships are baked into your session at the moment you log in, and they never change for the life of that session. When you logged in, login (or your display manager, or sshd) read the group files once, computed your union, and stamped it onto your shell process. Everything you launch from that shell inherits that frozen snapshot. Editing /etc/group afterward changes the file — but not the running processes that already copied it. Your shell is carrying a photograph of the group database from login time, and photographs don't update.
The fix is simply to start a fresh session: log out and back in, or for SSH just disconnect and reconnect. The new shell re-reads the files and picks up your new group. That's it.
Here's the loveliest detail, and the bit almost nobody notices: the groups man page tells you this is going to happen. Read it carefully — "print the groups a user is in… for the current process (which may differ if the groups database has changed)." That parenthetical is the entire trap, documented in plain sight since forever. The command reports your process's groups, the live snapshot — not what the files currently say. The files and your session can disagree, and after a usermod they do. (Want to see the truth straight from the file instead of your stale session? That's the next section.)
Warning
This is also why you should never panic-debug a permissions problem by re-running
groupsright afterusermod. It's expected to lie until you re-login. Open a brand-new terminal (orsshin fresh) and check there. If the new session shows the group and you still can't do the thing, now you have a real problem worth chasing.
There's a way to fake a fresh session for one command — newgrp docker spawns a new shell with that group active — but honestly, log out and back in. It's cleaner, it tests the real thing, and it sidesteps newgrp's own quirks (more on it in the cheat sheet).
groups vs id vs getent: Reading From Both Ends
You now know group membership is stored from two directions, so naturally there are tools for looking from each end. Knowing which to reach for is half the skill.
groups and id ask "what groups is this user in?" — and they answer from the live session, the frozen snapshot. groups gives you just the names; id gives you names and numbers and the uid/gid breakdown. Use id when the numbers matter (comparing across machines, debugging a mount with uid=/gid= options), groups when you just want the quick human-readable list.
getent group asks the reverse: "who's in this group?" — and crucially, it reads the files as they are right now, not your session snapshot:
getent group sudo
sudo:x:27:arndt
That's the membership list for sudo: GID 27, with arndt as its sole member. This is the tool to reach for right after a usermod, because it shows the current truth — if getent lists the user but groups doesn't, you've confirmed the login-session trap and know to just re-login.
The name getent is "get entries," and the superpower is that it doesn't only read /etc/group. It reads through the system's Name Service Switch (NSS) — the layer configured in /etc/nsswitch.conf — so on a box where users and groups come from LDAP or Active Directory instead of flat files, getent group still works while a naive grep /etc/group would come up empty. On a big corporate network, that distinction is the difference between "the command works" and "why is this group invisible." File this away — it's the quiet reason getent is the correct way to query groups, even on a simple box.
One more reverse trick worth knowing: there's no --gid-name flag, but you can look a group up by its number with getent group 27, which is handy when a ls -l shows you a numeric group ID with no name attached (usually a sign the group was deleted out from under some files).
Reading It by Example
Real groups lines and the verdict each one earns. Build the instinct.
arndt cdrom sudo audio video → a desktop user with admin rights. The cdrom/audio/video say "workstation"; the sudo says "and they can become root." Normal for a personal laptop, slightly eyebrow-raising on a production server.
www-data → a textbook service account. One group, its own private group, nothing else. This process can touch its own files and nothing dangerous. Exactly what a web server worker should look like — minimal blast radius if it's ever compromised.
deploy docker → stop and look hard. This account isn't root by name, but docker membership means it can start a container that mounts the host's entire filesystem and writes anywhere as root. A leaked key to a docker-group user is, for practical purposes, a leaked root key. The next section is about exactly this.
alice sudo docker adm → a fully-trusted admin. sudo for root, docker for containers, adm for logs. Fine if alice is meant to be a senior operator — and a quiet liability if this is some forgotten intern account from two years ago. The line tells you the power; only you know if it's deserved.
bob (just the username, nothing after the :) → a locked-down user in only their primary group. Can't sudo, can't do much. Perfect for an account that only needs to run one specific service or own one directory.
The docker Group: Root in a Trench Coat
This deserves its own section because it's the single most common way a "non-root" account turns out to be root all along — and the groups line is where you'd catch it.
Membership in the docker group lets you talk to the Docker daemon's socket. The daemon runs as root. So you can ask it to do root things — and the classic proof is a one-liner that mounts the host's whole filesystem inside a throwaway container and hands you a root shell over it:
docker run -v /:/host -it alpine chroot /host sh
Danger
Anyone in the
dockergroup can run the command above and become root on the host. There is nosudo, no password, no audit trail. Putting a user indockeris functionally identical to giving them passwordless root — treat it with exactly that gravity. The Docker project documents this openly; it's not a bug, it's the architecture. When you seedockeron agroupsline, read it as "root," and ask whether that user truly needs it.
This is the page's quiet thesis in miniature: a groups line isn't a list of hobbies, it's a list of keys. Some of those keys open the whole building, and they don't always have scary names.
Cheat Sheet
The command itself, in full:
groups— groups of the current (logged-in) user, from the live session snapshotgroups USER— groups of a named user, inuser : groupsformgroups u1 u2 u3— several users at once, one labelled line eachgroups --help/groups --version— the only two flags, and you'll never reach for them
The neighbours you'll actually use alongside it:
id— names and numbers and uid/gid; reach for it when the numbers matterid -nG USER— just the supplementary group names (the closestidequivalent togroups)id -gn USER— only the primary group namegetent group NAME— the reverse view: who's in a group, read from the current files (LDAP-aware)getent group GID— look up a group by its numberusermod -aG GROUP USER— add a user to a group (the-ais mandatory: without it,-Greplaces the whole list and silently drops every other group — a genuine foot-gun)gpasswd -a USER GROUP/gpasswd -d USER GROUP— add / remove, the friendlier modern waynewgrp GROUP— start a sub-shell with that group active now, without re-logging-insg GROUP -c 'cmd'— run a single command under a different primary group;newgrp's one-shot cousin
Gotchas
- The login-session trap, again, because it's that common.
groupsreflects your session, not the current files. After any group change: log out, log back in.getentshows the truth in the meantime. usermod -Gwithout-ais destructive.usermod -aG docker bobadds.usermod -G docker bobreplaces — bob losessudo,adm, everything, and keeps onlydocker. The missingahas locked admins out of their own boxes. Always type-aGas one reflexive unit.- New files take your primary group, not all of them. Create a file while your primary group is your personal
alicegroup, and a teammate in the sharedwebteamgroup can't read it — even though you're inwebteamtoo. The fix is often the setgid bit on the directory (thesindrwxr-sr-x), which forces new files to inherit the directory's group instead of yours. That one trick solves most "we share a folder but keep stepping on each other's permissions" problems. groupson a deleted user shows nothing useful. If a user was removed but their files weren't,ls -lshows a bare numeric GID.getent group <number>will usually confirm there's no name left.root's short line is not a bug. The superuser sails past permission checks regardless of groups, so it simply doesn't need them. A long group list onrootwould be theatre.
History & Philosophy
Groups are nearly as old as Unix itself, but they used to be meaner. On the earliest systems a user could be in only one group at a time — your /etc/passwd primary group was the whole story, and to act as a member of another group you had to actively switch with newgrp, surrendering the old one. That's the historical ghost still haunting newgrp today: it spawns a shell with a new primary group, a direct echo of the days when you could only wear one group hat at once.
The leap to supplementary groups — being in many groups simultaneously, the union we've been celebrating — arrived with BSD in the early 1980s and was such an obvious improvement it became universal. It's easy to take for granted now, but it's genuinely the feature that makes group-based permissions usable: without it, every "alice needs to read the web files and the logs and run docker" would be an impossible juggling act of one-group-at-a-time switches.
BTW, here's a tangent that explains a number you'll trip over eventually. Why do regular users so often start at UID and GID 1000? Convention, hardened into near-law: IDs below 1000 are reserved for the system — root is 0, and the daemons (daemon, bin, mail, the whole crowd at the top of /etc/group) take the low numbers. Humans begin at 1000 by tradition that traces back through Debian and others to the old useradd default. So uid=1000 is quietly announcing "I'm the first real person who logged into this machine." On older systems you'll see the line drawn at 500 instead (Red Hat used to start there) — a tiny archaeological tell of which family a box descends from.
And the deepest idea worth carrying away: a Linux "user" isn't really a person at all — it's a uid plus a set of gids, a little bundle of numbers the kernel stamps onto processes and checks against files. groups and id just translate that bundle back into names you can read. Once you see identity on Linux as numbers attached to processes, a whole category of mysteries — why a file shows a number instead of a name, why a container's root can write your files, why a mount needs uid= options — stops being mysterious and starts being obvious. Pull the thread on groups and you've started unravelling the entire permission model. It's all just numbers, all the way down.
See Also
id— the richer view: uid, gid, and the full group union with numbersgetent— query groups (and users) the NSS-aware, LDAP-safe waysudo— what membership in thesudo/wheelgroup actually grantschmod— set the permission bits that groups are checked againstchown— change a file's owning user and group- /etc/group — where supplementary memberships are stored
- /etc/passwd — where each user's primary group is recorded
- superuser — root, and why it ignores groups entirely
- setgid — the directory bit that fixes shared-folder group woes
- Docker — why
dockergroup membership is effectively root
Not sure which of your services quietly run as root, or what each account on the box can actually reach?
CleverUptime watches every server you run, detects the services living on it, and explains each one in plain language — so when something is running with more power than it needs, you find out from a calm dashboard instead of from an incident.
Want to see your own server's health right now? One command, no signup, no install.