--- title: "How to Manage Linux Users, Groups and Permissions" description: "Create Linux users and groups, set file permissions with chmod and chown, build a shared team directory with setgid and ACLs, and grant limited sudo access." category: "Server Setup" language: en source: https://cubepath.com/docs/server-setup/how-to-configure-users-and-permissions-on-linux updated: 2026-09-25 --- # How to Manage Linux Users, Groups and Permissions Linux decides who can read, change or run each file based on its owner, its group and a set of permission bits. In this tutorial you will create user accounts and a group, read and change permissions with `chmod` and `chown`, build a shared directory where a team can collaborate safely, fine-tune access with ACLs, and give a user limited `sudo` rights. The examples use Ubuntu 24.04; differences for Debian 12 and Rocky Linux 9 are noted where they matter. ## Prerequisites To follow this guide you need: - A server running Ubuntu 24.04 LTS (Debian 12 and Rocky Linux 9 work with the noted changes), for example a CubePath VPS. - A non-root user with `sudo` privileges, logged in over SSH. The examples create two users, `alice` and `bob`, and a group called `developers`. Use your own names if you prefer. ## Step 1 - Creating and managing user accounts Each account has an entry in `/etc/passwd` (name, UID, home directory, shell) and a password hash in `/etc/shadow`, which only root can read. On Ubuntu and Debian, `adduser` is the friendly tool: it creates the home directory, copies the skeleton files from `/etc/skel` and asks for a password: ```bash sudo adduser alice ``` `useradd` is the low-level command available on every distribution (on Rocky Linux, `adduser` is simply a link to it). Use `-m` to create the home directory and `-s` to set the shell, then set a password: ```bash sudo useradd -m -s /bin/bash bob sudo passwd bob ``` Check the new accounts with `id`, which shows the UID, the primary group and all supplementary groups: ```bash id alice ``` ```output uid=1001(alice) gid=1001(alice) groups=1001(alice),100(users) ``` A few everyday changes: | Task | Command | |------|---------| | Change a user's shell | `sudo usermod -s /bin/zsh alice` | | Lock an account (keeps files) | `sudo usermod -L bob` | | Unlock it | `sudo usermod -U bob` | | Expire an account on a date | `sudo usermod -e 2026-12-31 bob` | | Delete a user and home directory | `sudo userdel -r bob` | > Note: `usermod -L` only locks the password. A user with an SSH key can still log in. To block every login, also expire the account with `sudo usermod -e 1 bob`, or set its shell to `/usr/sbin/nologin`. ## Step 2 - Creating groups and adding members Groups let you grant access to several users at once. Create the `developers` group: ```bash sudo groupadd developers ``` Add both users as supplementary members. The `-a` flag (append) is essential: without it, `-G` replaces all of the user's existing supplementary groups: ```bash sudo usermod -aG developers alice sudo usermod -aG developers bob ``` Verify the membership: ```bash getent group developers ``` ```output developers:x:1003:alice,bob ``` To remove a user from a group, use `gpasswd`: ```bash sudo gpasswd -d bob developers ``` Group changes apply to **new** login sessions. A user who is already logged in has to log out and back in (or start a new SSH session) before `id` shows the new group. ## Step 3 - Reading file permissions List a file with `ls -l`: ```bash ls -l /etc/passwd ``` ```output -rw-r--r-- 1 root root 2247 Sep 20 10:12 /etc/passwd ``` The first column has ten characters: the file type (`-` file, `d` directory, `l` symbolic link) followed by three sets of `rwx`, for the **owner**, the **group** and **others**. After the link count come the owner (`root`) and the group (`root`). The bits mean different things on files and directories: | Bit | Value | On a file | On a directory | |-----|-------|-----------|----------------| | `r` | 4 | Read the contents | List the names inside | | `w` | 2 | Modify the contents | Create, delete and rename entries | | `x` | 1 | Run it as a program | Enter it and access entries by name | Adding the values of each set gives the numeric (octal) form. `rw-r--r--` is `644`, `rwxr-xr-x` is `755`, and `rwx------` is `700`. `stat` shows both notations at once: ```bash stat -c '%A %a %U:%G %n' /etc/passwd /etc/shadow ``` ```output -rw-r--r-- 644 root:root /etc/passwd -rw-r----- 640 root:shadow /etc/shadow ``` On Rocky Linux, `/etc/shadow` has mode `000` and is readable only through root's special privileges. ## Step 4 - Changing permissions with chmod `chmod` accepts either numbers or symbolic changes. Create a test file as your user to experiment: ```bash touch ~/report.txt chmod 640 ~/report.txt ls -l ~/report.txt ``` ```output -rw-r----- 1 sammy sammy 0 Sep 24 11:02 /home/sammy/report.txt ``` Symbolic mode is handy for changing one bit without touching the rest. `u`, `g`, `o` and `a` stand for user (owner), group, others and all: ```bash chmod g+w ~/report.txt chmod o-rwx ~/report.txt ``` The same syntax makes a script executable for its owner, for example `chmod u+x ~/backup.sh`. Typical values: | Mode | Use | |------|-----| | `600` | Private files: SSH keys, `.env` files with secrets | | `644` | Regular files other users may read | | `700` | Private directories, such as `~/.ssh` | | `755` | Scripts and directories everyone may enter | To apply permissions to a whole tree, do not use `chmod -R 755`, which makes every file executable. Use the capital `X`, which adds execute only to directories (and to files that are already executable): ```bash chmod -R u=rwX,g=rX,o= ~/myapp ``` ## Step 5 - Changing ownership with chown Only root can give a file to another user, so `chown` needs `sudo`. The syntax is `owner:group`: ```bash sudo chown alice:developers /home/sammy/report.txt ``` Change just the group with `chown :group` (or the older `chgrp`), and use `-R` for directories: ```bash sudo chown -R www-data:www-data /var/www/example ``` Check the result: ```bash ls -l /home/sammy/report.txt ``` ```output -rw-rw---- 1 alice developers 0 Sep 24 11:02 /home/sammy/report.txt ``` ## Step 6 - Building a shared team directory with setgid A common need is a directory where every member of a group can create and edit files. Plain permissions are not enough: new files get the creator's primary group, so other members cannot edit them. The **setgid** bit on a directory solves this by making new files inherit the directory's group. Create the directory, hand it to the `developers` group and set mode `2770`. The leading `2` is setgid, `770` gives full access to owner and group and nothing to others: ```bash sudo mkdir -p /srv/project sudo chown root:developers /srv/project sudo chmod 2770 /srv/project ls -ld /srv/project ``` ```output drwxrws--- 2 root developers 4096 Sep 24 11:10 /srv/project ``` The `s` in the group position confirms setgid is on. Test it by creating a file as `alice`: ```bash sudo -u alice touch /srv/project/notes.md ls -l /srv/project ``` ```output -rw-r--r-- 1 alice developers 0 Sep 24 11:12 notes.md ``` The file belongs to the `developers` group even though alice's primary group is `alice`. Whether other members can also **edit** it depends on the creator's umask, covered in the next step. Files created through `sudo` use sudo's default umask of `0022`, which is why this test file has no group write bit. When alice logs in with her own SSH session on Ubuntu, her umask is `0002` and her new files are `-rw-rw-r--`, so `bob` can edit them. Two related special bits are worth knowing: - **Sticky bit** (`1`, shown as `t`): in a shared writable directory, users can only delete their own files. `/tmp` uses it (`drwxrwxrwt`). Set it with `sudo chmod +t /srv/project` if members must not delete each other's files. - **setuid** (`4`, shown as `s` in the owner position): a program runs with its owner's privileges, as `/usr/bin/passwd` does. Never set it on your own scripts. List every setuid file on the system to know what is there: ```bash sudo find / -xdev -perm -4000 -type f ``` ## Step 7 - Understanding the default umask The **umask** removes permissions from newly created files. Files start from `666` and directories from `777`, and the umask bits are subtracted. ```bash umask ``` ```output 0002 ``` On Ubuntu 24.04 and Rocky Linux 9, regular users have `0002` (new files `664`, directories `775`) and root has `0022` (`644` and `755`). On Debian 12 regular users get `0022` by default, so group members cannot edit each other's files in the shared directory. To give a user `0002`, add this line to their `~/.profile`: ```bash echo 'umask 0002' >> ~/.profile ``` For a service that should create private files, set the umask in its systemd unit instead, with `UMask=0077` in the `[Service]` section. ## Step 8 - Fine-grained access with ACLs Standard permissions allow only one owner and one group. Access Control Lists add entries for extra users or groups. Suppose `carol`, who is not a developer, needs read-only access to `/srv/project`. The `acl` tools are installed by default on Ubuntu; on Debian 12 or Rocky Linux install them with `sudo apt install acl` or `sudo dnf install acl`. Create the user and grant read and traverse access on the existing tree: ```bash sudo adduser carol sudo setfacl -R -m u:carol:rX /srv/project ``` Add a **default** ACL so files created in the future inherit the same rule: ```bash sudo setfacl -d -m u:carol:rX /srv/project ``` Inspect the result: ```bash getfacl /srv/project ``` ```output # file: srv/project # owner: root # group: developers # flags: -s- user::rwx user:carol:r-x group::rwx mask::rwx other::--- default:user::rwx default:user:carol:r-x default:group::rwx default:mask::rwx default:other::--- ``` In `ls -l`, a `+` after the permission string tells you a file has ACLs. Remove carol's entries with `sudo setfacl -R -x u:carol /srv/project` and `sudo setfacl -d -x u:carol /srv/project`. ## Step 9 - Granting sudo access Full administrator rights come from group membership: the `sudo` group on Ubuntu and Debian, `wheel` on Rocky Linux: ```bash sudo usermod -aG sudo alice ``` Often a user only needs one or two privileged commands. Put such rules in a separate file under `/etc/sudoers.d/` and always edit it with `visudo`, which checks the syntax before saving so a typo cannot break `sudo`: ```bash sudo visudo -f /etc/sudoers.d/developers ``` This rule lets members of `developers` restart and check the Nginx service, and nothing else: ```text %developers ALL=(root) /usr/bin/systemctl restart nginx, /usr/bin/systemctl status nginx ``` Give the file safe permissions and verify what a user may run: ```bash sudo chmod 0440 /etc/sudoers.d/developers sudo -l -U bob ``` ```output User bob may run the following commands on server: (root) /usr/bin/systemctl restart nginx, /usr/bin/systemctl status nginx ``` Always use full paths in sudo rules, and never allow commands that can spawn a shell or edit arbitrary files (editors, `less`, `find`, `bash`), because those give full root access in practice. ## Step 10 - Setting password aging For accounts that still use passwords, `chage` controls expiry. Show the current policy for `bob`: ```bash sudo chage -l bob ``` Require a change every 90 days, with a 7-day warning, and force a new password at the next login: ```bash sudo chage -M 90 -W 7 bob sudo chage -d 0 bob ``` These settings apply to password logins and to `sudo` prompts. Users who log in with SSH keys are not blocked by an expired password, but will have to change it the next time `sudo` asks for it. ## Troubleshooting **`Permission denied` on a file that looks readable**: the user also needs `x` on every parent directory. Check the whole path with `namei -l /srv/project/notes.md`. **A user does not get the new group's access**: group membership is read at login. Ask the user to open a new session and check with `id`. **Group members cannot edit each other's files in the shared directory**: the creator's umask removed group write. Check with `umask` and set `0002` as in Step 7, then fix existing files with `sudo chmod -R g+w /srv/project`. If files must always be group-writable regardless of umask, add a default ACL for the group: `sudo setfacl -d -m g:developers:rwX /srv/project`. **`sudo: parse error in /etc/sudoers.d/...`**: the file was edited without `visudo`. Fix it with `sudo visudo -f /etc/sudoers.d/developers`, or, if sudo no longer works at all, with `pkexec visudo` or from the server's web console as root. ## Conclusion You created users and groups, read and changed permissions in both notations, built a shared directory with setgid, added an ACL for a user outside the group, and delegated specific commands through sudo. Next, you can harden SSH so each of these users logs in with their own key, review group memberships periodically with `getent group`, and apply the same ownership model to your application directories under `/srv` or `/var/www`.