Linux users, groups and sudo access illustrated with a terminal and a security shield
Linux

Linux Users, Groups and sudo: A Practical Admin Guide

Sooner or later your VPS stops being a one-person machine. A colleague needs to deploy the app, a freelancer has to restart a service late at night, a second student joins the thesis project. The quick answer is to hand out the root password or drop everyone into the sudo group, and that quick answer is exactly how small servers end up with five people who can do anything, nobody who remembers who changed what, and an ex-contractor whose SSH key still works a year later. Linux already ships everything you need to do this properly: users, groups and sudo rules. In this guide we walk through all three with real commands you can copy and paste.

By the end you will know how to create, lock and delete accounts, how to add a user to a group (including the classic “linux add user to group sudo” task), how to write limited sudo rules in /etc/sudoers.d/ that allow only specific commands, and how to set up a shared folder where a team can work without permission headaches. Everything was written for Debian 13 and Ubuntu 24.04. Where the two behave differently, we say so explicitly.

What you need

  • A VPS (KVM or LXC) running Debian 13 or Ubuntu 24.04.
  • An existing account with sudo rights, logged in over SSH. We call it “your admin user” below.
  • A second SSH session open as a safety net while you change sudo rules, plus knowledge of where the web console or rescue mode is in your client area, in case you lock yourself out.
  • About 30 minutes and basic comfort with the terminal (typing commands, editing a file with nano).

If you are on Debian and set a root password during installation, the sudo package may not be installed at all. Log in as root and run apt install sudo first. Our VPS images come with a sudo-enabled user, so most readers can skip this.

How Linux thinks about users, groups and sudo

Before touching anything, a few definitions, because every command below builds on them.

A user is an account with a name and a number, the UID. The kernel only cares about the number; the name is for humans. root is always UID 0 and can do anything. Accounts below 1000 are system users, created by packages to run services (www-data for the web server, _apt for package downloads). Human accounts start at 1000 on Debian and Ubuntu.

A group is a named set of users with its own number, the GID. Every user has one primary group (on Debian and Ubuntu it is usually a private group with the same name as the user) and any number of supplementary groups, such as sudo or devteam. Groups exist so you can grant access once, to the group, instead of to each person separately.

This information lives in four plain text files:

  • /etc/passwd: one line per user (name, UID, GID, full name, home directory, login shell). Readable by everyone.
  • /etc/shadow: password hashes and expiry dates. Readable only by root.
  • /etc/group: one line per group and its members.
  • /etc/gshadow: group passwords, almost never used.

Every file and directory has an owner, a group and three sets of permissions (read r, write w, execute x) for owner, group and everyone else. That is how a group turns into actual access.

Finally, sudo is the program that lets an allowed user run a command as another user, usually root. Its rules live in /etc/sudoers and in drop-in files inside /etc/sudoers.d/. Each sudo use is logged with the real person’s name, which is the whole point: accountability.

Who can do what on the VPSUsersGroups and rulesAccess grantedalice (admin)bobdeploysudo groupdevteam groupsudoers.d/deploy-nginxAny command as rootShared team foldernginx reload/restartIdentity files/etc/passwd, /etc/shadow, /etc/groupsudo rules/etc/sudoers + /etc/sudoers.d/*
Figure 1: users get access through groups and sudo rules, never by sharing the root password. alice is a full admin, bob only works in the team folder, deploy can only reload nginx.

Why not just share the root password?

Three reasons. Accountability: if four people log in as root, the logs only say “root did it”. Revocation: when someone leaves, you have to change the password and tell everyone else, instead of locking one account. Least privilege: most people need to do two or three things as root, not everything. A deploy script that can only reload nginx cannot accidentally (or maliciously) wipe the disk.

adduser or useradd?

Debian and Ubuntu ship two tools for creating accounts, and tutorials mix them up constantly:

adduseruseradd
What it isFriendly wrapper, Debian/Ubuntu specificLow-level tool, present on every distro
Home directoryCreated automatically, filled from /etc/skelOnly with -m
Login shellFrom /etc/adduser.conf (bash)Pass -s /bin/bash or you may get /bin/sh
PasswordAsks interactivelySet later with passwd
Best forA human at the terminalScripts and automation

We use adduser interactively and show the useradd equivalent where it matters.

The procedure: Steps 1 to 71234567InspectusersCreatea userAdd togroupsLimitedsudoSharedfolderLocka userDeletea usergetentadduserusermod -aGvisudo -fsetfaclusermod -LdeluserOnboardingAccessOffboarding
Figure 2: the seven numbered steps of this guide. Steps 1 to 3 bring people in, Steps 4 and 5 grant access, Steps 6 and 7 take it away again.

Step 1: Inspect existing users and groups

Always look before you change anything. List only the human accounts (UID 1000 and above, excluding nobody, which is 65534):

getent passwd | awk -F: '$3 >= 1000 && $3 < 65534 { print $1, $3, $6, $7 }'

getent reads the account database (the files above, plus any directory service), and awk prints name, UID, home and shell. On a fresh VPS you should see a single line, your admin user, something like cristian 1000 /home/cristian /bin/bash.

Now check who already has full sudo rights, and which groups you belong to:

getent group sudo
id

The first command prints sudo:x:27:cristian: the group name, its GID (27 on Debian and Ubuntu) and the members. id shows your UID, primary group and all supplementary groups. On Ubuntu the first user is often also in adm, cdrom, dip, plugdev and sometimes lxd. Keep lxd in mind, we come back to it in the security section.

Do not be alarmed by how long the full lists are. Run getent passwd | wc -l and getent group | wc -l and you will see dozens of entries even on a brand new server. These are system accounts and groups created by packages, and you should leave them alone.

Accounts and groups on a fresh VPS (approximate)System accountsHuman accountsGroups010203040506070Count (approx.)~201~45~301~60Debian 13Ubuntu 24.04Fresh VPS image (exact numbers vary by image and installed packages)
Figure 3: approximate counts from Step 1 on fresh images. Almost every entry is a system account or group created by packages; only one is a real person.

Step 2: Create a user

Let us create an account for a colleague called alice:

sudo adduser alice

adduser creates the alice user and a private alice group, makes /home/alice, copies the default dotfiles from /etc/skel, then asks for a password twice and for optional details (full name, room number and so on, press Enter to skip). The last line asks “Is the information correct? [Y/n]”, answer Y.

The non-interactive equivalent, useful in scripts, is:

sudo useradd -m -s /bin/bash -c "Alice Popescu" alice
sudo passwd alice

Here -m creates the home directory, -s sets the login shell and -c stores the full name. Without -s, useradd may give the account /bin/sh, which works but is unpleasant to use interactively.

Check the result and the home directory permissions:

getent passwd alice
ls -ld /home/alice

You will see a line like alice:x:1001:1001:Alice Popescu,,,:/home/alice:/bin/bash. The x means the real password hash is in /etc/shadow. For the home directory, recent adduser versions create private homes (drwx------ or drwxr-x---, depending on the version and on DIR_MODE in /etc/adduser.conf), while useradd follows HOME_MODE or UMASK in /etc/login.defs. If you see drwxr-xr-x, other users can browse alice’s files, so tighten it with sudo chmod 750 /home/alice.

Give the user an SSH key

Passwords over SSH are a target for bots; keys are much safer. Ask alice for her public key (the .pub file, never the private one) and install it:

sudo install -d -m 700 -o alice -g alice /home/alice/.ssh
echo "ssh-ed25519 AAAAC3Nza...replace-with-real-key alice@laptop" | sudo tee -a /home/alice/.ssh/authorized_keys
sudo chown alice:alice /home/alice/.ssh/authorized_keys
sudo chmod 600 /home/alice/.ssh/authorized_keys

install -d creates the .ssh directory with the right owner and mode in one go. tee -a appends the key (plain > would not work through sudo). SSH is strict: if .ssh or authorized_keys is writable by anyone else, it silently ignores the key.

Two useful variations. If the person will only ever log in with a key, create the account with sudo adduser --disabled-password alice: no password is set at all. Keep in mind that such a user cannot pass a sudo password prompt, which matters in Step 4. And if you set a temporary password, force a change at first login with sudo passwd --expire alice.

Step 3: Add the user to groups (linux add user to group sudo)

This is the step most people search for. To give alice full admin rights, add her to the sudo group:

sudo usermod -aG sudo alice

-G sets supplementary groups and -a means append. The -a is critical: usermod -G sudo alice without it replaces all of alice’s supplementary groups with just sudo, silently removing her from everything else. Two equivalent commands that cannot make that mistake:

sudo gpasswd -a alice sudo
# or, Debian/Ubuntu only:
sudo adduser alice sudo

Verify:

id alice

Expected output: uid=1001(alice) gid=1001(alice) groups=1001(alice),27(sudo). Group membership is read at login, so if alice is already logged in, she must log out and back in before sudo works for her. Test it from her side with sudo whoami, which should print root after asking for her own password (not root’s). From your admin account you can check without logging in as her: sudo -l -U alice should list (ALL : ALL) ALL.

To take the right away again:

sudo gpasswd -d alice sudo

On Red Hat family systems the admin group is called wheel instead of sudo; on Debian and Ubuntu it is sudo.

Creating and removing groups

Your own groups work the same way. Create one for the team we use in Step 5:

sudo groupadd devteam
getent group devteam

The output devteam:x:1002: shows a new, empty group. Remove a group with sudo groupdel devteam; Linux refuses if it is still someone’s primary group.

A useful middle ground between “nothing” and “full root”: membership in adm lets a user read most log files in /var/log, and systemd-journal lets them read the full system journal, both without sudo.

Step 4: Give limited sudo rights with sudoers.d

Full sudo is fine for admins. For everyone else, we write narrow rules. A sudoers rule reads like a sentence:

who   where=(as_whom)   commands
%sudo ALL=(ALL:ALL)     ALL

The default line above says: members of group sudo (the % means group), on any host, may run as any user and group, any command. You will find it in /etc/sudoers, together with the line @includedir /etc/sudoers.d (older systems write #includedir, which is not a comment despite the #). That include line is why we never edit /etc/sudoers itself: we drop separate files into /etc/sudoers.d/, one per purpose, so they are easy to review and delete.

Before editing anything, open a second terminal and run sudo -i in it. That root shell stays open even if you break sudo, so you can fix your mistake immediately.

A rule for a deploy user

Say a CI pipeline logs in as deploy and needs to test and reload nginx, nothing else. Create the user without a password (it will use an SSH key) and find the exact command paths:

sudo adduser --disabled-password deploy
command -v systemctl nginx

Typical output is /usr/bin/systemctl and /usr/sbin/nginx. Sudo matches commands by full path, so always copy them from this output. Now create the rule file with visudo, which checks the syntax before saving:

sudo visudo -f /etc/sudoers.d/deploy-nginx

It opens your default editor (usually nano). Paste:

# Let the deploy user test and reload nginx, nothing else
Cmnd_Alias DEPLOY_NGINX = /usr/sbin/nginx -t, /usr/bin/systemctl reload nginx, /usr/bin/systemctl restart nginx
deploy ALL=(root) NOPASSWD: DEPLOY_NGINX

Cmnd_Alias gives a list of commands a name (uppercase, and unique across all sudoers files). Because we listed arguments, deploy can run exactly these command lines: systemctl stop nginx or systemctl restart ssh are refused. A command listed without arguments would allow any arguments, so always be specific. NOPASSWD is needed here because deploy has no password; for human users, prefer leaving it out.

Save and exit. Then validate everything and check permissions:

sudo visudo -c
sudo ls -l /etc/sudoers.d/

You want to see parsed OK for /etc/sudoers and for each file in sudoers.d, and the file mode -r--r----- root root (0440). If the mode differs, fix it with sudo chmod 0440 /etc/sudoers.d/deploy-nginx.

Test it as the deploy user:

sudo -l -U deploy
sudo -iu deploy
sudo systemctl reload nginx      # allowed
sudo systemctl stop nginx        # refused
exit

sudo -l -U deploy prints the rules, ending with (root) NOPASSWD: /usr/sbin/nginx -t, /usr/bin/systemctl reload nginx, /usr/bin/systemctl restart nginx. The refused command answers with “Sorry, user deploy is not allowed to execute … as root”. If nginx is not installed on your VPS, swap in a service you actually run. Reading the status does not need sudo at all: systemctl status nginx works for any user.

A rule for a group, with sudoedit

Rules for groups scale better than rules for individuals. Here members of webadmins can reload nginx and edit its main config:

sudo groupadd webadmins
sudo usermod -aG webadmins bob
sudo visudo -f /etc/sudoers.d/webadmins
%webadmins ALL=(root) /usr/bin/systemctl reload nginx, sudoedit /etc/nginx/nginx.conf

sudoedit is the safe way to let someone edit a root-owned file: sudo copies the file to a temporary location, bob edits the copy with his own editor running as bob, and sudo writes it back. Granting sudo nano /etc/nginx/nginx.conf instead would run the editor as root, and editors can open other files or run shell commands. Bob runs it as sudoedit /etc/nginx/nginx.conf.

Commands you should never grant

Any command that can write arbitrary files or start other programs equals full root. That includes shells (bash, sh), editors (vim, nano), pagers (less, more), find, tar, rsync, cp, mv, chmod, chown, interpreters (python3, perl), docker, package managers (apt install runs package scripts) and systemctl edit. Wildcards in arguments (/usr/bin/systemctl * nginx) are also dangerous, because * can match extra arguments.

Step 5: Set up a shared team folder

alice and bob need a folder where both can create and edit files, and nobody else can look inside. Plain permissions get you 90% of the way; one ACL gets you the rest.

sudo usermod -aG devteam alice
sudo usermod -aG devteam bob
sudo mkdir -p /srv/projects/devteam
sudo chown root:devteam /srv/projects/devteam
sudo chmod 2770 /srv/projects/devteam

/srv is the standard place for data served by the machine. The mode 2770 means: owner and group can read, write and enter (77), others get nothing (0), and the leading 2 sets the setgid bit. On a directory, setgid makes every new file and subfolder belong to the devteam group, instead of the creator’s private group.

There is one remaining trap: the umask. It is a per-process mask that removes permissions from new files, and the usual default of 022 removes group write. So alice’s new files would be readable by bob but not editable. Instead of changing everyone’s umask, we set a default ACL (Access Control List) on the folder, which overrides the umask for anything created inside it:

sudo apt install -y acl
sudo setfacl -m g:devteam:rwx /srv/projects/devteam
sudo setfacl -d -m g:devteam:rwx,o::--- /srv/projects/devteam
getfacl /srv/projects/devteam

The first setfacl grants the group access explicitly, the second (-d) defines the defaults that new files and folders inherit. The getfacl output should include group:devteam:rwx, default:group:devteam:rwx and default:other::---, plus # flags: -s- for the setgid bit.

Reading the shared folder: drwxrws---+drwxrws---+typeowner: rootgroup: devteamothersACLdirectoryread, write,enters = setgid,group inheritedno accessat alldefault ACLkeeps g+rwchmod 2770 = setgid (2) + owner rwx (7) + group rwx (7) + others none (0)setfacl -d -m g:devteam:rwx makes new files group-writable despite umask 022
Figure 4: what Step 5 produces. Setgid keeps the group on new files, the default ACL keeps them editable by the whole team, and others are locked out.

Test it, using sudo -u to act as each user (sudo loads fresh group membership, so this works even before they log in again):

sudo -u alice touch /srv/projects/devteam/notes.txt
sudo -u bob sh -c 'echo "edited by bob" >> /srv/projects/devteam/notes.txt'
sudo ls -l /srv/projects/devteam

Expected: -rw-rw----+ 1 alice devteam 14 ... notes.txt. Owner alice, group devteam, group can write, others nothing, and bob’s edit succeeded. If you also want to stop people deleting each other’s files, add the sticky bit with sudo chmod +t /srv/projects/devteam (mode becomes 3770).

A note for containers: all of this works inside an unprivileged LXC container, since users and groups live inside the container. On some storage setups, especially bind mounts from the host, setfacl may fail with “Operation not supported”. In that case keep the setgid folder and ask the team to use umask 002 in their ~/.bashrc.

Step 6: Lock a user temporarily

Someone goes on leave, or you suspect a compromised account. You want to block access without deleting anything. The common mistake is sudo passwd -l alice: it only disables the password, and alice can still log in with her SSH key. The usermod manual itself recommends combining a password lock with an expiry date:

sudo usermod -L -e 1 alice

-L locks the password and -e 1 sets the account expiry to day 1 after 1 January 1970, so the account counts as expired. Through PAM, which both SSH and cron use, an expired account cannot log in with a password or a key, and its cron jobs normally stop running too.

Locking does not end sessions that are already open. Close them:

sudo loginctl terminate-user alice
pgrep -u alice || echo "no processes left"

If processes remain (or loginctl is not available in your container), use sudo pkill -u alice. Verify the lock:

sudo passwd -S alice
sudo chage -l alice

passwd -S shows L in the second column (locked), and chage -l shows “Account expires: Jan 02, 1970”. To unlock later:

sudo usermod -U -e "" alice

An empty -e "" removes the expiry date. For key-only accounts without a password, usermod -U may warn that unlocking would leave a passwordless account; that is fine, the expiry change is what restores access.

Step 7: Delete a user safely

Deleting is final, so do it in this order: lock (Step 6), look around, back up, delete, check leftovers. First, see what alice leaves behind outside her home:

sudo crontab -u alice -l
sudo find / -xdev -user alice -not -path "/home/alice/*" 2>/dev/null
sudo grep -rl alice /etc/sudoers.d/

The first command shows her scheduled jobs (“no crontab for alice” is good news). The find searches the root filesystem (-xdev stays on it) for files she owns elsewhere, for example in /srv/projects/devteam. Shared team files should usually be handed over with sudo chown bob <file>. The grep finds sudoers files that mention her, which you should remove or edit with visudo.

Back up her home directory and make the archive yours, because sudo tar creates root-owned files:

sudo tar -czf ~/alice-home-$(date +%F).tar.gz -C /home alice
sudo chown "$USER": ~/alice-home-*.tar.gz

Copy it off the server from your own computer with scp youradmin@your-vps-ip:~/alice-home-*.tar.gz .. Now delete the account and its home:

sudo deluser --remove-home alice
id alice

The portable equivalent is sudo userdel -r alice. Both refuse while the user still has running processes, which is why we ended them in Step 6. id alice should now answer “no such user”. Finally, look for orphaned files: sudo find / -xdev -nouser lists files whose owner no longer exists. They show a bare number instead of a name, and a future user who receives the same UID would silently inherit them, so reassign or delete them.

Troubleshooting and common mistakes

  • The user lost all their groups. You ran usermod -G without -a. Re-add the groups with sudo usermod -aG group1,group2 alice and check with id alice.
  • “Permission denied” right after adding someone to a group. Group membership is loaded at login. Log out and back in, or start a new shell with the group using newgrp devteam.
  • A sudoers.d file is ignored. Sudo skips files whose name contains a dot or ends with ~. deploy.conf is ignored, deploy-nginx works. Also check the 0440 mode.
  • “alice is not in the sudoers file”. She is not in the sudo group yet, or she has not logged in again since you added her.
  • sudo asks a key-only user for a password. That user has no password. Either set one with sudo passwd deploy or grant only specific commands with NOPASSWD.
  • A rule does not match. The path is probably different. Run command -v and copy the exact path, and remember that arguments in the rule must match exactly.
  • You broke sudo. Use the root shell you kept open in the second terminal and run visudo -c. If you closed it, use su - if a root password exists, otherwise the web console or rescue mode from your client area to fix the file.
  • Where are the sudo logs? On Ubuntu 24.04 they are in /var/log/auth.log and the journal. Debian 13 does not install rsyslog by default, so there is no auth.log: use sudo journalctl _COMM=sudo --since today on both.

Security notes

  • Least privilege by default. Full sudo only for the people who really administer the server; everyone else gets a narrow file in /etc/sudoers.d/.
  • Some groups are root in disguise. Membership in docker, lxd or disk gives a user full control of the machine without any sudo prompt. Treat adding someone to these groups exactly like adding them to sudo.
  • Always use visudo, for the main file and for drop-ins. A syntax error in a file edited directly can disable sudo for everyone.
  • Audit regularly. getent group sudo, ls /etc/sudoers.d/ and sudo -l -U <user> show who can do what in under a minute. Pair them with the human account list from Step 1 every few months.
  • Offboard the same day. Lock (Step 6) as soon as someone leaves, delete (Step 7) once you have what you need from their files.
  • Shorten the sudo grace period if several people share the server: a drop-in file such as /etc/sudoers.d/00-defaults containing Defaults timestamp_timeout=5 asks for the password again after 5 minutes instead of the default 15.
  • Prefer SSH keys for every account, and consider disabling password logins and direct root login over SSH once every admin has a working key and sudo.

So, how much access does everyone really need?

Usually much less than “all of it”. With the seven steps above you can bring a person in, give them exactly the rights their work requires, share files with a team cleanly, and take everything back the day they leave, all with tools already installed on every Debian and Ubuntu server. Five minutes spent writing a sudoers.d file instead of handing out full root saves hours of guessing after the first incident.

You can try every command in this guide on a VPS with us, with root access from minute one. If you would rather have someone set up accounts, sudo policies, SSH keys and regular access audits for your team, our server administration service takes care of it.

FAQ

How do I add a user to the sudo group on Ubuntu or Debian?

Run sudo usermod -aG sudo username, then ask the user to log out and back in. Check with id username. Never forget the -a, otherwise the user’s other supplementary groups are removed.

What is the difference between adduser and useradd?

adduser is a friendly Debian/Ubuntu wrapper that creates the home directory, sets bash and asks for a password. useradd is the low-level tool available on every distribution and needs flags like -m and -s /bin/bash to get the same result.

Why doesn’t my new group work until I log out?

Group membership is read when a session starts. Existing shells keep the old list. Log out and in again, or use newgrp groupname for a single shell.

Is NOPASSWD safe?

It is acceptable for automation accounts restricted to a few exact commands. For humans with broad rights, keep the password prompt: it protects you if someone gets hold of an unlocked session.

What is the wheel group?

It is the traditional admin group on Red Hat family distributions, playing the role that the sudo group plays on Debian and Ubuntu.

Trusted By & Member Of

We are proud members of leading internet infrastructure organizations.

RIPE NCC MANRS PeeringDB RoTLD DSIX SBIX 4IXP LOCIX Euro-IX RIPE NCC MANRS PeeringDB RoTLD DSIX SBIX 4IXP LOCIX Euro-IX