User Provisioning (shani-user-setup)
shani-user-setup automatically provisions every regular user (UID 1000–59999) with the correct groups, default shell, Flatpak remotes, Nix channels, and rootless container namespaces. It runs as root via shani-user-setup.service, triggered by a systemd path unit (shani-user-setup.path) that watches for any of three conditions: a new/changed /etc/passwd in the /etc overlay's upper layer (i.e. a user was just added), a modified /etc/skel, or the /data/user-setup-needed marker file — the marker is written by shani-deploy after every slot switch and by shani-reset after a factory reset, so newly-applied group assignments and skel changes are picked up on the next boot even without a fresh user account.
What It Does
For each interactive user account on the system, shani-user-setup:
- Groups — adds the user to all groups listed in
/etc/shani-extra-groups, skipping any that don't exist - Default shell — sets the shell to Zsh (falling back to Bash if Zsh isn't installed), only if the current shell differs
- Flatpak remote — adds the
flathubremote for the user if not already present - Nix channel — adds the
nixpkgs-unstablechannel if no channel namednixpkgsexists (never overwrites an existing nixpkgs channel) - subuid/subgid ranges — allocates 65,536 sub-UIDs and sub-GIDs for rootless Podman/LXC/LXD if not already assigned
- Podman storage migration — runs
podman system migrateto upgrade the storage graph if/var/lib/containersis mounted
A stamp file at ~/.cache/shani/user-setup.stamp records the script mtime and extra-groups list. If the stamp matches, the expensive steps are skipped on subsequent runs. Set FORCE_SETUP=1 to bypass.
Extra Groups
The list of groups added to every user is read from a single file:
/etc/shani-extra-groups
Format: one comma-separated line, no spaces.
wheel,video,input,audio,kvm,storage,network,realtime,scanner,lp,cups,libvirt,lxd
Group Reference
| Group | Purpose |
|---|---|
wheel | Sudo privileges for system administration |
input | Direct input device access (keyboards, mice, controllers) |
realtime | Real-time scheduling, HPET/RTC access for audio production and low-latency gaming |
video | GPU and video hardware access |
sys | Hardware monitoring and sensor access |
cups, lp | Printer management and job submission |
scanner | Scanner device access |
nixbld | Nix build users group — required for the Nix package manager daemon |
lxc, lxd | LXC/LXD container management without root |
kvm | Virtual machine management (KVM hardware access) |
libvirt | libvirt VM management via virsh and virt-manager |
This file is the single source of truth shared between shani-user-setup and the adduser/useradd wrappers. Editing it ensures that all future users (and any re-provisioning runs) get the same groups.
If the file is missing or empty, shani-user-setup adds no extra groups at all — the group-membership step silently has nothing to do. It does not fall back to a built-in list. Shell, Flatpak remote, Nix channel, and subuid/subgid provisioning are unaffected and still run normally. In practice the file is written by the base image install, so this only matters if you delete it yourself.
# View current extra groups
cat /etc/shani-extra-groups
# Add a group (via /etc overlay — persists across updates)
sudo nano /etc/shani-extra-groups
# Force re-provisioning all users with the new group list
sudo FORCE_SETUP=1 shani-user-setup
Triggering
shani-user-setup.path watches three things, and running the service is rate-limited to at most 3 triggers per 60 seconds:
/data/overlay/etc/upper/passwdcreated or changed — fires the moment a new user is added (viauseradd/adduser, which copy-up/etc/passwdinto the overlay), without needing any marker file/etc/skelmodified — belt-and-suspenders coverage for a slot switch that changes skel contents/data/user-setup-neededcreated — the explicit marker, written byshani-deployafter every slot switch and byshani-resetafter a factory reset
# systemd path unit watches for these
systemctl status shani-user-setup.path
# Trigger manually (e.g. after adding a new user or editing shani-extra-groups)
sudo touch /data/user-setup-needed
Whichever trigger fires, the service itself re-provisions every interactive user (UID 1000–59999) on the system, not just the one that changed.
Running Manually
# Provision all users (normal run)
sudo shani-user-setup
# Dry run — log what would change without making changes
sudo DRY_RUN=1 shani-user-setup
# Force full re-provisioning (ignore stamp file)
sudo FORCE_SETUP=1 shani-user-setup
Checking Status
# View provisioning logs
journalctl -t shani-user-setup -n 50
# Check path unit status
systemctl status shani-user-setup.path
systemctl status shani-user-setup.service
# Check if a user is in the required groups
id myuser
# Check subuid/subgid assignment
grep myuser /etc/subuid /etc/subgid
After a Factory Reset
After shani-reset, user accounts are gone (the /etc overlay is wiped). On the next boot:
- The first-run wizard creates a new user account
shani-user-setup.pathdetects the new account and triggers provisioning- The new user gets all groups, correct shell, and Flatpak/Nix setup automatically
Files in /home/<username> survive the reset (the @home subvolume is not wiped). Once the account is re-created with the same username, all personal files are immediately accessible.
Rootless Container Support
shani-user-setup allocates subUID/subGID ranges automatically, which are required for rootless Podman, Distrobox, LXC, and LXD.
The allocation algorithm finds the highest existing range end in /etc/subuid and allocates the next 65,536 IDs to avoid collisions. For example, with one existing user allocated at 100000–165535, a second user gets 165536–231071.
# Verify your subuid/subgid assignment
cat /etc/subuid
cat /etc/subgid
# Verify Podman rootless works
podman run --rm alpine echo hello
See Also
- Shell & Environment — Zsh configuration and shell tools
- Factory Reset — re-provisioning after a reset
- System Config — editing
/etc/shani-extra-groupsvia the overlay