॥ श्री ॥

User Provisioning (shani-user-setup)

Updates & Config 2026-08-28

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:

  1. Groups — adds the user to all groups listed in /etc/shani-extra-groups, skipping any that don't exist
  2. Default shell — sets the shell to Zsh (falling back to Bash if Zsh isn't installed), only if the current shell differs
  3. Flatpak remote — adds the flathub remote for the user if not already present
  4. Nix channel — adds the nixpkgs-unstable channel if no channel named nixpkgs exists (never overwrites an existing nixpkgs channel)
  5. subuid/subgid ranges — allocates 65,536 sub-UIDs and sub-GIDs for rootless Podman/LXC/LXD if not already assigned
  6. Podman storage migration — runs podman system migrate to upgrade the storage graph if /var/lib/containers is 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

GroupPurpose
wheelSudo privileges for system administration
inputDirect input device access (keyboards, mice, controllers)
realtimeReal-time scheduling, HPET/RTC access for audio production and low-latency gaming
videoGPU and video hardware access
sysHardware monitoring and sensor access
cups, lpPrinter management and job submission
scannerScanner device access
nixbldNix build users group — required for the Nix package manager daemon
lxc, lxdLXC/LXD container management without root
kvmVirtual machine management (KVM hardware access)
libvirtlibvirt 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:

  1. /data/overlay/etc/upper/passwd created or changed — fires the moment a new user is added (via useradd/adduser, which copy-up /etc/passwd into the overlay), without needing any marker file
  2. /etc/skel modified — belt-and-suspenders coverage for a slot switch that changes skel contents
  3. /data/user-setup-needed created — the explicit marker, written by shani-deploy after every slot switch and by shani-reset after 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:

  1. The first-run wizard creates a new user account
  2. shani-user-setup.path detects the new account and triggers provisioning
  3. 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