1. Introduction

Systems exist to serve their users. While a user can be a human or another machine, accounts set permissions, storage, and general usage boundaries. In most cases, we have tools like useradd and adduser to set up and configure a new Linux account.

In this tutorial, we explore how to create a Linux user without using purpose-built commands by replicating their process step-by-step. First, we briefly summarize the steps that user creation tools employ. Next, we follow them one by one. Finally, we perform integrity checks to confirm no errors were made.

We tested the code in this tutorial on Debian 11 (Bullseye) with GNU Bash 5.1.4. It should work in most POSIX-compliant environments.

2. Linux User Creation Process

To manually create a user, we follow each step that a regular tool like useradd would:

  1. Append a line to the main user data file
  2. Append a line to the main group data file
  3. Create the user home directory
  4. Create a matching entry in the hashed user and group files
  5. Perform integrity checks

Indeed, there are several reasons for using tools to automate user adding instead of performing the actions manually:

  • syntax preservation
  • error checking
  • consistent information entry

Because of these and the sensitivity of our actions, we perform integrity checks after all other actions.

3. /etc/passwd

To begin with, the first step to user creation is an entry in the /etc/passwd, which holds basic user information in seven colon-separated columns per unique user row:

  1. Username – limited to around 1-32 characters on most distributions
  2. Password – stored hashed, where x means the actual hash should be in /etc/shadow
  3. User ID – unique numeric value
  4. Group ID
  5. User information – comma-delimited fields for additional free-text data
  6. Home directory – absolute path
  7. Command or shell – run on login

When editing /etc/passwd directly, it’s best to use a tool like vipw. Essentially, vipw locks /etc/passwd just like visudo does for the /etc/sudoers file. Importantly, vipw doesn’t automatically check the syntax after any edits.

Let’s append the following line with vipw for our new user baeldung:

$ vipw
[...]
baeldung:x:1010:1010:,,,:/home/baeldung:/bin/bash
[...]
You have modified /etc/passwd.
You may need to modify /etc/shadow for consistency.
Please use the command 'vipw -s' to do so.

Here, we make up the user and group ID numbers but ensure they are unique. Despite the warning telling us we need to add the necessary changes to /etc/shadow, we do that in the end for security reasons.

4. /etc/group

Since we added a made-up group ID for our user to /etc/passwd, we should now configure an actual group that matches it.

To do so, we turn to the /etc/group file. It contains a list similar to /etc/passwd but with only four colon-separated columns per unique group row:

  1. Group name – limited to around 1-32 characters on most distributions
  2. Password – stored hashed, where x means the actual hash should be in /etc/gshadow
  3. Group ID – unique numeric value
  4. List of comma-separated user names

Similar to the case with /etc/passwd, it’s recommended to edit /etc/group via the -g or –group flag to vigr, a symbolic link alias of vipw.

Let’s append a line with our group ID from earlier:

$ vigr --group
[...]
baeldung:x:1010:
[...]
You have modified /etc/group.
You may need to modify /etc/gshadow for consistency.
Please use the command 'vigr -s' to do so.

Notably, we don’t add the name of our user to the last field, effectively leaving it empty since group ID 1010 is already the primary group for that user as specified in /etc/passwd.

5. Home Path

At this point, we have matching user and group entries. Yet, our user row in /etc/passwd also contains a home path we need to create.

5.1. Create Home Directory

Of course, creating the directory itself is simple with mkdir. However, new user home paths usually begin with the copied contents of the /etc/skel directory, i.e., a [skel]eton template:

$ cp --recursive /etc/skel /home/baeldung

In particular, for non-existent destinations, we can use cp with its –recursive or -r flag, which effectively copies the source under a different name.

5.2. Set Permissions

Critically, a user’s home should be their own. Let’s check the permissions of our newly-created directory:

$ ls -l /home
[...]
drwxr-xr-x 2 root root 4096 Oct 20 20:10 /home/baeldung

In our case, root and not baeldung has ownership of the path. Let’s correct that:

$ chown --recursive baeldung:baeldung /home/baeldung
$ chmod --recursive 0755 /home/baeldung
$ ls -l /home
[...]
drwxr-xr-x 2 baeldung baeldung 4096 Oct 20 20:10 /home/baeldung

For example, in our environment, users own their homes, and their permissions are rwxr-xr-x, mode 0755. So, we use chown and chmod to enforce both recursively (–recursive, -r).

Naturally, we can apply more restrictive permissions like 0700, if required.

6. Shadow Files

While group passwords in /etc/gshadow are relatively rare, users with blank passwords are a liability.

For this reason, we use the passwd command to set a password for our baeldung user, as it automatically populates /etc/shadow and /etc/gshadown:

$ passwd baeldung
New password:
Retype new password:
passwd: password updated successfully

Now, we have a user with the name baeldung and a user ID 1010. This new user is in the baeldung group with group ID 1010.

7. Integrity Checks and Login

Finally, let’s employ pwck, the password file integrity checker, to confirm our settings are correct:

$ pwck
pwck: no changes

Optimally, like in this case, pwck produces no output about our username, user ID, group, or group ID, because our changes are correct. In case of issues like missing /etc/shadow entries, pwck might be able to supply prompts and correct problems based on the answers.

At this point, we can log in as our new user via the su command:

$ su baeldung
Password:
$ whoami
baeldung

After entering the correct password, we confirm our identity with whoami.

8. Summary

In this article, we performed all steps necessary to create a user manually without the help of useradd and adduser.

First, we added an entry to /etc/passwd via vipw. After that, we did the same for /etc/group, but with vigr –group. Next, we created the respective home directory for our entries by copying /etc/skel. Following that, permissions were set for the new home path. Finally, we modified both /etc/shadow and /etc/gshadow with the passwd command.

Of course, all file entries can be appended without the help of external utilities as long as we correctly calculate the password hash.

In conclusion, while executing new user creation steps manually is a viable option, it’s usually better to use dedicated tools.

Comments are open for 30 days after publishing a post. For any issues past this date, use the Contact form on the site.