Kokil Thapa - Professional Web Developer in Nepal
Freelancer Web Developer in Nepal with 15+ Years of Experience

Kokil Thapa is an experienced full-stack web developer focused on building fast, secure, and scalable web applications. He helps businesses and individuals create SEO-friendly, user-focused digital platforms designed for long-term growth.

Manage Users and Groups on Linux

By Kokil Thapa | Last reviewed: September 2026

When you deploy a Laravel app or WordPress site on Ubuntu, you must manage users and groups on Linux correctly or permissions break within days. A web app owned by root blocks PHP-FPM writes. A cron job running as the wrong user reads a stale release path. On production servers I maintain, user and group setup is not optional housekeeping—it is part of every Linux system administration workflow. This guide walks through real commands, config files, and fixes you can copy on Ubuntu 22/24 today.

What files does Linux use to manage users and groups?

Linux stores account data in plain-text databases under /etc. You rarely edit them by hand, but you must know what each file controls when debugging login or permission errors.

  • /etc/passwd — username, UID, GID, home directory, default shell.
  • /etc/shadow — password hashes and expiry rules (root-readable only).
  • /etc/group — group name, GID, and member list.
  • /etc/gshadow — group passwords and admin members (seldom used on modern servers).
  • /etc/sudoers — who may run commands as root via sudo.

A typical /etc/passwd line looks like this:

deploy:x:1001:1001:Deploy User:/home/deploy:/bin/bash

The fields are: login name, placeholder password (x means hash lives in shadow), UID, primary GID, GECOS comment, home path, login shell.

On Ubuntu, system accounts often use UIDs below 1000. Human and app accounts usually start at 1000. Keep UIDs stable across servers if you NFS-mount storage or rsync backups between hosts. Changing a UID after files exist is painful—every owned file must be updated.

Linux User and Group Data Model/etc/passwdUID, home, shell/etc/shadowPassword hashes/etc/groupGID, membersLogin sessionSSH, sudo, cronFile ownershipuser:group modeUID and GID tie accounts to permissions on disk
How passwd, shadow, and group files connect when you manage users and groups on Linux

For deeper permission work, pair this with our guide on Linux file permissions and ACLs. The UID and GID numbers you assign here directly drive every chmod decision on the filesystem.

How do you create, modify, and delete users on Linux?

On Debian and Ubuntu, prefer adduser for interactive human accounts and useradd for scripted service accounts. Both wrap the same underlying databases.

Create a human user interactively

sudo adduser alice

Ubuntu prompts for password and optional profile fields. Verify the account:

id alice
getent passwd alice

Create a service account without login shell

App deploy users should not accept SSH passwords. I use this pattern on Laravel servers:

sudo useradd -m -s /bin/bash -G www-data deploy
sudo mkdir -p /home/deploy/.ssh
sudo chmod 700 /home/deploy/.ssh
sudo nano /home/deploy/.ssh/authorized_keys
sudo chown -R deploy:deploy /home/deploy/.ssh

Lock password login so only keys work:

sudo passwd -l deploy

Generate a strong passphrase for any break-glass account with the password generator if you store emergency credentials offline.

Modify an existing user

sudo usermod -aG sudo alice
sudo usermod -d /var/www/alice -m alice
sudo usermod -s /usr/sbin/nologin oldbatch

The -aG flag appends groups. Without -a, usermod -G replaces the entire supplementary group list—a common production mistake.

Delete a user safely

  1. Confirm no cron jobs or systemd units reference the account (cron jobs on Linux often hard-code usernames).
  2. Archive or reassign owned files: sudo find / -user alice 2>/dev/null.
  3. Remove the account: sudo deluser --remove-home alice on Ubuntu, or sudo userdel -r alice elsewhere.

Our Ubuntu user management guide covers similar flows with desktop-focused notes. Server admins should still read both—desktop and headless setups diverge on defaults.

User Provisioning WorkflowuseraddSet shelland home dirAdd groupswww-data etcSSH keysVerify idsudo accessProduction readyNever skip verify—wrong GID breaks deploy scripts silently
Standard workflow to manage users and groups on Linux before granting production access

How do you create groups and manage membership on Linux?

Groups bundle permissions. A user has one primary group (stored in /etc/passwd) and zero or more supplementary groups (stored in /etc/group).

Create and inspect groups

sudo groupadd developers
getent group developers
groups alice

Rename or change GID only before files depend on the number:

sudo groupmod -n devteam developers
sudo groupmod -g 1500 developers

Add and remove members

sudo usermod -aG developers alice
sudo gpasswd -d alice developers
sudo delgroup developers

Common Ubuntu groups worth knowing:

GroupTypical purposeCaution
sudoFull admin via sudoLimit to trusted operators only
www-dataApache/Nginx and PHP-FPM on Debian/UbuntuApp code must be readable, not writable, by this group
dockerRun containers without rootEquivalent to root on many setups
admRead log files in /var/logUseful for support staff
deploy (custom)CI/CD release ownershipPair with www-data for shared write dirs

On sister sites I deploy with Deployer 7 and GitLab CI—projects like Notary Kathmandu—the deploy user owns releases while PHP-FPM runs as www-data. Shared directories such as storage/ and bootstrap/cache/ get group www-data and mode 2775 so new files inherit the group. That pattern prevents upload failures without opening the whole tree to the web server user.

Official reference: the Debian Administrator's Handbook section on user accounts documents these tools consistently across releases. For RHEL-family systems, see the Red Hat documentation on useradd and groupadd—flags differ slightly, but the concepts match.

How should you configure sudo and service accounts in production?

Shared root passwords are unacceptable on any server you care about. Grant privilege through sudo with scoped rules instead.

Safe sudo patterns

Add a user to the sudo group on Ubuntu:

sudo usermod -aG sudo alice

For finer control, create a file in /etc/sudoers.d/—never edit /etc/sudoers directly without visudo:

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

Example: allow deploy to reload PHP-FPM without full root:

deploy ALL=(root) NOPASSWD: /bin/systemctl reload php8.3-fpm

Validate syntax before saving—bad sudoers files can lock you out. Related reading: manage services on Linux with systemd.

Service account checklist

  • Dedicated Unix user per app or per site on shared hosts.
  • No password login; SSH keys or local socket auth only.
  • Shell /usr/sbin/nologin for database-only accounts if they never need SSH.
  • Document UID/GID in your runbook or Ansible inventory.
  • Rotate keys when staff leave—group membership persists until removed.

MySQL and PostgreSQL create their own system users (mysql, postgres) during package install. Do not run mysqld as root. When you automate database backups on Linux, run dump scripts as a user that can read credentials but cannot sudo.

Production Ownership ModelWrong: root owns appPHP cannot write storageDeploy needs sudo dailyRight: deploy + groupdeploy:www-data on code2775 on storage dirsShared release layoutcurrent -> releases/20260315deploy writes; www-data reads and uploads
Correct user and group ownership when you manage users and groups on Linux for PHP and Laravel deployments

For hosting clients who lack in-house ops staff, this is exactly the work covered under support and maintenance and domain registration and hosting—getting ownership right once saves hours of ticket noise later.

How do you troubleshoot UID, GID, and permission problems?

Most "it worked yesterday" Linux permission bugs trace back to a UID mismatch after restore, tarball extract, or manual chown.

Diagnose quickly

ls -la /var/www/example/current/storage
id deploy
id www-data
stat -c '%U:%G %a' /var/www/example/current/storage/logs

Find orphaned files after deleting a user:

sudo find /var/www -nouser -o -nogroup 2>/dev/null

Fix ownership recursively when you are sure of the target:

sudo chown -R deploy:www-data /var/www/example/shared/storage
sudo chmod -R ug+rwx /var/www/example/shared/storage
sudo find /var/www/example/shared/storage -type d -exec chmod g+s {} \;

The setgid bit (g+s, shown as s in group execute) keeps new uploads group-writable by www-data.

UID collisions after migration

Restoring a backup onto a fresh VPS often maps files to the wrong names. User deploy might be UID 1001 on the old box but 1002 on the new one. The numeric UID on disk still says 1001—which now belongs to someone else entirely.

Fix options, in order of preference:

  1. Recreate users with explicit UIDs: sudo useradd -u 1001 -o deploy (use -o only when necessary).
  2. Mass chown after confirming the correct mapping.
  3. Re-deploy from Git instead of rsyncing storage/ with preserved numeric owners.

I've hit this during website migration work when clients move off cheap shared hosting. Always export /etc/passwd and /etc/group from the source server before cutover.

Audit and compliance basics

List human accounts with login shells:

getent passwd | awk -F: '$7 !~ /nologin|false/ {print $1, $3, $7}'

Find members of privileged groups:

getent group sudo
getent group docker

Pair this with Linux server monitoring and log rotation so audit trails do not fill the disk. Lock inactive accounts:

sudo passwd -l formeremployee
sudo usermod -L formeremployee

External authority: the passwd(5) man page defines field layout, and Ubuntu documents adduser in the official server guide at ubuntu.com/server/docs.

Permission Denied Debug TreePermission denied?Check owner UIDCheck group GIDusermod / chowngpasswd / chmod g+sRetest as www-data
Decision flow when you manage users and groups on Linux to resolve permission errors

More command reference lives in essential Linux commands for Ubuntu users and Linux process management. Firewalls matter too—a misconfigured sudo rule is an internal risk; pair user hygiene with iptables vs nftables hardening.

Key Takeaways

  • Manage users and groups on Linux through useradd, usermod, groupadd, and gpasswd—not by hand-editing /etc/passwd.
  • Always use usermod -aG when adding supplementary groups; omitting -a wipes existing memberships.
  • Separate deploy users from www-data; use group ownership and setgid on shared write directories.
  • Grant admin rights via scoped /etc/sudoers.d/ files instead of sharing the root password.
  • Document UID/GID before migrations; fix numeric ownership before debugging application code.
  • Audit sudo and docker group membership regularly on every production VPS.

People Also Ask

What is the difference between a primary group and supplementary groups?

The primary group is the GID on the /etc/passwd line. It applies to new files the user creates unless overridden. Supplementary groups come from /etc/group and grant shared access—such as letting deploy also act as www-data on upload folders. Run id username to see both.

Should I use adduser or useradd on Ubuntu?

Use adduser for interactive human accounts—it handles home directories, skeleton files, and password prompts. Use useradd in shell scripts and Ansible when you need explicit flags for service accounts. Both update the same backend files.

How do I list all users on a Linux server?

Run getent passwd to list every passwd entry from local files and NSS sources like LDAP. Filter login-capable users with awk on the shell field. For group membership, run getent group groupname.

Why do files show numeric UID instead of a username after restore?

The username lookup failed because that UID has no matching entry in /etc/passwd. Recreate the user with the same UID or run chown to assign files to the correct account. This is one of the most common post-migration surprises on VPS hosts.

Build a clean account model before your next deploy

Manage users and groups on Linux with the same discipline you apply to firewall rules and database backups. Get UID, GID, and sudo scope right on day one, and PHP-FPM, cron, and CI pipelines stop fighting each other. Onboarding a new server takes less than an hour when you follow a checklist; fixing ownership across years of uploads takes days.

If you run production apps on Ubuntu and want ownership, sudo, and deploy users configured properly from the start, see the Adventure Third Pole Trek stack and other portfolio deployments for real examples—or read more from about me and Linux performance tuning basics. When you need hands-on help, contact us for server setup aligned with how you actually ship code.

Frequently Asked Questions

The primary group is the GID on the /etc/passwd line and applies to new files the user creates. Supplementary groups come from /etc/group and grant shared access, such as deploy also acting as www-data on upload folders. Run id username to see both.

Use adduser for interactive human accounts—it handles home directories, skeleton files, and password prompts. Use useradd in shell scripts and Ansible when you need explicit flags for service accounts. Both update the same backend files under /etc/passwd and /etc/shadow.

Run getent passwd to list every passwd entry from local files and NSS sources like LDAP. Filter login-capable users with awk on the shell field. For group membership, run getent group groupname.

Linux stores account data under /etc. /etc/passwd holds username, UID, GID, home directory, and default shell. /etc/shadow stores password hashes and expiry rules, readable only by root. /etc/group lists group name, GID, and members. /etc/gshadow covers group passwords and admin members, though it is seldom used on modern servers. /etc/sudoers defines who may run commands as root via sudo. You rarely edit these by hand, but knowing each file is essential when debugging login or permission errors on Ubuntu production hosts.

On Laravel servers I use useradd with a home directory, bash shell, and www-data supplementary group: sudo useradd -m -s /bin/bash -G www-data deploy. Set up SSH keys under /home/deploy/.ssh with mode 700, chown deploy:deploy, then lock password login with sudo passwd -l deploy so only keys work. This pattern appears on Deployer 7 and GitLab CI deployments where the deploy user owns releases while PHP-FPM runs as www-data. Never let the deploy account accept password-based SSH on production.

The -aG flag appends a user to supplementary groups without touching existing memberships. Running usermod -G without -a replaces the entire supplementary group list—a common production mistake that silently removes sudo, www-data, or docker access. I have seen operators lose deploy permissions mid-release because they added one group and wiped the rest. Always append with -aG, then verify with id username and getent group groupname before granting production access or closing a change ticket.

Shared root passwords are unacceptable on any server you care about. Add trusted operators to the sudo group with sudo usermod -aG sudo alice. For finer control, create scoped rules in /etc/sudoers.d/ using visudo—never edit /etc/sudoers directly. Example: allow deploy to reload PHP-FPM without full root via deploy ALL=(root) NOPASSWD: /bin/systemctl reload php8.3-fpm. Validate syntax before saving, because a bad sudoers file can lock you out entirely. Audit sudo and docker group membership regularly on every production VPS.

The deploy user should own releases while PHP-FPM runs as www-data. Shared directories such as storage/ and bootstrap/cache/ get group www-data and mode 2775 so new files inherit the group. That prevents upload failures without opening the whole tree to the web server user. On sister sites deployed with Deployer 7 and GitLab CI, this setgid pattern stops PHP-FPM and CI pipelines from fighting over write access. Apply chown -R deploy:www-data on shared storage, chmod ug+rwx, then set g+s on directories so uploads stay group-writable by www-data.

Most permission bugs trace back to UID mismatch after restore, tarball extract, or manual chown. Diagnose with ls -la on the target path, id deploy, id www-data, and stat -c '%U:%G %a' on storage/logs. Find orphaned files with sudo find /var/www -nouser -o -nogroup. Fix ownership when you are sure of the target: sudo chown -R deploy:www-data on shared storage, chmod ug+rwx, then find -type d -exec chmod g+s {} \; so new uploads inherit www-data group write access. Confirm cron jobs and systemd units are not running as the wrong user before blaming application code.

sudo grants full admin via sudo—limit to trusted operators only. www-data is used by Apache/Nginx and PHP-FPM on Debian/Ubuntu; app code must be readable, not writable, by this group. docker lets users run containers without root and is equivalent to root on many setups—treat membership carefully. adm allows reading log files in /var/log, useful for support staff. A custom deploy group pairs with www-data for CI/CD release ownership on shared write directories. Audit membership in sudo and docker regularly, because group access persists until explicitly removed when staff leave.

Confirm no cron jobs or systemd units reference the account, because cron jobs on Linux often hard-code usernames. Archive or reassign owned files first: sudo find / -user alice 2>/dev/null. Remove the account with sudo deluser --remove-home alice on Ubuntu, or sudo userdel -r alice elsewhere. After deletion, check for orphaned files with find -nouser -o -nogroup under /var/www and reassign them before they cause permission errors for remaining services. Document the removal in your runbook so future migrations do not recreate the same UID accidentally.

Restoring a backup onto a fresh VPS often maps files to the wrong names. User deploy might be UID 1001 on the old box but 1002 on the new one, while numeric ownership on disk still says 1001—which now belongs to someone else. Fix options in order of preference: recreate users with explicit UIDs using sudo useradd -u 1001 -o deploy, mass chown after confirming the correct mapping, or re-deploy from Git instead of rsyncing storage/ with preserved numeric owners. Always export /etc/passwd and /etc/group from the source server before cutover.

Use a dedicated Unix user per app or per site on shared hosts. Disable password login; rely on SSH keys or local socket auth only. Set shell to /usr/sbin/nologin for database-only accounts that never need SSH. Document UID and GID in your runbook or Ansible inventory. Rotate keys when staff leave, because group membership persists until removed. MySQL and PostgreSQL create their own system users during package install—do not run mysqld as root. When automating database backups, run dump scripts as a user that can read credentials but cannot sudo.

The username lookup failed because that UID has no matching entry in /etc/passwd.

List human accounts with login shells using getent passwd piped through awk on the shell field to exclude nologin and false entries. Find members of privileged groups with getent group sudo and getent group docker. Lock inactive accounts with sudo passwd -l formeremployee and sudo usermod -L formeremployee. Pair this audit work with Linux server monitoring and log rotation so audit trails do not fill the disk. Review results after staff changes, because supplementary group membership in sudo or docker is not removed automatically when someone stops using the server.

Share this article

0 Comments

Leave a comment

Your email is not published. Comments appear once they have been read. Sign in to have your details filled in.

Quick Contact Options
Choose how you want to connect me: