Omarchy User Provisioning Workflow: Creating Users and Finalizing Setup

The Omarchy user provisioning workflow is a three-phase process—deferred first-boot creation, runtime per-user finalization, and first-run graphical setup—that automates account creation and system personalization while maintaining idempotency through state markers.

Omarchy, the opinionated workstation setup from 37signals (Basecamp), handles user creation differently than traditional Linux distributions. Instead of creating users during the ISO installation, Omarchy uses a deferred provisioning model that separates system installation from account setup, allowing for unattended installs and consistent runtime finalization. This workflow ensures that every user receives the same development environment configuration regardless of when their account was created.

The Three-Phase Provisioning Architecture

The provisioning system is split between root-side (first-boot) and runtime (per-user) operations, ensuring that system-level configuration happens before user-level customization.

Phase 1: Deferred First-Boot Provisioning

When installing with the --defer-provisioning flag, Omarchy leaves a marker file at /var/lib/omarchy/provisioning/pending rather than creating a user during the ISO installation. On the next boot, the systemd unit omarchy-provision-owner.service (running on tty1) executes bin/omarchy-provision-owner to complete setup.

This script presents an installer-style console form that collects user details and performs system-level actions:

  • Creates the system account via useradd or modifies existing users with usermod
  • Records chosen groups and configures sudo access
  • Writes the password using chpasswd
  • Installs authorized SSH keys
  • Configures hostname and timezone
  • Invokes omarchy-provision-user with the --first-install context

The implementation in bin/omarchy-provision-owner uses functions defined in install/provisioning/setup-form.sh to handle the interactive prompts and validation.

Phase 2: Runtime Per-User Finalization

After the user exists, bin/omarchy-provision-user runs as the target user (it aborts if invoked as root). This script creates an idempotency marker at ~/.local/state/omarchy/done/finalize-user to prevent duplicate runs.

Key finalization tasks include:

  • Skill symlinking: Creates symlinks from ~/.agents/skills/* to $OMARCHY_PATH/default/agents/skills/* for development tool integration
  • XDG configuration: Updates user directories and GTK bookmarks
  • Default applications: Sets the default web browser and mailto handlers
  • Per-user hooks: Sources install/user/all.sh for additional customization that requires $HOME context

When invoked with --first-install, the script marks all shipped migrations as already applied, ensuring that chroot installations don't trigger unnecessary updates on first boot.

Phase 3: First-Run Graphical Setup

When the user logs into the graphical session for the first time, bin/omarchy-provision-first-run executes automatically via default/hypr/autostart.lua. This wrapper ensures the user finalization marker exists (re-running omarchy-provision-user if necessary) before executing desktop-specific tasks:

  • Installs post-update hooks (install-voxtype.hook, setup-fingerprint.hook, setup-agent.hook)
  • Enables per-user systemd units (omarchy-migrate-notify.service, omarchy-sleep-lock.service)
  • Applies GNOME/GTK theme settings and speaker tuning
  • Displays the welcome notification

Completion is recorded in ~/.local/state/omarchy/done/first-run-user, making this phase fully idempotent.

Key Provisioning Scripts and Their Roles

Understanding the specific entry points helps when debugging or customizing the workflow:

  • bin/omarchy-provision-owner: Root-only script for initial account creation. Handles the transition from "no users" to "owner account exists" and is responsible for the create_user function that writes to /var/lib/omarchy/provisioning/setup-user.

  • bin/omarchy-provision-user: User-context finalizer that handles skill symlink creation and sources install/user/all.sh. This script contains the finalize_user function and respects the --force flag to bypass the omarchy-done checks.

  • bin/omarchy-provision-first-run: Graphical session initializer that bridges the gap between console provisioning and desktop readiness. It executes scripts in install/user/first-run/ including enable-user-units.sh.

How the Components Fit Together

The complete workflow executes in this order:

  1. ISO Installation: When omarchy-apply-system --defer-provisioning runs, it skips user creation and writes the pending marker to /var/lib/omarchy/provisioning/pending.

  2. First Boot: The omarchy-provision-owner.service detects the pending file and launches the owner provisioning script on tty1, presenting the user_form and executing create_user with the development-aware group configuration.

  3. User Finalization: Immediately after creation, the owner script calls omarchy-provision-user --force --first-install, which creates skill symlinks and runs install/user/all.sh.

  4. Desktop Initialization: Upon first graphical login, omarchy-provision-first-run executes, enabling user systemd units and applying desktop-specific settings via run_first_run_step.

Working with the Provisioning Commands

These commands are useful for manual intervention or debugging the Omarchy user provisioning workflow:


# Trigger first-boot provisioning manually (normally runs automatically on tty1)

sudo systemctl start omarchy-provision-owner.service

# Finalize an existing user (run as the target user, not root)

omarchy provision user

# Or manually with flags:

omarchy-provision-user --force --first-install

# Re-run first-run setup after fixing a broken initial session

omarchy-provision-first-run --force

The create_user function in bin/omarchy-provision-owner handles both new and existing accounts:

create_user() {
  echo "$username" >"$PROVISIONING_DIR/setup-user"
  if getent passwd "$username" >/dev/null; then
    usermod -aG "$(user_groups)" ${full_name:+-c "$full_name"} "$username"
  else
    useradd -m -G "$(user_groups)" -s /bin/bash ${full_name:+-c "$full_name"} "$username"
  fi
  printf '%s:%s\n' "$username" "$password" | chpasswd
  printf '%s:%s\n' root "$password" | chpasswd
}

Skill symlinking in bin/omarchy-provision-user supports multiple AI agent namespaces:

mkdir -p ~/.agents/skills ~/.claude/skills ~/.codex/skills ~/.pi/agent/skills
for skill in "$OMARCHY_PATH"/default/agents/skills/*/; do
  skill=${skill%/}
  name=${skill##*/}
  ln -sfn "$skill" ~/.agents/skills/"$name"
  # ... symlinks for other namespaces

done

Idempotency and State Markers

Every phase of the Omarchy user provisioning workflow uses state markers to ensure safety and repeatability:

  • Owner provisioning: Records the username in /var/lib/omarchy/provisioning/setup-user
  • User finalization: Creates ~/.local/state/omarchy/done/finalize-user
  • First-run completion: Creates ~/.local/state/omarchy/done/first-run-user

The omarchy-done utility manages these markers, checking for existence before executing phase-specific logic. Each script supports --force to bypass these checks when re-provisioning is necessary.

Summary

  • Omarchy uses deferred provisioning to separate system installation from user creation, writing a pending marker rather than creating accounts during the ISO phase.
  • bin/omarchy-provision-owner handles root-side account creation on first boot, including group assignment, password setting, and SSH key installation.
  • bin/omarchy-provision-user performs runtime finalization as the target user, creating skill symlinks and sourcing install/user/all.sh.
  • bin/omarchy-provision-first-run completes the workflow during the first graphical login, enabling user systemd units and applying desktop settings.
  • All phases are idempotent via state markers in ~/.local/state/omarchy/done/ and support --force for manual re-runs.

Frequently Asked Questions

How do I re-run the user provisioning if it fails?

Use the --force flag with the specific script that failed. For the finalization phase, run omarchy-provision-user --force as the target user (not root). For the first-run phase, use omarchy-provision-first-run --force. Avoid forcing the owner provisioning unless you intend to create a new primary account, as it will overwrite the existing setup-user marker.

Can I use Omarchy with pre-existing user accounts?

Yes. The create_user function in bin/omarchy-provision-owner checks for existing accounts with getent passwd and uses usermod instead of useradd if the user already exists. However, you must still run omarchy-provision-user to complete the runtime finalization phase and create the necessary skill symlinks and XDG configuration.

Where does Omarchy store the provisioning state?

State markers are stored in three locations: /var/lib/omarchy/provisioning/ for system-wide owner provisioning data, ~/.local/state/omarchy/done/ for per-user completion markers, and /etc/omarchy/ for configuration persistence. The finalize-user and first-run-user markers specifically prevent duplicate execution of the runtime and graphical phases.

What happens during deferred provisioning installs?

When the installer runs with --defer-provisioning, it skips all user creation logic and writes /var/lib/omarchy/provisioning/pending. The system then boots to a minimal state without a regular user account. On first boot, omarchy-provision-owner.service detects the pending file and launches the interactive setup form on tty1, gathering username, password, groups, and SSH keys before creating the account and finalizing the system.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →