You need a new login account, a locked-down service identity, or just a group, and Debian's adduser handles all three with sane defaults baked in. This covers creating a normal user, a system account, a group, and adding an existing user to it, using the installed adduser 3.137ubuntu1 interface, also the version on this machine. Allow five minutes for a straightforward account and longer if you need to review local policy first.
You need root privileges for account and group changes. Use a real placeholder name in the commands below, then replace it deliberately; do not paste a production username into a test command. Check the current configuration and program version first:
adduser --version
sudo sed -n '1,220p' /etc/adduser.conf
The configuration file supplies defaults. On this installation, its documented defaults include /home for normal home directories, /bin/bash for normal login shells, UID and GID ranges beginning at 1000, and a private 0700 mode for normal home directories. Your file may have been changed; the values that matter most are DHOME, DSHELL, DIR_MODE, USERGROUPS, USERS_GROUP, EXTRA_GROUPS and the UID/GID ranges.
Checkpoint: decide whether this is a human login, a service identity, a group, or an existing-user membership change. The --system option is not a synonym for "more secure": it selects the system UID/GID ranges and service-account defaults, nothing more.
Run this as root. Supplying --comment avoids the interactive comment prompt; the password prompt is still intentional, since it lets you set the account's password through the normal password helper.
sudo adduser --comment 'Example Operator' exampleuser
With the usual configuration, adduser chooses the first available UID in the normal range, creates a same-named primary group, creates the home directory, copies suitable files from /etc/skel, and uses the configured shell. It may also add the account to configured extra groups. Do not assume the numeric ID or supplementary groups: inspect them.
getent passwd exampleuser
id exampleuser
stat -c '%A %U:%G %n' /home/exampleuser
Expected results are one passwd record, a machine-selected UID and GID, and a home directory owned by exampleuser. The exact numbers and group list vary with existing accounts and /etc/adduser.conf.
For a daemon that should not get a usable login shell or an automatically created home, create a system user with an explicitly named group:
sudo adduser --system --group \
--home /var/lib/example-service \
--shell /usr/sbin/nologin \
--comment 'Example service account' \
example-service
Unless you say otherwise, a system user's home is /nonexistent, its shell is /usr/sbin/nologin, no password is set, and /etc/skel is not copied. This example requests a real home path, so adduser creates it; that does not create a daemon, grant filesystem access, or replace the service's own hardening.
getent passwd example-service
getent group example-service
test "$(getent passwd example-service | cut -d: -f7)" = /usr/sbin/nologin && echo 'shell check passed'
stat -c '%A %U:%G %n' /var/lib/example-service
Safety boundary: do not turn a service account into a login account casually. SSH keys, PAM rules and other mechanisms can still permit access even when a password is disabled, so review the complete access path before deployment.
Use addgroup for a normal group. It starts empty and gets a dynamically selected GID:
sudo addgroup projectfiles
getent group projectfiles
To add an existing user to an existing group, pass both names to adduser. This changes supplementary membership and normally takes effect for new login sessions:
sudo adduser exampleuser projectfiles
id exampleuser
Do not confuse --ingroup with that two-argument form. During user creation, --ingroup projectfiles selects the primary group, and the named group must already exist; a primary group is not interchangeable with a supplementary group when a service checks file ownership or group permissions.
For a one-off policy exception, prefer an explicit command-line option. For site-wide policy, edit /etc/adduser.conf through your normal configuration-management process. Its syntax is one option and value per line, with optional quoting; comments must start with # in the first column. For example, ADD_EXTRA_GROUPS controls whether new non-system users join the space-separated groups in EXTRA_GROUPS; it does not retroactively change existing accounts.
You can test a separate configuration file with --conf, but do not treat that as a privilege boundary. The adduser manual warns against granting partial sudo access to adduser with restricted arguments, since the configuration-file option can be used to alter what the command does. Give operators a reviewed wrapper with a narrow policy if delegated account creation is required.
Names are another common trap. The default regular expressions are deliberately conservative: ordinary names begin with a lowercase letter and use lowercase letters, digits, dashes and underscores, and system names may also begin with an underscore. --allow-bad-names weakens that policy, while --allow-all-names follows the underlying useradd checks more closely. Use either only after checking every consumer of the name, since confusing names make auditing and shell work harder.
Account creation is a state change. Before removing anything, confirm the exact account with getent passwd NAME, stop or reconfigure services that use it, check ownership of its files, and follow your site's account-removal procedure. Removing a user can strand files or break a running service; do not use a recursive home-directory removal option unless you have a verified backup and have checked the target path.
If a command reports the object already exists, inspect it rather than forcing a new UID, GID or shell: an explicit --uid or --gid fails when that number is already used. Exit status 0 can mean the requested object was created or that a compatible object already existed, so always verify attributes with getent and id.
getent and id rather than trusting the code alone.getent passwd or getent group.