# How Omarchy Manages User Services like bt-agent and omarchy-sleep-lock

> Learn how Omarchy manages user services like bt-agent and omarchy-sleep-lock using systemd user units and migration scripts for seamless operation and updates.

- Repository: [37signals/omarchy](https://github.com/basecamp/omarchy)
- Tags: how-to-guide
- Published: 2026-08-19

---

**Omarchy manages user services such as `bt-agent` and `omarchy-sleep-lock` through systemd user units that are automatically enabled during first-run setup and maintained via version-specific migration scripts.**

Omarchy, Basecamp's opinionated Linux desktop environment, delegates background helper management to **systemd user-level units** rather than system-wide services. This architecture ensures that utilities like the Bluetooth pairing agent and session lock handler start only when a graphical session is active and run with user privileges instead of root.

## User-Level Systemd Unit Definitions

Both services are defined as user-level systemd units located in `default/systemd/user/`, allowing them to integrate with the graphical session lifecycle.

### Bluetooth Pairing Agent Service

The **`bt-agent.service`** provides automatic Bluetooth pairing acceptance without requiring manual PIN entry. Defined in `default/systemd/user/bt-agent.service`, the unit includes hardware and dependency guards:

```ini
[Unit]
Description=Bluetooth pairing agent (auto-accept)
ConditionPathIsDirectory=/sys/class/bluetooth
ExecCondition=/usr/bin/systemctl is-active --quiet bluetooth.service
After=dbus.socket
Requires=dbus.socket

[Service]
Type=simple
ExecStart=/usr/bin/bt-agent -c NoInputNoOutput
Restart=on-failure
RestartSec=2

[Install]
WantedBy=graphical-session.target

```

The **ConditionPathIsDirectory** directive ensures the service skips activation on machines lacking Bluetooth hardware, while **ExecCondition** verifies the system Bluetooth daemon is active before launching.

### Session Sleep Lock Service

The **`omarchy-sleep-lock.service`** secures the session before system suspension. Located at `default/systemd/user/omarchy-sleep-lock.service`, it invokes the `omarchy-system-sleep-monitor` helper:

```ini
[Unit]
Description=Lock Omarchy before suspend
After=dbus.socket wayland-session-waitenv.service
Requires=dbus.socket
PartOf=graphical-session.target
ConditionEnvironment=OMARCHY_PATH
ConditionEnvironment=WAYLAND_DISPLAY

[Service]
Type=simple
ExecStart=/usr/bin/omarchy-system-sleep-monitor
Restart=always
RestartSec=2

[Install]
WantedBy=graphical-session.target

```

The **ConditionEnvironment** checks ensure the service only runs within a proper Omarchy Wayland session, preventing activation in incompatible environments.

## First-Run Installation and Enablement

During initial setup, the script [`install/user/first-run/enable-user-units.sh`](https://github.com/basecamp/omarchy/blob/main/install/user/first-run/enable-user-units.sh) iterates over a hard-coded list of services including `bt-agent.service` and `omarchy-sleep-lock.service`. It calls the helper function `enable_user_unit` defined in [`install/lib.sh`](https://github.com/basecamp/omarchy/blob/main/install/lib.sh), which executes:

```bash
systemctl --user enable --now "$1"

```

This command installs the units into `$HOME/.config/systemd/user/` and immediately starts them. Because both units specify `WantedBy=graphical-session.target`, they automatically activate when the user logs into the graphical session and terminate upon logout.

## Upgrade Handling and Migration Scripts

Omarchy preserves service continuity across updates through targeted migration scripts. When the repository layout changes, the upgrade script `bin/omarchy-upgrade-to-quattro` retires obsolete unit copies and re-invokes `enable_user_unit` to install current versions.

For the sleep-lock service specifically, [`migrations/1785608166.sh`](https://github.com/basecamp/omarchy/blob/main/migrations/1785608166.sh) performs health checks on existing user configurations. It attempts to reset failed states, restart the service, and verify it reaches active status. If the unit remains unhealthy, the script logs actionable errors and attempts clean termination, ensuring the user environment stays synchronized with the repository.

## Runtime Conditions and Dependencies

The Bluetooth agent employs **conditional activation** to avoid unnecessary resource consumption. The `ConditionPathIsDirectory=/sys/class/bluetooth` check gates startup on hardware presence, while `ExecCondition` validates the system `bluetooth.service` is running. These conditions prevent restart loops on Bluetooth-less systems, as validated by [`test/shell.d/bluetooth-test.sh`](https://github.com/basecamp/omarchy/blob/main/test/shell.d/bluetooth-test.sh).

The sleep-lock service exposes its state through standard systemd interfaces. Omarchy shell scripts query this status programmatically; for example, `omarchy-debug-idle` uses `systemctl --user status omarchy-sleep-lock.service` to verify lock readiness before suspending.

## Testing and Validation

Omarchy ships a comprehensive test suite in `test/shell.d/` that guards against regressions:

- **[`systemd-test.sh`](https://github.com/basecamp/omarchy/blob/main/systemd-test.sh)** validates that both units appear in the enabled user units list and that stale copies are properly reset during upgrades.
- **[`bluetooth-test.sh`](https://github.com/basecamp/omarchy/blob/main/bluetooth-test.sh)** confirms `bt-agent.service` correctly skips activation when Bluetooth hardware is absent.
- **[`sleep-lock-test.sh`](https://github.com/basecamp/omarchy/blob/main/sleep-lock-test.sh)** and **[`sleep-lock-environment-migration-test.sh`](https://github.com/basecamp/omarchy/blob/main/sleep-lock-environment-migration-test.sh)** verify the service respects environment variables (`OMARCHY_PATH`, `WAYLAND_DISPLAY`) and handles start/stop transitions cleanly.

These tests run under `./test/shell` and assert the integrity of the user service management pipeline.

## Manual Service Management

While Omarchy automates enablement, you can interact with these services directly using `systemctl --user`:

Enable and start the Bluetooth agent manually:

```bash
systemctl --user enable --now bt-agent.service
systemctl --user status bt-agent.service

```

Verify the sleep-lock service state:

```bash
systemctl --user show --property=ActiveState --value omarchy-sleep-lock.service

```

If the service is inactive, start it with:

```bash
systemctl --user enable --now omarchy-sleep-lock.service

```

## Summary

- Omarchy defines user services in `default/systemd/user/` as systemd user units tied to `graphical-session.target`.
- **`bt-agent.service`** auto-accepts Bluetooth pairings only when hardware is present and the system Bluetooth service is active.
- **`omarchy-sleep-lock.service`** locks the session before suspend, requiring `OMARCHY_PATH` and `WAYLAND_DISPLAY` environment variables.
- **First-run installation** occurs via [`install/user/first-run/enable-user-units.sh`](https://github.com/basecamp/omarchy/blob/main/install/user/first-run/enable-user-units.sh), which enables and starts units immediately.
- **Migration scripts** like [`migrations/1785608166.sh`](https://github.com/basecamp/omarchy/blob/main/migrations/1785608166.sh) and `bin/omarchy-upgrade-to-quattro` handle upgrades by retiring old units and re-enabling current versions.
- **Comprehensive tests** in `test/shell.d/` validate enablement, conditional logic, and migration behavior.

## Frequently Asked Questions

### What triggers bt-agent to start?

The `bt-agent.service` unit starts automatically when you log into a graphical session, provided the machine has Bluetooth hardware (`/sys/class/bluetooth` exists) and the system `bluetooth.service` is active. These checks prevent the service from attempting to start on incompatible systems or when Bluetooth is disabled.

### How does Omarchy ensure services survive upgrades?

When upgrading to a new version, the script `bin/omarchy-upgrade-to-quattro` removes obsolete unit files and re-runs `enable_user_unit` for each service. Additionally, migration scripts like [`migrations/1785608166.sh`](https://github.com/basecamp/omarchy/blob/main/migrations/1785608166.sh) verify service health, reset failed states, and ensure the units are active after the upgrade completes.

### Can I disable omarchy-sleep-lock if I don't want automatic locking?

Yes. You can disable the service for your user without affecting system functionality by running `systemctl --user disable omarchy-sleep-lock.service`. However, this will prevent Omarchy from automatically locking your session before suspend, which may have security implications.

### Where are the user service files actually stored?

Omarchy keeps source unit files in `default/systemd/user/` within the repository. When enabled, systemd creates symlinks in your home directory under `$HOME/.config/systemd/user/`, which is the standard location for user-level systemd configurations.