> ## Content Index
> Fetch the complete content index at: https://blog.sudowl.com/llms.txt
> Use this file to discover other available public pages before exploring further.

# Linux User Management — adduser, Groups, sudo and SSH Keys
- URL: https://blog.sudowl.com/linux-user-management-adduser-sudo-ssh-keys/
- Published: 2026-03-31T10:00:00.000Z
- Updated: 2026-10-11T08:47:37.000Z
- Description: Admin and service users on Ubuntu/Debian: adduser vs useradd, groups, sudoers drop-ins, SSH keys with correct permissions, and locking users properly.
- Author: Lukas
- Tags: Linux, Debian, Ubuntu

Every fresh server needs the same few things on the user side: one admin account that isn't root, SSH keys instead of passwords, maybe a deploy user with limited sudo, and system users for the services. It's simple, but there are a handful of details that either lock you out or leave a door open — usually permissions on `.ssh` and the `-a` in `usermod`.

**TL;DR:** `adduser` (not `useradd`) for humans, `usermod -aG sudo` for admin rights, `ssh-copy-id` for keys, sudoers changes only via `visudo -f /etc/sudoers.d/<name>`, and to lock someone out use `usermod -L -e 1`, because a locked password alone doesn't stop SSH keys.

```bash
# admin user
sudo adduser alice
sudo usermod -aG sudo alice

# from your workstation
ssh-keygen -t ed25519 -C "alice@laptop"
ssh-copy-id alice@server

# test in a NEW terminal before touching anything else
ssh alice@server 'sudo -v && echo ok'

```

## adduser vs useradd

On Debian and Ubuntu there are two tools, and they do different things:

- **`adduser`** — the friendly Debian wrapper. Creates the user, a matching group, the home directory (copied from `/etc/skel`), sets the shell to bash, asks for a password. This is what you want for humans.
- **`useradd`** — the low-level tool. Without flags it may create a user with no home directory and `/bin/sh` as shell, depending on `/etc/default/useradd`. Fine in scripts where you pass everything explicitly, annoying interactively.

```bash
# the useradd equivalent of a sane adduser
sudo useradd -m -s /bin/bash -G sudo alice
sudo passwd alice

```

For a user who only ever logs in with a key, skip the password:

```bash
sudo adduser --disabled-password alice

```

They can't `sudo` with a password then, of course — either set one later or give them specific `NOPASSWD` rules (below).

## Groups

```bash
id alice                    # uid, gid, all groups
groups alice
sudo usermod -aG docker alice
sudo gpasswd -d alice docker   # remove from a group

```

Two things to know:

- Group changes apply at the **next login**. An existing SSH session keeps the old group list. Log out and back in (or `newgrp docker` for one shell).
- Some groups are root in disguise. `sudo` obviously, but also `docker` (can mount `/` into a container), `lxd`, and `disk` (raw block device access). Treat membership in those like handing out the root password. More on the Docker side in [installing Docker](/docker-install-ubuntu/).

## Gotcha: `usermod -G` without `-a`

```bash
sudo usermod -G docker alice      # WRONG

```

Without `-a` (append), `-G` *replaces* all supplementary groups with the list you give. alice is now in `docker` and nothing else — no more `sudo`. If alice is your only admin and root login is disabled, you just locked yourself out of root on that box. Always `-aG`. If it already happened: rescue console, or any other sudo-capable account, and add `sudo` back.

## sudo

On Ubuntu and Debian, members of the `sudo` group get full sudo via this line in `/etc/sudoers`:

```
%sudo   ALL=(ALL:ALL) ALL

```

Debian detail: if you set a root password during installation, the installer doesn't install `sudo` at all and doesn't create an admin user in the `sudo` group. As root:

```bash
apt install sudo
usermod -aG sudo alice

```

### Restricted sudo with drop-ins

Never edit `/etc/sudoers` directly with a normal editor. A syntax error there means sudo refuses to run at all — for everyone. Use `visudo`, which checks syntax before saving, and put your rules in a drop-in:

```bash
sudo visudo -f /etc/sudoers.d/deploy

```

```
# deploy can restart the app, nothing else, no password
deploy ALL=(root) NOPASSWD: /usr/bin/systemctl restart myapp.service, /usr/bin/systemctl status myapp.service

```

Rules that list arguments match those arguments exactly, so `deploy` can run precisely these two commands. Use full paths. Avoid giving out commands that can spawn a shell or edit files (`vim`, `less`, `find`, `tar`, `systemctl edit`) — any of those is effectively full root.

Check what a user may do:

```bash
sudo -l -U deploy
sudo visudo -c        # syntax-check all sudoers files

```

## Gotcha: sudoers.d file names

Files in `/etc/sudoers.d/` whose name contains a dot or ends with `~` are **silently ignored**. `deploy.conf` does nothing. `deploy` works. This exists so editor backups and package leftovers don't get loaded — but it means your carefully written rule just doesn't apply, and nothing tells you. `visudo -f` doesn't warn about it either.

## SSH keys

On your machine:

```bash
ssh-keygen -t ed25519 -C "alice@laptop"
ssh-copy-id -i ~/.ssh/id_ed25519.pub alice@server

```

Ed25519 is short, fast and supported everywhere that matters. Use a passphrase on the key, and an agent so you don't type it every time.

If `ssh-copy-id` isn't an option (password auth already off, or you're setting up a user from root), do it by hand on the server:

```bash
sudo install -d -m 700 -o alice -g alice /home/alice/.ssh
echo 'ssh-ed25519 AAAA... alice@laptop' | sudo tee -a /home/alice/.ssh/authorized_keys
sudo chown alice:alice /home/alice/.ssh/authorized_keys
sudo chmod 600 /home/alice/.ssh/authorized_keys

```

One key per line in `authorized_keys`. Comment at the end of each line so you know later whose key it is — removing access for a person is then one `grep`.

## Gotcha: wrong permissions = key silently ignored

sshd's `StrictModes` (on by default) refuses keys if the home directory, `.ssh` or `authorized_keys` is writable by anyone except the owner, or owned by the wrong user. The client just falls back to password and you wonder why your key "doesn't work". Typical causes: `authorized_keys` created as root and never chowned, or a home directory someone made group-writable.

The server log tells you exactly:

```bash
sudo journalctl -u ssh -n 50 | grep -i -E 'authentication refused|bad ownership'

```

And from the client side:

```bash
ssh -v alice@server

```

Look for which keys are offered and whether the server accepted one. Correct state:

```bash
ls -ld /home/alice /home/alice/.ssh /home/alice/.ssh/authorized_keys
# drwxr-x---  alice alice  /home/alice           (755 or 750 is fine, not group/world-writable)
# drwx------  alice alice  /home/alice/.ssh
# -rw-------  alice alice  /home/alice/.ssh/authorized_keys

```

Once keys work for every admin, turn off password authentication and root login — that's in the [SSH hardening checklist](/ssh-hardening-ubuntu-checklist/).

## Service users

Services should run as their own user without a login shell or password:

```bash
sudo adduser --system --group --home /opt/myapp myapp
# useradd equivalent:
sudo useradd --system --user-group --home-dir /opt/myapp --shell /usr/sbin/nologin myapp

```

`--system` gives a UID below 1000 and, with `adduser`, `nologin` as shell. The app's files belong to `myapp`, the systemd unit has `User=myapp`, and if the app is compromised, the attacker is `myapp`, not root. How the unit side looks is in [systemd service unit files](/systemd-service-unit-file/).

## Locking and removing users

Someone leaves, a key leaks, a contractor's job is done.

```bash
# lock: password locked AND account expired
sudo usermod -L -e 1 alice

# kick existing sessions
sudo loginctl terminate-user alice

# unlock later
sudo usermod -U -e '' alice

```

## Gotcha: `usermod -L` doesn't stop SSH keys

`usermod -L` (or `passwd -l`) only puts a `!` in front of the password hash. Password login is dead, but on Ubuntu/Debian with `UsePAM yes`, **SSH key login still works**. The usermod man page even says it: to lock the account and not just the password, also set the expiry date to 1\. An expired account fails PAM's account check, which applies to key logins too. Also remove their keys from `authorized_keys` — on every account they had keys on, including shared ones like `deploy`.

To delete for good:

```bash
sudo deluser --remove-home alice
# check for leftovers they owned elsewhere
sudo find / -xdev -user alice 2>/dev/null

```

Also grep `/etc/sudoers.d/` and crontabs for the name. If you build servers from templates, bake the admin user and keys into cloud-init instead of repeating this per VM — see [Proxmox cloud-init templates](/proxmox-cloud-init-vm-template-ubuntu/) and the [initial setup checklist](/ubuntu-server-initial-setup-checklist/).

---

### Related posts

- [Set Up Automatic Security Updates on Ubuntu and Debian](https://blog.diengdoh.com/ubuntu-automatic-updates-unattended-upgrades/?ref=blog.sudowl.com)
- [Proxmox + Cloud-Init — Spinning Up an Ubuntu VM in 30 Seconds](https://blog.diengdoh.com/proxmox-cloud-init-vm-template-ubuntu/?ref=blog.sudowl.com)
- [rsync Backups That Actually Work — No Excuses](https://blog.diengdoh.com/rsync-backup-linux/?ref=blog.sudowl.com)