Create a User Home Safely with mkhomedir_helper
New account, missing home directory, first login already failing: that is exactly what mkhomedir_helper is for. This guide creates the missing home, copies in the skeleton files and checks the result without touching an existing home by mistake. It uses mkhomedir_helper from libpam-modules-bin version 1.5.3-5ubuntu5.7.
The route
Jump straight to the step you need, or tick off Done means at the end.
Allow about fifteen minutes. You need an existing account whose home directory does not yet exist, a shell, and elevated privileges for the creation step; ordinary inspection can stay unprivileged. There is no dry-run mode in the installed command.
1. Check the account before changing anything
Choose the account first. Replace NEWUSER with an account that already exists, not a name you want the helper to create:
$ getent passwd NEWUSER
NEWUSER:x:1001:1001:Example User:/home/NEWUSER:/bin/bash
$ home=$(getent passwd NEWUSER | cut -d: -f6)
$ printf 'home: %s\n' "$home"
home: /home/NEWUSER
$ test -e "$home"; printf 'home exists: %s\n' "$?"
home exists: 1
The helper uses the account's configured home path. A final status of 1 above means that path does not exist yet. Check it carefully before reaching for sudo: a typo in the username selects a different account, and a typo in a manually supplied path can put files somewhere unexpected.
Checkpoint
Stop if the account is missing or the home already exists. The helper is deliberately conservative: it never touches a home directory that is already present.
2. Inspect the command shape and defaults
Read the installed manual page and confirm the binary you will actually run:
$ command -v mkhomedir_helper
/usr/sbin/mkhomedir_helper
$ dpkg-query -W -f='${Package} ${Version}\n' libpam-modules-bin
libpam-modules-bin 1.5.3-5ubuntu5.7
$ man 8 mkhomedir_helper
The syntax is:
mkhomedir_helper USER [UMASK [SKELDIR [HOME_MODE]]]
Only USER is required. Leave the rest out and the installed manual documents an umask of 0022, /etc/skel as the skeleton directory, and a home mode computed from the umask. That skeleton directory supplies the new home's initial contents, so check what it actually contains rather than assuming an application-specific skeleton exists:
$ sudo test -d /etc/skel && echo '/etc/skel is available'
/etc/skel is available
$ sudo find /etc/skel -maxdepth 1 -mindepth 1 -printf '%f\n' | sort
The sudo in the first command is only there because the example asks it to perform the check. If your account can already read the directory, both commands run fine without it.
3. Create the missing home with the defaults
Once the username and home path have passed the checks, run the helper with only the username:
$ sudo mkhomedir_helper NEWUSER
Normal completion is quiet. A zero exit status is your checkpoint:
$ printf 'helper status: %s\n' "$?"
helper status: 0
$ sudo stat -c 'owner=%U group=%G mode=%a path=%n' "$home"
owner=NEWUSER group=NEWUSER mode=<calculated mode> path=/home/NEWUSER
$ sudo find "$home" -maxdepth 1 -mindepth 1 -printf '%f\n' | sort
The exact mode and file list depend on the account, the installed defaults and the contents of /etc/skel. Verify ownership and permissions rather than treating the sample output above as a promise: the helper copies the skeleton contents and applies ownership as part of creating the home, but it does not print a success report.
Tip
Do not run the command a second time expecting it to refresh dotfiles. Once the directory exists, the helper does nothing to it, which protects an existing home but also means changing /etc/skel later never updates old accounts.
4. Use an explicit skeleton and mode only when required
Reach for the optional arguments when an account needs a different initial template or policy. This keeps the documented umask, selects a named skeleton directory, and passes a home mode explicitly:
$ sudo mkhomedir_helper NEWUSER 0022 /srv/skel/interactive 0750
Every optional argument is positional, so you cannot supply HOME_MODE while skipping UMASK or SKELDIR. Check the custom directory first:
$ sudo test -d /srv/skel/interactive && echo 'skeleton is available'
skeleton is available
$ sudo find /srv/skel/interactive -maxdepth 1 -mindepth 1 -printf '%f\n' | sort
This is another state-changing command with the same precondition as the default form: the target home must not already exist. Treat the skeleton as trusted input, since files copied into a new account home can affect shell startup and user configuration.
Use a leading zero for an octal-style value such as 0750, and keep the values aligned with your local policy. A malformed argument makes the program report a bogus value and exit without a useful partial-success summary, so inspect the target afterwards rather than assuming a shell error undid every filesystem operation.
5. Test the workflow in a disposable location
Learn the behaviour on a throwaway account before touching a real one. This changes the local account database and creates files, so run it only on a test machine or in an approved maintenance window:
$ demo_user="mkhomedir-demo-$$"
$ demo_home="/tmp/$demo_user"
$ sudo useradd --no-create-home --home-dir "$demo_home" --shell /bin/sh "$demo_user"
$ sudo mkhomedir_helper "$demo_user"
$ sudo stat -c 'owner=%U group=%G mode=%a path=%n' "$demo_home"
owner=mkhomedir-demo-12345 group=mkhomedir-demo-12345 mode=<calculated mode> path=/tmp/mkhomedir-demo-12345
$ sudo find "$demo_home" -maxdepth 1 -mindepth 1 -printf '%f\n' | sort
Your process ID and resulting account identifiers will differ. What matters is that the helper exits successfully, the temporary home now exists, and its owner matches the test account. Use a unique name and confirm it does not already exist before creating the account.
When you are finished, remove only this disposable account and the exact temporary home you just inspected:
$ sudo userdel "$demo_user"
$ sudo rm -rf -- "$demo_home"
$ test ! -e "$demo_home" && echo 'temporary home removed'
Warning
rm -rf is irreversible. Do not substitute a real home path, an empty variable, or a broad directory. If you need to keep the test account for another check, skip cleanup and remove it later through your normal account-management procedure.
6. Diagnose a failed creation
Capture the status immediately after the helper runs, before anything else:
$ sudo mkhomedir_helper NEWUSER
$ status=$?
$ printf 'helper status: %s\n' "$status"
helper status: 0
Status 0 means normal completion. A missing username or an unusable configured home will fail instead. Recheck the account record, its configured home, the parent directory permissions and the skeleton path; do not guess at an option, since the installed command accepts only the positional arguments shown above and documents no force, verbose or dry-run switch.
If the home now exists after a non-zero status, stop and inspect it before rerunning anything:
$ sudo stat -c 'owner=%U group=%G mode=%a path=%n' "$home"
$ sudo find "$home" -maxdepth 1 -mindepth 1 -printf '%f\n' | sort
Recovery
Do not delete or overwrite a partially populated real home as an automatic recovery step. Preserve it for review, compare it with the expected skeleton, and use your account backup or change-control process if files need restoring. The helper's no-touch rule only applies when the home already existed before invocation; it is not a transaction system for every failure during creation.
Done means
- The account exists and its configured home path was checked before elevation.
- The home was absent before the helper ran, or the helper was correctly left alone because it already existed.
- The chosen skeleton and positional arguments were verified rather than guessed.
- The helper returned status
0, and ownership, mode and initial contents were inspected. - No real home was removed during recovery; disposable test data was cleaned up only after its exact path was confirmed.