user
Manages local user accounts -- create, modify, and delete users with configurable shell, home directory, groups, and SSH authorized keys.
Organizational guidance: user accounts are vigo's most complex resource type and benefit from a specific stacks layout (usercrate files under
stacks/usercrates/, directory-level compliance claims, lookup-table per-host variation, two-phase retirement). See the User Management concept page for why and how, and the Glossary for what "usercrate" means.
Parameters
| Parameter | Required | Default | Description |
|---|---|---|---|
username |
Yes | -- | Username. |
state |
Yes | -- | Desired state: present or absent. |
uid |
No | -- | Desired UID. |
shell |
No | -- | Login shell (e.g., /bin/bash). |
home |
No | -- | Home directory path. |
comment |
No | -- | User comment / GECOS field. |
group |
No | -- | Primary group. |
gid |
No | -- | Numeric primary group ID. Use to pin the GID instead of (or alongside) group; passed to useradd -g. |
groups |
No | -- | Supplementary groups. Accepts either a comma-separated string ("plex,docker") or a YAML list ([plex, docker]). List form is preferred because it composes with lookup tables for per-envoy variation. |
password |
No | -- | Plaintext password. The agent computes $6$<salt>$<hash> SHA-512-crypt at apply time and feeds it to usermod -p / useradd -p — /etc/shadow still holds a hash, just one derived on the envoy instead of upstream. Idempotency works by re-running the crypt with the existing salt and comparing. Typically sourced from the vault via secret:vigo/os-users/<name> (which a vigocli secrets set password call populates). |
authorized_keys |
No | -- | SSH authorized keys content (written to ~/.ssh/authorized_keys). |
system |
No | false |
If true, create a system account (useradd --system). |
manage_home |
No | true |
If false, skip -m flag on useradd (no home dir creation). If true, userdel -r removes home on delete. |
password_max_age |
No | -- | Maximum password age in days. Runs chage -M <days> after create/update. |
expiredate |
No | -- | Account expiration date (YYYY-MM-DD). Passed to useradd --expiredate / usermod --expiredate. |
purge_groups |
No | false |
When true with groups, removes user from supplementary groups not in the list (usermod -G instead of -aG). |
sudo_passwd |
No | false |
If true, grant sudo access with password required. Writes /etc/sudoers.d/<username>. Mutually exclusive with sudo_nopasswd. |
sudo_nopasswd |
No | false |
If true, grant passwordless sudo access. Writes /etc/sudoers.d/<username> with NOPASSWD. Mutually exclusive with sudo_passwd. |
puddle |
No | false |
If true, authorize this user to use puddle and lockbox on this envoy (0.41.0+). The user executor writes /var/lib/vigo/usercrate-policy/<username>.json; the per-user CLI verbs (vigo swarm puddle *) read it as a precondition. Removing this flag triggers a destructive scrub on the next reconcile pass: queue a lockbox LEAVE sentinel for fan-out, then wipe ~/.vigo-puddle/, ~/.lockbox/, ~/lockbox/, and ~/.vigo-gitback/. |
gitback |
No | false |
If true, authorize this user to create/join gitback projects on this envoy (0.41.0+). Same cache mechanism as puddle. Removing this flag scrubs ~/.vigo-gitback/ for that user on the next reconcile pass. The root-owned bare repos under /var/lib/vigo/swarm/gitback/ survive — admin cleanup is a separate path. |
curator |
No | false |
If true, authorize this user to publish to the curator P2P artifact registry on this envoy (0.51.0+). A heavier grant than gitback: — "may publish to the fleet's registry, fleet-readable by default" vs "may found personal git repos." Same cache mechanism as puddle / gitback. Reading curator artifacts (vigo swarm curator pull / inspect / etc.) is ungated; this flag only controls publishing. Removing this flag scrubs ~/.vigo-curator/ for that user on the next reconcile pass via ScrubScope::Curator. Already-published artifacts persist in the fleet catalog — you can't recall them by losing a flag. Publishing also requires both swarm.curator.enabled to match the envoy hostname and an unlocked puddle (vigo swarm puddle unlock). |
poolq |
No | false |
If true, authorize this user to post to poolq topics on this envoy (ADR-029). Same cache mechanism and unlocked-puddle precondition as curator. Removing this flag scrubs ~/.vigo-poolq/ for that user on the next reconcile pass via ScrubScope::Poolq (provided puddle stays enabled). |
States
present-- Ensure the user exists with the specified attributes. Creates withuseraddif missing, modifies withusermodif attributes differ.absent-- Delete the user and optionally their home directory (userdel --remove).
Idempotency
Reads /etc/passwd to check if the user exists and compares each attribute (UID, shell, home, groups, etc.). Only runs useradd or usermod when attributes need changing.
Examples
Basic
resources:
- name: deploy user
type: user
username: deploy
shell: /bin/bash
groups: [sudo, docker]
Both groups: [sudo, docker] (YAML list) and groups: "sudo,docker" (comma string) are accepted. List form is preferred because it composes with lookup tables:
groups:
tag:workstation: [docker, virtualbox, kvm]
tag:mediaserver: [docker, plex]
default: [docker]
System account
resources:
- name: myapp service account
type: user
username: myapp
system: "true"
shell: /usr/sbin/nologin
home: /var/lib/myapp
With authorized keys
resources:
- name: deploy user
type: user
username: deploy
shell: /bin/bash
authorized_keys: |
ssh-ed25519 AAAA... admin@example.com
ssh-ed25519 AAAA... ci@example.com
Expiring contractor account
resources:
- name: contractor
type: user
username: contractor
shell: /bin/bash
expiredate: "2026-06-30"
password_max_age: "90"
groups: "developers"
purge_groups: "true"
Without home directory
resources:
- name: service-runner
type: user
username: svcrun
manage_home: "false"
shell: /usr/sbin/nologin
Sudo with password
resources:
- name: deploy user
type: user
username: deploy
shell: /bin/bash
sudo_passwd: "true"
Passwordless sudo
resources:
- name: admin user
type: user
username: admin
shell: /bin/bash
sudo_nopasswd: "true"
RDP / VNC desktop session (~/.xsession)
The user executor manages account state only — it does not write
~/.xsession. To launch a specific desktop environment under xrdp or
x11vnc, declare the ~/.xsession file as a separate file resource:
resources:
- name: dan
type: user
username: dan
shell: /bin/bash
authorized_keys: |
ssh-ed25519 AAAA... dan@workstation
- name: dan-xsession
type: file
target_path: /home/dan/.xsession
owner: dan
group: dan
mode: "0755"
when: has_command('cinnamon-session')
content: |
#!/bin/sh
# Locale and D-Bus already exported by /etc/xrdp/startwm.sh.
exec cinnamon-session
depends_on: [dan]
The when: gate is the right place to make the xsession per-host:
hosts without cinnamon-session skip the file entirely. Substitute
gnome-session, startxfce4, mate-session, startplasma-x11, etc.
for other desktops. Splitting account state from session state keeps
each resource doing one thing and avoids the failure modes of two
user resources fighting over the same target user.
Remove a user
resources:
- name: old-user
type: user
username: old-user
state: absent
Platform
Linux only. On Windows, the same type: user maps to the user_windows executor.
Notes
- Uses
useradd,usermod, anduserdelcommands. - The
authorized_keyscontent is written to<home>/.ssh/authorized_keyswith mode 0600, and the.sshdirectory is created with mode 0700 if needed. - When
state: absent, the user is deleted withuserdel --remove(which also removes the home directory) and any/etc/sudoers.d/<username>file is removed. - Sudoers files are written to
/etc/sudoers.d/<username>with mode0440. Setting bothsudo_passwdandsudo_nopasswdtotrueis an error. Removing both params from a user that previously had sudo will delete the sudoers file.