StackrootServer-side web stacks and the Linux systems that hold them, from config to debug.

Linux User Accounts

Linux user accounts are the named identities that the Linux Kernel assigns to every person and process that signs in to the system, and the full list of them lives in the /etc/passwd file, which holds one line per account with the login name, the user ID, the group ID, the home directory, and the login shell.

A terminal displaying a list of system users with their group memberships.

Managing Linux user accounts is the routine an admin runs to list users, add new ones, place them in groups, and change their passwords, and every step lands in one of 4 files: /etc/passwd for the account itself, /etc/shadow for the password hash, /etc/group for the group membership, and /etc/gshadow for the group password. This page walks through all 4 tasks on a modern distribution, so the commands below assume Ubuntu Server 24.04 or Debian 12, where the useradd and usermod tools ship in the passwd package by default.

How to list users on Linux

To list users on Linux, you read the same 3 files the system reads, and each file answers a different question about who is on the box. The table below maps each question to the file that answers it and the command that prints the answer.

Question File Command
Who has an account? /etc/passwd cut -d: -f1 /etc/passwd
Who has a login shell? /etc/passwd getent passwd | grep -E '/(ba)?sh$'
Who belongs to a group? /etc/group getent group sudo
Is a password set? /etc/shadow getent shadow | cut -d: -f1,2

Each line in /etc/passwd carries 7 colon separated fields, and the 2 you check most are field 3, the primary group ID, and field 7, the login shell. Accounts that cannot log in have /usr/sbin/nologin written in field 7, and the getent filter above strips them out, which is how you count real people on the box instead of 40+ system services. The getent group sudo line ends with a comma separated member list, and a trailing comma with nothing after it means the group currently has no members at all. To see who actually has a password, the getent shadow | cut -d: -f1,2 pair prints a ! or a * in the hash field for accounts that can only log in with an SSH key or not at all, and those 2 symbols are the difference between an account that is locked and one that is open.

How to add a user on Linux

To add a user on Linux, you run useradd with the flags that say what to create, and the flags decide whether the new account can log in at all. The 2 commands below cover the everyday case and the hardened case.

The everyday command is useradd -m -s /bin/bash -G docker,jenkins deploy, and each flag does one job: the -m flag creates the home directory from the skeleton in /etc/skel, the -s flag sets the login shell, and the -G flag appends the account to the docker and jenkins supplementary groups. The hardened command is useradd -m -s /usr/sbin/nologin -U app1, which creates a dedicated group named app1 and locks the login shell, so the account can run a service but cannot sign in from a terminal. Both commands end with the same chpasswd step, echo 'app1:Correct-Horse-Battery-9' | chpasswd, because a useradd without a password leaves the account with a locked hash, and a locked hash in /etc/shadow means every password login attempt fails until someone writes a real one. The useradd tool writes the new line into /etc/passwd with the next free user ID, and on a fresh Debian 12 install that counter starts at 1000, so the first human account you add becomes user ID 1000, which is why the user ID range 1000 to 59999 is the range you memorize for human accounts versus the 0 to 999 range for system services.

How to add a user to a group

To add a user to a group, you append the login name to the group line, and the usermod -aG form is the only one that is safe, because the -a flag is what stops it from replacing the existing membership list. The command is usermod -aG sudo carol, and the moment it runs, the sudo line in /etc/group gains carol at the end of its member list. The failure mode to remember is running usermod -G sudo carol without the a flag, which rewrites that same line so that sudo becomes the only group carol belongs to and every earlier membership is deleted in one pass. Membership changes only take effect at the next login, so a session that was open when the change ran still carries the old group list, and the way to verify the new state without waiting for a relogin is to run id carol, which prints the user ID, the group ID, and every supplementary group in one line. The same append logic runs in the other direction with gpasswd -d carol sudo, which removes carol from the group without touching any of her other memberships, and with gpasswd -M sudo carol, which sets the full member list in one shot for the case where the group should contain exactly one person.

How to change a user password on Linux

To change a user password on Linux, you replace the hash in /etc/shadow, and the command you pick depends on whether you are the user, an admin, or a script. A user changes their own password with passwd, which verifies the current hash first, then asks for the new secret twice, and refuses to write a password that the cracklib dictionary check scores below the policy floor. An admin changes someone else's password with sudo passwd carol, and the current password prompt is skipped because root is trusted to reset it. A script changes many passwords in one pass with chpasswd, which reads lines of the form carol:NewSecret123 from standard input and writes one hash per line. The hash that chpasswd writes on Debian 12 is an argon2id string that starts with $argon2id$ and carries its cost parameters inside it, so a shadow file from an old MD5 era box and one from a current box look nothing alike, and the PAM module reads that prefix to decide which algorithm to verify with. The policy behind the prompts lives in /etc/login.defs, and the 3 lines that matter are PASS_MAX_DAYS 90, PASS_MIN_DAYS 7, and PASS_WARN_AGE 14, which together cap a password at 90 days of life, floor it at 7 days of minimum age, and start warning 14 days before expiry.

Where user management sits in the Linux stack

User management on Linux is a stack of 4 layers, and knowing which layer a command touches tells you where a change takes effect and where a mistake lives. Layer 1 is the files, the 4 plain text records in /etc that every tool reads and writes, and an edit made by hand with a text editor bypasses every check the tools would otherwise run. Layer 2 is the PAM stack, the modules in /etc/pam.d that decide whether a login attempt is allowed, which is where a 30 second lockout after 5 failed password attempts is configured, and where the argon2id hash from the previous section gets verified against the typed secret. Layer 3 is the kernel itself, because the Linux Kernel turns a login into a user ID on every system call, and the setuid bit on a binary is the kernel trusting that file to run as a different user ID. Layer 4 is the application, and a web server that runs PHP as www-data inherits that user ID for every request it serves, which is why a directory that only deploy can write to is exactly the boundary between the web user and your deployment scripts. The tools that sit across these layers are the ones worth learning the flags of, and the Linux Commands in the /usr/bin/passwd package are small, careful writers to the files layer, while the audit tools in the auditd package and the file search utilities that let you Find Files on Linux by owner before you delete an account reach out to the kernel layer to answer who owns what on disk. The audit trail for all of it lands in the log, where every passwd and useradd event writes a line to /var/log/auth.log, and the last line of that log is the first place you look when a user reports that a password they just changed is not working, because the log names the exact PAM module that said no.

How MySQL user accounts differ from Linux user accounts

MySQL user accounts are a separate namespace that the database server maintains in its own tables, and the difference from a Linux account shows up the moment a PHP script connects and the connection is refused. A Linux account exists in /etc/passwd and is verified by PAM, while a MySQL account exists in the mysql.user table and is verified by the mysqld process, which means a user named deploy on the box and a user named deploy in the database are 2 unrelated identities that happen to share a name. The grant syntax makes the separation explicit: CREATE USER 'deploy'@'10.0.0.%' IDENTIFIED BY 'StrongPass9'; GRANT SELECT, INSERT ON shop.* TO 'deploy'@'10.0.0.%'; creates the database identity scoped to the 10.0.0.0 subnet, and that subnet scope has no counterpart anywhere at the operating system layer. The practical rule for an admin is that every service that opens a database connection needs 2 identities, one Linux account that owns the process and one MySQL account that owns the rows, and Managing MySQL user accounts is a separate routine with its own password aging, its own lockout, and its own audit trail in the general log of mysqld. When a production incident asks who wrote to the shop table, the answer lives in the MySQL general log, not in /var/log/auth.log, and that distinction is the line that keeps the two namespaces from being mixed up in an investigation.

The 4 tasks on this page, list, add, group, and password, close the loop an admin runs every time a new engineer joins a team or one leaves, and the files, PAM, kernel, and application layers keep each change scoped to where it belongs, which is the whole point of keeping user management on Linux out of one giant script and into 4 small, auditable commands.

Where to go next