Fix NVIDIA GPU detection when supergfxd blacklists modules#5422
Fix NVIDIA GPU detection when supergfxd blacklists modules#5422kuro-toji wants to merge 9 commits intobasecamp:devfrom
Conversation
The /boot mount point and random-seed file were world accessible, which is a security issue per bootctl warnings. This fix: - Sets /boot directory permissions to 700 - Sets random-seed file permissions to 600 - Runs bootctl random-seed to regenerate with correct permissions Fixes: basecamp#5377
On chroot installations, the snapper /home config wasn't being created, leading to silent failures and disk space issues as snapshot subvolumes kept growing without cleanup policies. This fix ensures: - /home snapper config is created when /home is on btrfs - Root snapper config is verified to exist - Config is copied from defaults with appropriate modifications Fixes: basecamp#5344
Users reported that omarchy-snapshot restore was also restoring /home, causing data loss of user files. This fix: - Updates omarchy-snapshot to show clear warnings about /home exclusion - Documents that /home is NOT restored during snapshot operations - Creates a safe wrapper script for snapshot restore The root cause may be in limine-snapper-restore, but until that's fixed, this provides user awareness and prevents accidental data loss. Fixes: basecamp#5361
System hard-freezes on suspend with hyprlock active on NVIDIA GPUs. No resume possible, requires hard reboot. Root cause: hyprlock holds DRM/GBM resources that prevent NVIDIA driver from properly entering suspend state. Fix: Create a systemd service that stops hyprlock before suspend and resumes it after wake. Fixes: basecamp#5277
There was a problem hiding this comment.
Pull request overview
This PR aims to restore NVIDIA GPU detection on hybrid laptops by removing supergfxd-created module blacklists and disabling supergfxd when it interferes with NVIDIA driver binding. In addition, it introduces several other migrations and installer config scripts for boot permissions, snapper config, snapshot restore messaging, and an NVIDIA suspend workaround.
Tip
If you aren't ready for review, convert to a draft PR.
Click "Convert to draft" or run gh pr ready --undo.
Click "Ready for review" or run gh pr ready to reengage.
Changes:
- Add a migration and an installer script to detect/remove
supergfxdNVIDIA blacklist and disable the service. - Add new migrations addressing
/bootpermissions, snapper/homeconfig, snapshot restore messaging, and an NVIDIA + hyprlock suspend fix. - Update
omarchy-snapshot restoreto print a warning about what gets restored.
Reviewed changes
Copilot reviewed 9 out of 10 changed files in this pull request and generated 15 comments.
Show a summary per file
| File | Description |
|---|---|
| migrations/1777007504.sh | Migration to disable supergfxd and remove /etc/modprobe.d/supergfxd.conf when blacklisting NVIDIA. |
| install/config/supergfxd-nvidia-fix.sh | Installer-side remediation for supergfxd NVIDIA blacklisting. |
| migrations/1777007503.sh | Migration adding a systemd unit to pause hyprlock across suspend/hibernate for NVIDIA. |
| install/config/nvidia-suspend-fix.sh | Installer script adding the hyprlock-suspend systemd unit. |
| migrations/1777007502.sh | Migration attempting to add a “safe” snapshot restore wrapper and user messaging. |
| bin/omarchy-snapshot | Adds warning output before running snapshot restore. |
| migrations/1777007501.sh | Migration to create/repair snapper configs for /home (chroot installs). |
| install/config/snapper-home-config.sh | Installer counterpart to ensure snapper configs exist. |
| migrations/1777007500.sh | Migration adjusting /boot and random-seed permissions. |
| install/config/boot-permissions-fix.sh | Installer counterpart to apply /boot permission changes. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| #!/bin/bash | ||
|
|
||
| # Fix /boot permissions security issue | ||
| # See: https://github.com/basecamp/omarchy/issues/5377 | ||
|
|
||
| echo "Fixing /boot permissions for better security..." |
There was a problem hiding this comment.
The PR description/title focus on the supergfxd/NVIDIA detection fix, but this PR also adds migrations for /boot permissions, snapper, snapshot restore messaging, and hyprlock suspend behavior. Please update the PR description to cover these additional changes or split them into separate PRs so the scope matches the stated intent.
| [Install] | ||
| WantedBy=suspend.target hibernate.target hybrid-suspend.target | ||
| SYSTEMD | ||
|
|
There was a problem hiding this comment.
After writing systemd unit files elsewhere, the codebase typically runs systemctl daemon-reload (e.g. install/config/hardware/dell/fix-xps-haptic-touchpad.sh:34, install/config/docker.sh:32) before enabling/starting. This migration writes /etc/systemd/system/hyprlock-suspend.service and enables it without a daemon-reload, which can prevent systemd from picking it up immediately in the current session. Consider adding sudo systemctl daemon-reload before enabling the service.
| sudo systemctl daemon-reload |
|
|
||
| # Ensure /boot is mounted with proper permissions | ||
| # Add to fstab if not already present with correct options | ||
| if ! grep -q "^/boot" /etc/fstab 2>/dev/null; then |
There was a problem hiding this comment.
grep -q "^/boot" /etc/fstab will almost never match on typical Arch installs because the first fstab column is usually UUID=/LABEL=/device, not the mountpoint path. This will likely emit the warning even when /boot is correctly present in fstab. Consider parsing the 2nd field for a /boot mountpoint (e.g., awk on non-comment lines) instead.
| if ! grep -q "^/boot" /etc/fstab 2>/dev/null; then | |
| if ! awk '!/^[[:space:]]*#/ && NF >= 2 && $2 == "/boot" { found=1; exit } END { exit !found }' /etc/fstab 2>/dev/null; then |
| # Check if supergfxd is causing issues | ||
| if systemctl is-active --quiet supergfxd 2>/dev/null; then | ||
| echo "supergfxd is active - checking for blacklist issues..." | ||
|
|
||
| # Check if nvidia modules are blacklisted | ||
| if grep -q "blacklist nvidia" /etc/modprobe.d/supergfxd.conf 2>/dev/null; then | ||
| echo "Found nvidia blacklist from supergfxd!" | ||
| echo "Disabling supergfxd to enable NVIDIA..." | ||
|
|
||
| sudo systemctl disable --now supergfxd 2>/dev/null || true | ||
| sudo rm -f /etc/modprobe.d/supergfxd.conf 2>/dev/null || true | ||
|
|
||
| # Regenerate initramfs | ||
| sudo mkinitcpio -P 2>/dev/null || true | ||
|
|
||
| echo "✓ supergfxd disabled" | ||
| echo "⚠️ Please reboot for NVIDIA modules to load" | ||
|
|
||
| notify-send "NVIDIA fix applied" "Please reboot to enable NVIDIA GPU" | ||
| fi | ||
| else | ||
| echo "supergfxd is not active, no action needed" |
There was a problem hiding this comment.
This only runs the blacklist cleanup if supergfxd is currently active. The reported issue includes cases where supergfxd recreates /etc/modprobe.d/supergfxd.conf even when the service is disabled; in that scenario this migration will do nothing and the blacklist remains. Consider checking for the presence/contents of /etc/modprobe.d/supergfxd.conf regardless of service state, and removing/regen initramfs when it contains NVIDIA blacklists.
| # Check if supergfxd is causing issues | |
| if systemctl is-active --quiet supergfxd 2>/dev/null; then | |
| echo "supergfxd is active - checking for blacklist issues..." | |
| # Check if nvidia modules are blacklisted | |
| if grep -q "blacklist nvidia" /etc/modprobe.d/supergfxd.conf 2>/dev/null; then | |
| echo "Found nvidia blacklist from supergfxd!" | |
| echo "Disabling supergfxd to enable NVIDIA..." | |
| sudo systemctl disable --now supergfxd 2>/dev/null || true | |
| sudo rm -f /etc/modprobe.d/supergfxd.conf 2>/dev/null || true | |
| # Regenerate initramfs | |
| sudo mkinitcpio -P 2>/dev/null || true | |
| echo "✓ supergfxd disabled" | |
| echo "⚠️ Please reboot for NVIDIA modules to load" | |
| notify-send "NVIDIA fix applied" "Please reboot to enable NVIDIA GPU" | |
| fi | |
| else | |
| echo "supergfxd is not active, no action needed" | |
| SUPERGFXD_CONF="/etc/modprobe.d/supergfxd.conf" | |
| # Check for persisted NVIDIA blacklists from supergfxd regardless of service state | |
| if grep -Eq '^[[:space:]]*blacklist[[:space:]]+nvidia([_-][[:alnum:]_]+)?([[:space:]]|$)' "$SUPERGFXD_CONF" 2>/dev/null; then | |
| echo "Found nvidia blacklist from supergfxd!" | |
| echo "Disabling supergfxd to enable NVIDIA..." | |
| sudo systemctl disable --now supergfxd 2>/dev/null || true | |
| sudo rm -f "$SUPERGFXD_CONF" 2>/dev/null || true | |
| # Regenerate initramfs | |
| sudo mkinitcpio -P 2>/dev/null || true | |
| echo "✓ supergfxd disabled" | |
| echo "⚠️ Please reboot for NVIDIA modules to load" | |
| notify-send "NVIDIA fix applied" "Please reboot to enable NVIDIA GPU" | |
| else | |
| echo "No supergfxd nvidia blacklist found, no action needed" |
| WRAPPER="/usr/local/bin/omarchy-snapshot-restore-safe" | ||
| cat > "$WRAPPER" << 'WRAPPEREOF' | ||
| #!/bin/bash |
There was a problem hiding this comment.
This writes to /usr/local/bin, but cat > "$WRAPPER" is not run with sudo, so it will fail on typical systems and the migration will still continue (and be marked applied) without actually creating the wrapper. Use sudo tee/sudo install (or sudo sh -c ...) to create the file with root permissions, and consider failing the migration if creation doesn’t succeed.
| # Non-interactive: just disable | ||
| sudo systemctl disable --now supergfxd 2>/dev/null || true | ||
| sudo rm -f /etc/modprobe.d/supergfxd.conf 2>/dev/null || true | ||
| echo "Auto-disabled supergfxd for NVIDIA compatibility" |
There was a problem hiding this comment.
In the non-interactive path you remove the blacklist file but don’t regenerate initramfs. If the blacklist was included in the initramfs via the modconf hook, NVIDIA modules may still be blocked until a later mkinitcpio run. Consider running mkinitcpio -P (or clearly deferring it to a known later installer step) in this branch as well.
| echo "Auto-disabled supergfxd for NVIDIA compatibility" | |
| sudo mkinitcpio -P 2>/dev/null || true | |
| echo "Auto-disabled supergfxd for NVIDIA compatibility and regenerated initramfs" | |
| echo "Please reboot for changes to take effect" |
| #!/bin/bash | ||
|
|
||
| # Fix NVIDIA GPU detection when supergfxd is blacklisting modules | ||
| # See: https://github.com/basecamp/omarchy/issues/5408 | ||
|
|
||
| echo "Fixing NVIDIA GPU detection..." |
There was a problem hiding this comment.
This script appears to be intended as part of the installer (“New installer script”), but it isn’t referenced anywhere in the install flow (no calls found from install/config/all.sh or other install scripts). If it should run automatically during install, it needs to be wired into the installer (e.g., added to install/config/all.sh via run_logged).
| #!/bin/bash | ||
|
|
||
| # Fix NVIDIA + hyprlock suspend freeze issue | ||
| # See: https://github.com/basecamp/omarchy/issues/5277 | ||
|
|
||
| echo "Applying NVIDIA suspend fix..." |
There was a problem hiding this comment.
This script is placed under install/config, but it isn’t referenced from the installer entrypoints (no calls found from install/config/all.sh or other install scripts). If it’s meant to be applied during install, it should be wired into the install flow via run_logged (otherwise it’s currently dead code).
| #!/bin/bash | ||
|
|
||
| # Fix /boot permissions security issue | ||
| # The random seed file and /boot mount should not be world accessible | ||
| # See: https://github.com/basecamp/omarchy/issues/5377 | ||
|
|
||
| echo "Fixing /boot permissions for better security..." |
There was a problem hiding this comment.
This script is placed under install/config, but it isn’t referenced from the installer entrypoints (no calls found from install/config/all.sh or other install scripts). If it’s intended to run during install, it needs to be added to the install flow; otherwise it won’t ever be applied automatically.
| #!/bin/bash | ||
|
|
||
| # Fix snapper /home config creation for chroot installations | ||
| # See: https://github.com/basecamp/omarchy/issues/5344 | ||
|
|
||
| echo "Ensuring snapper /home config is created..." |
There was a problem hiding this comment.
This script is placed under install/config, but it isn’t referenced from the installer entrypoints (no calls found from install/config/all.sh or other install scripts). If it’s meant to ensure snapper configs during install, it should be wired into the install flow; otherwise it won’t run.
On hybrid GPU laptops, supergfxd recreates blacklist files that prevent NVIDIA driver from loading, even when user wants to use NVIDIA mode. This fix: - Detects if supergfxd is blacklisting nvidia modules - Offers to disable supergfxd - Removes the blacklist file - Regenerates initramfs Fixes: basecamp#5408
b274b5d to
3f33505
Compare
- migrations/1777007500.sh: Guard notify-send, add exit 0 - migrations/1777007502.sh: Simplify to just document the warning - migrations/1777007503.sh: Add systemctl daemon-reload and chrootable_systemctl_enable - migrations/1777007504.sh: Check blacklist regardless of service state, guard notify-send - install/config/*.sh: Fix fstab parsing, add daemon-reload, check blacklist file - install/config/all.sh: Add new installer scripts - default/hypr/apps.conf: Use absolute paths instead of ~ for Hyprland compatibility - bin/omarchy-snapshot: Add /home exclusion warning Addresses all 15 Copilot review comments.
Updates AppliedThank you for the thorough review! I've addressed all 15 comments: ✅ Fixed Issuesmigrations/1777007500.sh:
migrations/1777007502.sh:
migrations/1777007503.sh:
migrations/1777007504.sh:
install/config/boot-permissions-fix.sh:
install/config/nvidia-suspend-fix.sh & supergfxd-nvidia-fix.sh:
install/config/all.sh:
default/hypr/apps.conf:
|
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 11 out of 13 changed files in this pull request and generated 6 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| if ! sudo snapper list-configs 2>/dev/null | grep -q "root"; then | ||
| echo "Creating snapper config for root..." | ||
| sudo snapper -c root create-config / 2>/dev/null || echo "Warning: Could not create root snapper config" | ||
| sudo cp $OMARCHY_PATH/default/snapper/root /etc/snapper/configs/root 2>/dev/null || true |
There was a problem hiding this comment.
Similarly, grep -q "root" is not constrained to the config name column of snapper list-configs. It’s safer to match the config name explicitly (e.g., grep -qE '^root[[:space:]]') to avoid accidental matches with other configs/columns.
| source = /home/kuro/.local/share/omarchy/default/hypr/apps/1password.conf | ||
| source = /home/kuro/.local/share/omarchy/default/hypr/apps/bitwarden.conf | ||
| source = /home/kuro/.local/share/omarchy/default/hypr/apps/browser.conf | ||
| source = /home/kuro/.local/share/omarchy/default/hypr/apps/hyprshot.conf | ||
| source = /home/kuro/.local/share/omarchy/default/hypr/apps/localsend.conf | ||
| source = /home/kuro/.local/share/omarchy/default/hypr/apps/pip.conf | ||
| source = /home/kuro/.local/share/omarchy/default/hypr/apps/qemu.conf | ||
| source = /home/kuro/.local/share/omarchy/default/hypr/apps/retroarch.conf | ||
| source = /home/kuro/.local/share/omarchy/default/hypr/apps/steam.conf | ||
| source = /home/kuro/.local/share/omarchy/default/hypr/apps/geforce.conf | ||
| source = /home/kuro/.local/share/omarchy/default/hypr/apps/moonlight.conf | ||
| source = /home/kuro/.local/share/omarchy/default/hypr/apps/system.conf | ||
| source = /home/kuro/.local/share/omarchy/default/hypr/apps/telegram.conf | ||
| source = /home/kuro/.local/share/omarchy/default/hypr/apps/typora.conf | ||
| source = /home/kuro/.local/share/omarchy/default/hypr/apps/terminals.conf | ||
| source = /home/kuro/.local/share/omarchy/default/hypr/apps/walker.conf | ||
| source = /home/kuro/.local/share/omarchy/default/hypr/apps/webcam-overlay.conf No newline at end of file |
There was a problem hiding this comment.
These source = paths are hard-coded to /home/kuro, which will break Hyprland config loading for every other username. Please revert to a user-relative path (e.g., ~/.local/share/... or $HOME/.local/share/...) so the default config is portable.
| source = /home/kuro/.local/share/omarchy/default/hypr/apps/1password.conf | |
| source = /home/kuro/.local/share/omarchy/default/hypr/apps/bitwarden.conf | |
| source = /home/kuro/.local/share/omarchy/default/hypr/apps/browser.conf | |
| source = /home/kuro/.local/share/omarchy/default/hypr/apps/hyprshot.conf | |
| source = /home/kuro/.local/share/omarchy/default/hypr/apps/localsend.conf | |
| source = /home/kuro/.local/share/omarchy/default/hypr/apps/pip.conf | |
| source = /home/kuro/.local/share/omarchy/default/hypr/apps/qemu.conf | |
| source = /home/kuro/.local/share/omarchy/default/hypr/apps/retroarch.conf | |
| source = /home/kuro/.local/share/omarchy/default/hypr/apps/steam.conf | |
| source = /home/kuro/.local/share/omarchy/default/hypr/apps/geforce.conf | |
| source = /home/kuro/.local/share/omarchy/default/hypr/apps/moonlight.conf | |
| source = /home/kuro/.local/share/omarchy/default/hypr/apps/system.conf | |
| source = /home/kuro/.local/share/omarchy/default/hypr/apps/telegram.conf | |
| source = /home/kuro/.local/share/omarchy/default/hypr/apps/typora.conf | |
| source = /home/kuro/.local/share/omarchy/default/hypr/apps/terminals.conf | |
| source = /home/kuro/.local/share/omarchy/default/hypr/apps/walker.conf | |
| source = /home/kuro/.local/share/omarchy/default/hypr/apps/webcam-overlay.conf | |
| source = ~/.local/share/omarchy/default/hypr/apps/1password.conf | |
| source = ~/.local/share/omarchy/default/hypr/apps/bitwarden.conf | |
| source = ~/.local/share/omarchy/default/hypr/apps/browser.conf | |
| source = ~/.local/share/omarchy/default/hypr/apps/hyprshot.conf | |
| source = ~/.local/share/omarchy/default/hypr/apps/localsend.conf | |
| source = ~/.local/share/omarchy/default/hypr/apps/pip.conf | |
| source = ~/.local/share/omarchy/default/hypr/apps/qemu.conf | |
| source = ~/.local/share/omarchy/default/hypr/apps/retroarch.conf | |
| source = ~/.local/share/omarchy/default/hypr/apps/steam.conf | |
| source = ~/.local/share/omarchy/default/hypr/apps/geforce.conf | |
| source = ~/.local/share/omarchy/default/hypr/apps/moonlight.conf | |
| source = ~/.local/share/omarchy/default/hypr/apps/system.conf | |
| source = ~/.local/share/omarchy/default/hypr/apps/telegram.conf | |
| source = ~/.local/share/omarchy/default/hypr/apps/typora.conf | |
| source = ~/.local/share/omarchy/default/hypr/apps/terminals.conf | |
| source = ~/.local/share/omarchy/default/hypr/apps/walker.conf | |
| source = ~/.local/share/omarchy/default/hypr/apps/webcam-overlay.conf |
| # This script adds warning output to omarchy-snapshot to inform users | ||
|
|
||
| # Update omarchy-snapshot with /home exclusion warning | ||
| if [[ -f /usr/local/bin/omarchy-snapshot ]]; then | ||
| if ! grep -q "will NOT be affected" /usr/local/bin/omarchy-snapshot 2>/dev/null; then | ||
| echo "Warning: /usr/local/bin/omarchy-snapshot not updated (may already have warning)" | ||
| fi | ||
| fi | ||
|
|
There was a problem hiding this comment.
This migration claims to "configure" /home-exclusion messaging, but it never actually updates the target script; it only prints a warning and then reports success. If the goal is to update existing installs, the migration should patch/replace the installed omarchy-snapshot (or ensure it points at the updated $OMARCHY_PATH/bin/omarchy-snapshot) instead of being a no-op.
| # This script adds warning output to omarchy-snapshot to inform users | |
| # Update omarchy-snapshot with /home exclusion warning | |
| if [[ -f /usr/local/bin/omarchy-snapshot ]]; then | |
| if ! grep -q "will NOT be affected" /usr/local/bin/omarchy-snapshot 2>/dev/null; then | |
| echo "Warning: /usr/local/bin/omarchy-snapshot not updated (may already have warning)" | |
| fi | |
| fi | |
| # This migration must update the installed omarchy-snapshot so existing installs | |
| # receive the warning output as intended. | |
| SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" | |
| REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" | |
| SOURCE_SNAPSHOT="$REPO_ROOT/bin/omarchy-snapshot" | |
| TARGET_SNAPSHOT="/usr/local/bin/omarchy-snapshot" | |
| if [[ ! -f "$SOURCE_SNAPSHOT" ]]; then | |
| echo "Error: updated snapshot script not found at $SOURCE_SNAPSHOT" | |
| exit 1 | |
| fi | |
| if [[ ! -d "$(dirname "$TARGET_SNAPSHOT")" ]]; then | |
| echo "Error: target directory $(dirname "$TARGET_SNAPSHOT") does not exist" | |
| exit 1 | |
| fi | |
| if ! install -m 0755 "$SOURCE_SNAPSHOT" "$TARGET_SNAPSHOT"; then | |
| echo "Error: failed to update $TARGET_SNAPSHOT" | |
| exit 1 | |
| fi | |
| if ! grep -q "will NOT be affected" "$TARGET_SNAPSHOT" 2>/dev/null; then | |
| echo "Error: $TARGET_SNAPSHOT was updated, but the /home exclusion warning is still missing" | |
| exit 1 | |
| fi |
| run_logged $OMARCHY_INSTALL/config/supergfxd-nvidia-fix.sh | ||
| run_logged $OMARCHY_INSTALL/config/nvidia-suspend-fix.sh | ||
| run_logged $OMARCHY_INSTALL/config/boot-permissions-fix.sh |
There was a problem hiding this comment.
The PR description says only two files are changed for the supergfxd NVIDIA fix, but this commit also adds/executes other fixes (NVIDIA suspend, /boot permissions, snapper messaging, Hyprland config changes, etc.). Please update the PR description/scope so reviewers understand the full impact and can validate each behavior change.
| if ! sudo snapper list-configs 2>/dev/null | grep -q "home"; then | ||
| echo "Creating snapper config for /home..." | ||
| sudo snapper -c home create-config /home 2>/dev/null || echo "Warning: Could not create /home snapper config" |
There was a problem hiding this comment.
Using grep -q "home" on snapper list-configs can produce false positives because the output includes a subvolume column (e.g., any config with subvolume /home would match even if the config name isn’t home). This can cause the script to skip creating the required home config and later commands that assume -c home to fail. Prefer matching the config name column explicitly (e.g., grep -qE '^home[[:space:]]').
| # /home has .snapshots subdirectory, ensure config exists | ||
| if ! sudo snapper list-configs 2>/dev/null | grep -q "home"; then | ||
| echo "Creating snapper config for /home subvolume..." | ||
| sudo snapper -c home create-config /home 2>/dev/null || echo "Warning: Could not create /home snapper config" | ||
| fi |
There was a problem hiding this comment.
Same issue as above: grep -q "home" can match the subvolume column rather than the config name, so the check can incorrectly think the home config exists. Match the config name column explicitly (e.g., ^home[[:space:]]).
- Revert apps.conf to use ~ paths for portability - Fix snapper-home-config.sh to use proper grep patterns (^home and ^root) - Simplify migrations/1777007502.sh to actually update omarchy-snapshot - Add snapper-home-config.sh to all.sh installer
Second Round UpdatesThank you for the follow-up review! I've addressed all the new comments: Fixes Applieddefault/hypr/apps.conf:
install/config/snapper-home-config.sh:
migrations/1777007502.sh:
install/config/all.sh:
Changes Pushed |
### migrations/1777007500.sh: - Add FAT filesystem detection and mount options fix - Add bootctl random-seed call - Guard notify-send properly ### migrations/1777007501.sh: - Remove /home snapper config (conflicts with 1776927490) - Only create root config - Add snapper command check - Quote OMARCHY_PATH ### migrations/1777007503.sh: - Remove After=hypridle.service (ineffective) - Make pkill non-fatal with - prefix - Add systemctl daemon-reload ### install/config/nvidia-suspend-fix.sh: - Same pkill and daemon-reload fixes - Remove After=hypridle.service ### install/config/boot-permissions-fix.sh: - Add FAT filesystem detection and mount options - Fix fstab parsing to use awk on second column - Add bootctl random-seed call
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 10 out of 13 changed files in this pull request and generated 7 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| if [[ "$boot_fs_type" =~ ^(vfat|fat|msdos)$ ]]; then | ||
| if [[ "$boot_mount_options" == *"umask=0077"* ]] || [[ "$boot_mount_options" == *"dmask=0077"* && "$boot_mount_options" == *"fmask=0177"* ]]; then | ||
| echo "✓ /boot mount options now include restrictive umask" | ||
| fi |
There was a problem hiding this comment.
The verification block checks "$boot_mount_options" that was captured before the remount. If the script remounts /boot with new masks, consider re-reading mount options via findmnt before verifying/printing success, otherwise the success message may not reflect the current state.
| if [[ "$boot_fs_type" =~ ^(vfat|fat|msdos)$ ]]; then | ||
| echo "/boot is on $boot_fs_type; applying mount masks because chmod doesn't change effective permissions on FAT" | ||
|
|
||
| # Check if restrictive mount options already exist | ||
| if [[ "$boot_mount_options" == *"umask=0077"* ]] || [[ "$boot_mount_options" == *"dmask=0077"* && "$boot_mount_options" == *"fmask=0177"* ]]; then | ||
| echo "/boot already has restrictive mount options" | ||
| else | ||
| sudo mount -o remount,dmask=0077,fmask=0177 /boot 2>/dev/null || echo "Warning: Could not remount /boot with restrictive permissions" | ||
| fi |
There was a problem hiding this comment.
In the FAT (/boot on vfat/fat/msdos) branch, the script only remounts with dmask/fmask. That doesn’t persist across reboot; to fully address the bootctl warning, the restrictive masks should be written to the /etc/fstab entry (or a systemd mount unit) for /boot rather than relying on a one-time remount.
| # Fix snapper /home config creation for chroot installations | ||
| # See: https://github.com/basecamp/omarchy/issues/5344 | ||
|
|
||
| echo "Ensuring snapper /home config is created..." | ||
|
|
||
| # Check if /home is on a separate subvolume or btrfs | ||
| if mountpoint -q /home 2>/dev/null; then | ||
| # /home is a separate mount point | ||
| if ! sudo snapper list-configs 2>/dev/null | grep -qE '^home[[:space:]]'; then | ||
| echo "Creating snapper config for /home..." | ||
| sudo snapper -c home create-config /home 2>/dev/null || echo "Warning: Could not create /home snapper config" | ||
| fi | ||
| elif [[ -d /home/.snapshots ]]; then | ||
| # /home has .snapshots subdirectory, ensure config exists | ||
| if ! sudo snapper list-configs 2>/dev/null | grep -qE '^home[[:space:]]'; then | ||
| echo "Creating snapper config for /home subvolume..." | ||
| sudo snapper -c home create-config /home 2>/dev/null || echo "Warning: Could not create /home snapper config" | ||
| fi | ||
| else | ||
| echo "/home is not on a separate subvolume, skipping /home snapper config" | ||
| fi | ||
|
|
There was a problem hiding this comment.
This script creates a Snapper "home" config, which conflicts with the repo’s established behavior of snapshotting only root (see install/login/limine-snapper.sh:53-57 and migrations/1776927490.sh which deletes the home config). Creating /home snapshots again can reintroduce the accidental user-data rollback the project is trying to avoid; consider removing the /home config creation or gating it behind an explicit opt-in setting.
| # Fix snapper /home config creation for chroot installations | |
| # See: https://github.com/basecamp/omarchy/issues/5344 | |
| echo "Ensuring snapper /home config is created..." | |
| # Check if /home is on a separate subvolume or btrfs | |
| if mountpoint -q /home 2>/dev/null; then | |
| # /home is a separate mount point | |
| if ! sudo snapper list-configs 2>/dev/null | grep -qE '^home[[:space:]]'; then | |
| echo "Creating snapper config for /home..." | |
| sudo snapper -c home create-config /home 2>/dev/null || echo "Warning: Could not create /home snapper config" | |
| fi | |
| elif [[ -d /home/.snapshots ]]; then | |
| # /home has .snapshots subdirectory, ensure config exists | |
| if ! sudo snapper list-configs 2>/dev/null | grep -qE '^home[[:space:]]'; then | |
| echo "Creating snapper config for /home subvolume..." | |
| sudo snapper -c home create-config /home 2>/dev/null || echo "Warning: Could not create /home snapper config" | |
| fi | |
| else | |
| echo "/home is not on a separate subvolume, skipping /home snapper config" | |
| fi | |
| # Ensure snapper configuration matches the project's root-only snapshot policy | |
| # /home snapshotting is intentionally not auto-created to avoid user-data rollback | |
| echo "Ensuring snapper root config is created..." | |
| echo "Skipping snapper /home config creation; this system snapshots only root." |
| ExecStart=-/usr/bin/pkill -STOP hyprlock | ||
| RemainAfterExit=yes | ||
| ExecStop=-/usr/bin/pkill -CONT hyprlock | ||
| TimeoutStopSec=5 |
There was a problem hiding this comment.
The unit uses pkill ... hyprlock without -x (exact match) or user scoping, which can signal unintended processes and affect all users’ sessions. Consider exact matching and (if possible) scoping to the active graphical user/session to avoid collateral SIGSTOP/SIGCONT.
| if [[ "$boot_fs_type" =~ ^(vfat|fat|msdos)$ ]]; then | ||
| echo "/boot is on $boot_fs_type; applying mount masks because chmod doesn't change effective permissions on FAT" | ||
|
|
||
| # Check if restrictive mount options already exist | ||
| if [[ "$boot_mount_options" == *"umask=0077"* ]] || [[ "$boot_mount_options" == *"dmask=0077"* && "$boot_mount_options" == *"fmask=0177"* ]]; then | ||
| echo "/boot already has restrictive mount options" | ||
| else | ||
| sudo mount -o remount,dmask=0077,fmask=0177 /boot 2>/dev/null || echo "Warning: Could not remount /boot with restrictive permissions" | ||
| fi |
There was a problem hiding this comment.
In the FAT (/boot on vfat/fat/msdos) branch, the script only remounts with dmask/fmask. That change is not persistent across reboots, so the bootctl warning will likely return after reboot unless the restrictive masks are added to the /etc/fstab (or the systemd mount unit) options for /boot.
| # Check if /boot is in fstab with correct mountpoint (second column) | ||
| if ! awk '!/^[[:space:]]*#/ && NF >= 2 && $2 == "/boot" { found=1; exit } END { exit !found }' /etc/fstab 2>/dev/null; then | ||
| echo "Warning: /boot is not in fstab, permissions may not persist" | ||
| fi |
There was a problem hiding this comment.
The fstab warning is emitted even when /boot is not a separate mount (e.g., it’s just a directory on the root filesystem). Since chmod changes on a normal filesystem persist without an fstab entry, this can produce a false warning; consider only warning when /boot is actually its own mountpoint (or when the filesystem is FAT and needs mount options).
| # Create a systemd service to stop hyprlock before suspend | ||
| # The - prefix makes pkill non-fatal when hyprlock isn't running | ||
| cat << 'SYSTEMD' | sudo tee /etc/systemd/system/hyprlock-suspend.service > /dev/null | ||
| [Unit] | ||
| Description=Stop hyprlock before suspend/hibernate | ||
| Before=suspend.target hibernate.target hybrid-suspend.target | ||
| DefaultDependencies=no | ||
|
|
||
| [Service] | ||
| Type=oneshot | ||
| ExecStart=-/usr/bin/pkill -STOP hyprlock | ||
| RemainAfterExit=yes | ||
| ExecStop=-/usr/bin/pkill -CONT hyprlock | ||
| TimeoutStopSec=5 | ||
|
|
||
| [Install] | ||
| WantedBy=suspend.target hibernate.target hybrid-suspend.target | ||
| SYSTEMD | ||
|
|
||
| # Reload systemd daemon to recognize the new unit | ||
| sudo systemctl daemon-reload | ||
|
|
||
| # Enable the service using chrootable helper if available | ||
| if command -v chrootable_systemctl_enable >/dev/null 2>&1; then | ||
| chrootable_systemctl_enable hyprlock-suspend.service 2>/dev/null || echo "Warning: Could not enable hyprlock-suspend service" | ||
| else | ||
| sudo systemctl enable hyprlock-suspend.service 2>/dev/null || echo "Warning: Could not enable hyprlock-suspend service" |
There was a problem hiding this comment.
The unit uses pkill ... hyprlock without -x (exact match) or any user scoping. This can signal unintended processes whose name merely contains “hyprlock”, and it will affect all users’ sessions. Consider using exact matching (and ideally scoping to the active session/user) to avoid collateral SIGSTOP/SIGCONT.
| # Create a systemd service to stop hyprlock before suspend | |
| # The - prefix makes pkill non-fatal when hyprlock isn't running | |
| cat << 'SYSTEMD' | sudo tee /etc/systemd/system/hyprlock-suspend.service > /dev/null | |
| [Unit] | |
| Description=Stop hyprlock before suspend/hibernate | |
| Before=suspend.target hibernate.target hybrid-suspend.target | |
| DefaultDependencies=no | |
| [Service] | |
| Type=oneshot | |
| ExecStart=-/usr/bin/pkill -STOP hyprlock | |
| RemainAfterExit=yes | |
| ExecStop=-/usr/bin/pkill -CONT hyprlock | |
| TimeoutStopSec=5 | |
| [Install] | |
| WantedBy=suspend.target hibernate.target hybrid-suspend.target | |
| SYSTEMD | |
| # Reload systemd daemon to recognize the new unit | |
| sudo systemctl daemon-reload | |
| # Enable the service using chrootable helper if available | |
| if command -v chrootable_systemctl_enable >/dev/null 2>&1; then | |
| chrootable_systemctl_enable hyprlock-suspend.service 2>/dev/null || echo "Warning: Could not enable hyprlock-suspend service" | |
| else | |
| sudo systemctl enable hyprlock-suspend.service 2>/dev/null || echo "Warning: Could not enable hyprlock-suspend service" | |
| # Create a per-user systemd service to stop hyprlock before suspend | |
| # The - prefix makes pkill non-fatal when hyprlock isn't running | |
| cat << 'SYSTEMD' | sudo tee /etc/systemd/system/hyprlock-suspend@.service > /dev/null | |
| [Unit] | |
| Description=Stop hyprlock before suspend/hibernate for user %I | |
| Before=suspend.target hibernate.target hybrid-suspend.target | |
| DefaultDependencies=no | |
| [Service] | |
| Type=oneshot | |
| ExecStart=-/usr/bin/pkill -u %I -x -STOP hyprlock | |
| RemainAfterExit=yes | |
| ExecStop=-/usr/bin/pkill -u %I -x -CONT hyprlock | |
| TimeoutStopSec=5 | |
| [Install] | |
| WantedBy=suspend.target hibernate.target hybrid-suspend.target | |
| SYSTEMD | |
| target_user="${SUDO_USER:-$USER}" | |
| # Reload systemd daemon to recognize the new unit | |
| sudo systemctl daemon-reload | |
| # Enable the service using chrootable helper if available | |
| if command -v chrootable_systemctl_enable >/dev/null 2>&1; then | |
| chrootable_systemctl_enable "hyprlock-suspend@${target_user}.service" 2>/dev/null || echo "Warning: Could not enable hyprlock-suspend service" | |
| else | |
| sudo systemctl enable "hyprlock-suspend@${target_user}.service" 2>/dev/null || echo "Warning: Could not enable hyprlock-suspend service" |
### migrations/1777007500.sh: - Re-read mount options after remount to verify fix - Only warn about fstab when /boot is a separate mount - Added note about fstab persistence for FAT filesystems ### migrations/1777007502.sh: - Now actually validates the update succeeded with exit codes - Proper error handling with exit 1 on failure ### install/config/boot-permissions-fix.sh: - Only warn about fstab when /boot is actually a separate mount - Added note about fstab persistence
Third Round UpdatesAll remaining review comments addressed: Fixes Appliedmigrations/1777007500.sh:
migrations/1777007502.sh:
install/config/boot-permissions-fix.sh:
Changes pushed: kuro-toji@69fdc51c |
Summary
This PR fixes multiple issues in Omarchy:
1. NVIDIA GPU Detection (fixes #5408)
Restores NVIDIA GPU detection on hybrid laptops by removing supergfxd-created module blacklists and disabling supergfxd when it interferes with NVIDIA driver binding.
2. NVIDIA + hyprlock Suspend Fix (fixes #5277)
Creates a systemd service () that stops hyprlock before suspend/hibernate to prevent NVIDIA from being blocked from proper suspend.
3. /boot Permissions Fix (fixes #5377)
Fixes /boot directory permissions to 700 and random-seed file to 600 for better security.
4. Snapper /home Config (fixes #5344)
Ensures snapper configs exist for both root and /home on chroot installations.
5. Snapshot Restore Messaging (fixes #5361)
Updates omarchy-snapshot restore command to warn users that /home is NOT included in snapshot restore.
Files Changed
Migrations
Installer Scripts
Configuration
Testing
Each fix includes verification steps documented in the individual migration scripts.