There was a time when adding a Mac OS X user from the command line meant talking
directly to NetInfo with nicl, copying a user template with ditto, and
hoping you did not fat-finger a UID. That was useful knowledge in 2007. It is
also very much not how I would tell someone to manage macOS users today.
Modern macOS stores local users through Directory Services, and the command-line
tools have changed. If you are administering a Mac over SSH now, the practical
tools to know are sysadminctl, dscl, createhomedir, passwd, and the
usual Unix permissions commands.
This article keeps the original spirit of the old post: sometimes you only have remote shell access, and you still need to create a working account. The details are updated for modern macOS.
Before You Create the User
Remote user creation is one of those tasks where a little caution saves a lot of cleanup. Before touching account records, answer a few questions:
- Do you need a local account, or should this Mac be bound to a directory service or managed by MDM?
- Should the user be a standard user or an administrator?
- Does the account need GUI login, SSH access, or both?
- Is FileVault enabled?
- Does the account need a Secure Token?
- Are you about to put a password in shell history, logs, or automation output?
For one-off administration, a local account may be fine. For fleets, use MDM. Hand-building local accounts over SSH does not scale well, and it is easy to end up with inconsistent state across machines.
Also, do not pass real passwords on the command line if you can avoid it. Some
macOS tools support - or interactive prompting so the password is not exposed
in shell history or process listings. Use that whenever practical.
Option 1: Use sysadminctl
For most modern macOS systems, sysadminctl is the friendliest command-line
tool for creating a local user.
Here is the shape of the command:
sudo sysadminctl \
-addUser buildadmin \
-fullName "Build Admin" \
-shell /bin/zsh \
-home /Users/buildadmin \
-password -
The -password - form prompts for the password instead of putting it directly
in your command. That is the version you want when you are typing this by hand.
If the account should be an administrator, add -admin:
sudo sysadminctl \
-addUser buildadmin \
-fullName "Build Admin" \
-shell /bin/zsh \
-home /Users/buildadmin \
-admin \
-password -
That is intentionally boring, which is what I want from account creation. The
older NetInfo process required you to manually create records, set attributes,
copy a home directory, change ownership, and then set a password. sysadminctl
packages that into a command that is harder to get partially wrong.
There are still reasons to understand the lower-level tools, though. When
something looks strange, dscl is how you inspect what actually exists.
Inspect the Account with dscl
dscl is the Directory Service command-line utility. The local directory node is
represented by ..
After creating the account, inspect it:
dscl . -read /Users/buildadmin
For a tighter check:
dscl . -read /Users/buildadmin UniqueID PrimaryGroupID NFSHomeDirectory UserShell
You can list local users with:
dscl . -list /Users
And you can check group membership with:
dscl . -read /Groups/admin GroupMembership
This is the modern replacement for the old nidump and nicl workflow. The
mental model is similar: user and group records have attributes. The tooling and
backing directory system changed.
Option 2: Build the Account with dscl
I prefer sysadminctl for normal account creation, but dscl is useful when
you need more explicit control or when you are repairing a system with unusual
state.
First, choose a username and a UID. On a single Mac, you can inspect existing UIDs like this:
dscl . -list /Users UniqueID | sort -nk2
Then create the user record:
sudo dscl . -create /Users/buildadmin
sudo dscl . -create /Users/buildadmin UserShell /bin/zsh
sudo dscl . -create /Users/buildadmin RealName "Build Admin"
sudo dscl . -create /Users/buildadmin UniqueID 505
sudo dscl . -create /Users/buildadmin PrimaryGroupID 20
sudo dscl . -create /Users/buildadmin NFSHomeDirectory /Users/buildadmin
On macOS, PrimaryGroupID 20 is commonly the staff group. Do not blindly copy
UIDs or group IDs from a blog post into a production machine. Check the target
system first.
Set the password:
sudo passwd buildadmin
Create and populate the home directory:
sudo createhomedir -c -u buildadmin
sudo chown -R buildadmin:staff /Users/buildadmin
If the user needs administrator rights:
sudo dseditgroup -o edit -a buildadmin -t user admin
Then verify:
id buildadmin
dscl . -read /Users/buildadmin UniqueID PrimaryGroupID NFSHomeDirectory UserShell
dscl . -read /Groups/admin GroupMembership | grep buildadmin
This is more verbose than sysadminctl, but it teaches you what is actually
being created. That is still valuable when you are debugging broken accounts,
migration leftovers, or automation that failed halfway through.
SSH Access Is a Separate Question
Creating a local user does not automatically mean that account can SSH into the Mac. Remote Login has to be enabled, and access may be restricted.
Check whether SSH is enabled:
sudo systemsetup -getremotelogin
Enable it if appropriate:
sudo systemsetup -setremotelogin on
On managed Macs, this may be controlled by configuration profile or MDM policy. Do not fight the management layer from the shell unless you are deliberately repairing a machine and understand what policy will do next.
If SSH is slow or strange between Macs, the issue may not be the account at all.
The companion guide on macOS SSH slow connections
walks through the network side of the problem: IPv4 versus IPv6, .local name
resolution, Remote Login, and server-side SSH daemon checks. Keep those layers
separate from user creation and account state.
Secure Token and FileVault Caveats
Modern macOS account administration has one complication the old NetInfo days did not: Secure Token.
If FileVault is enabled, a user may need a Secure Token to unlock the disk at boot. Creating an account from SSH does not guarantee that the account can unlock FileVault or perform every local security operation.
Check token status:
sysadminctl -secureTokenStatus buildadmin
Granting a Secure Token generally requires credentials from an existing token-enabled administrator:
sudo sysadminctl \
-secureTokenOn buildadmin \
-password - \
-adminUser existingadmin \
-adminPassword -
This is an area where fleet management tooling is much better than artisanal SSH commands. If you are doing this often, the right answer is probably MDM, Bootstrap Token, and a documented onboarding flow rather than clever shell snippets.
Cleanup and Removal
If you create the wrong account, clean it up deliberately.
With sysadminctl:
sudo sysadminctl -deleteUser buildadmin -secure
If you want to keep the home directory:
sudo sysadminctl -deleteUser buildadmin -keepHome
Before deleting anything, verify that you are on the right host:
hostname
scutil --get ComputerName
whoami
That sounds insultingly basic until the day you have SSH sessions open to three similar machines. Good operators build habits that protect them when attention is split.
Practical Recommendations
For a modern macOS system, my default approach is:
- Use MDM for repeatable fleet account policy.
- Use
sysadminctlfor one-off local user creation. - Use
dsclto inspect and repair Directory Services records. - Use
createhomedirwhen you need to explicitly create a home directory. - Avoid passwords in command history.
- Verify SSH access separately from account creation.
- Treat Secure Token and FileVault as first-class requirements, not afterthoughts.
The command line is still perfectly capable of creating a macOS user remotely, but the surrounding security model matters. A local admin account that cannot unlock FileVault, receive policy, or satisfy your audit requirements is not a finished account. It is just a record in the directory.
Conclusion
The old nicl recipe was useful in its time, but NetInfo is ancient history.
Modern macOS administration is cleaner in some ways and more security-aware in
others. sysadminctl gives you the high-level path. dscl gives you visibility
underneath it. createhomedir, passwd, and dseditgroup fill in the system
administration details when you need them.
Use the high-level tool when it does the job. Understand the lower-level records well enough to verify the result. And if you are doing this across more than a few machines, stop hand-rolling it over SSH and put the policy in your management system.
For more practical system administration notes, visit Slaptijack.