Skip to content
Β 
Β 

Latest commit

Β 

History

122 Commits

Folders and files

NameName
Last commit message
Last commit date
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

πŸ”„ waybar-nixos-updates

License: GPL-3.0 GitHub stars

A Waybar update checking script for NixOS that checks for available updates and displays them in your Waybar.

Here's how the module looks in Waybar with and without updates:

Screenshot with updates Screenshot updates

Here's how the module's tooltip looks when updates are available: Screenshot with updates

Credit goes to this project for the idea and starting point.

πŸ“¦ Dependencies

When using the flake, all dependencies are automatically handled. The script requires:

πŸ”§ Commands/Programs:

  1. nix - Used for nix flake update and nix build commands
  2. nvd - Used for comparing system versions (nvd diff)
  3. notify-send - For desktop notifications
  4. Standard utilities: bash, grep, awk, sed, iproute2 (for ip command)

πŸ’» System Requirements:

  1. NixOS operating system
  2. A running Waybar instance (the script outputs JSON for Waybar integration)
  3. Internet connectivity for performing update checks
  4. Desktop notification system compatible with notify-send

πŸ“‹ Configuration Assumptions:

  • Your flake is in ~/.config/nixos (configurable via Home Manager module)
  • Your flake's nixosConfigurations is named the same as your $hostname

⚑ Check Modes

waybar-nixos-updates supports two update checking strategies:

Full Mode (Default)

The original approach that performs a complete system closure build and diff:

  • Runs nix flake update + nix build + nvd diff
  • Detects all package changes including transitive dependencies
  • Shows added/removed packages
  • Takes several minutes to complete

Lightweight Mode

A faster alternative using lazy Nix evaluation:

  • Compares .version attributes using a single nix eval
  • Completes in seconds rather than minutes
  • Checks both NixOS system packages AND home-manager packages (auto-detected)
  • Supports single-channel or dual-channel (stable + unstable) configurations
  • ~80-85% attribute name coverage (some store paths don't map to nixpkgs attrs)
  • No transitive dependency tracking
Feature Full Lightweight
Speed Minutes (full build) Seconds (lazy eval)
Accuracy Complete closure diff Top-level packages only
Transitive deps βœ“ βœ—
Added/removed pkgs βœ“ βœ—
Attr name coverage 100% ~80-85%

Explicit Packages Filter: When CONFIG_DIR is set, lightweight mode automatically enables EXPLICIT_PACKAGES_ONLY mode, which only reports updates for packages explicitly defined in your nix configuration files. This significantly reduces false positives from system dependencies and provides results closer to what full mode would report.

Choose lightweight mode if you want quick, frequent checks and don't need to track transitive dependencies. Choose full mode if you need complete accuracy.

πŸš€ How to Use

πŸ’Ώ Installation Methods

This project provides multiple installation methods through its Nix flake:

1. Using the Flake as a Package

Add to your flake inputs:

{
  inputs.waybar-nixos-updates.url = "github:yourusername/waybar-nixos-updates";
  
  # In your system configuration:
  environment.systemPackages = [
    inputs.waybar-nixos-updates.packages.${system}.default
  ];
}

2. Using Home Manager Module (Recommended)

This provides the most flexibility for configuration:

{
  inputs.waybar-nixos-updates.url = "github:yourusername/waybar-nixos-updates";
  
  # In your home-manager configuration:
  imports = [ inputs.waybar-nixos-updates.homeManagerModules.default ];
  
  programs.waybar-nixos-updates = {
    enable = true;
    checkMode = "lightweight";      # "full" (default) or "lightweight"
    updateInterval = 3600;          # Check every hour
    notifications = true;           # Set to false to disable desktop notifications
    
    # Path to your NixOS flake (used by both modes):
    # - Full mode: for nix build and nvd diff
    # - Lightweight mode: for flake.lock and .nix file scanning
    nixosConfigPath = "~/.config/nixos";
    
    # Full mode only:
    updateLockFile = false;         # Use temp dir for checks
    
    # Lightweight mode - Option A: Single channel (simple)
    nixpkgsChannel = "github:NixOS/nixpkgs/nixpkgs-unstable";
    
    # Lightweight mode - Option B: Dual channel (for mixed stable/unstable)
    # Scans nixosConfigPath for .nix files to determine package sources
    # nixpkgsChannel = {
    #   stable = "pkgs";                  # Matches: pkgs.foo, with pkgs; [...]
    #   unstable = "pkgs-unstable";       # Matches: pkgs-unstable.foo, with pkgs-unstable; [...]
    # };
    
    # Common options:
    skipAfterBoot = true;           # Skip checks after boot/resume
    gracePeriod = 60;               # Wait 60s after boot
  };
  
  # Then add to your waybar configuration:
  programs.waybar.settings.mainBar."custom/nix-updates" = 
    config.programs.waybar-nixos-updates.waybarConfig;
}

3. Using NixOS Module

For system-wide installation:

{
  imports = [ inputs.waybar-nixos-updates.nixosModules.default ];
  
  services.waybar-nixos-updates.enable = true;
}

4. Using the Legacy default.nix

You can still use the included default.nix file with Home Manager:

imports = [ ./path-to-waybar-nixos-updates/default.nix ];

5. Manual Installation

For a manual installation, download the update-checker script, put it in your PATH and make it executable (chmod +x update-checker). Add the icons to your ~/.icons folder.

βš™οΈ Configuration Options

When using the Home Manager module, you can configure these options:

  • checkMode: Update check strategy - "full" (default) or "lightweight"
    • In lightweight mode, versions are compared with Nix's own ordering (builtins.compareVersions), so 5.3p9 β†’ 5.3p15 is correctly an upgrade and 1.16.1 β†’ 1.3.6 is not
    • A pending change where the channel is behind what you have installed is reported and marked (downgrade) rather than hidden β€” moving a package from pkgs-unstable to pkgs is a deliberate change worth seeing before you rebuild
  • updateInterval: Time in seconds between update checks (default: 3600)
  • notifications: Whether to show desktop notifications (default: true)
  • skipAfterBoot: Whether to skip update checks right after boot/resume (default: true)
  • gracePeriod: Time in seconds to wait after boot/resume before checking (default: 60)
  • clockFormat: Clock format for tooltip timestamps - "24h" (e.g. 14:23, default) or "12h" (e.g. 2:23 PM)
  • inputChecker.mode: How to handle stale flake inputs (default: "disabled")
    • "disabled": Don't check inputs (no resources used)
    • "show": Check and show in tooltip, but don't include in count
    • "count": Check, show in tooltip, and include in waybar count
    • Uses git ls-remote to compare locked revisions against upstream
    • Tooltip shows separate "Packages:" and "Inputs:" sections when multiple exist
    • Supported input types:
      • GitHub inputs (github:owner/repo/branch)
      • Generic git inputs (git+https://...) - Bitbucket, GitLab, self-hosted, etc.
  • inputChecker.pinned: How to handle pinned flake inputs (default: "disabled")
    • "disabled": Don't check pinned inputs (no resources used)
    • "show": Check and show in separate "Pinned:" section, but don't count
    • "count": Check, show, and include in waybar count
    • Pinned inputs are those with original.rev set in flake.lock
  • sourceChecks: Explicit upstream policies for sources the package and input checks can't interpret on their own β€” fixed revisions in package expressions, local checkouts, named release lines, and forks. Each entry states a current source (flake-input, revision, tag, or local), an upstream repository, and a policy of branch or tag, so intent is declared rather than guessed. Set mode per entry to "disabled", "show", or "count".
    • Checks run git ls-remote, bounded by SOURCE_CHECK_TIMEOUT (default 60s) per call, and are skipped entirely when there is no default route
    • Misconfiguration is reported in the tooltip rather than passing silently: current revision not found, upstream unreachable, unknown policy, Invalid sourceChecks configuration
    • Only an unset SOURCE_CHECKS_JSON means "no checks configured". An empty or malformed value is reported as invalid rather than read as an empty list, so a broken environment can't look like a clean result

Both modes:

  • nixosConfigPath: Path to your NixOS configuration flake directory (default: ~/.config/nixos)
    • Full mode: Used for nix build and nvd diff
    • Lightweight mode: Used for reading flake.lock and scanning .nix files for package sources

Full mode only:

  • updateLockFile: Whether to update the lock file directly or use a temporary copy (default: false)

Lightweight mode only:

  • nixpkgsChannel: Either a single flake ref string, or an attrset for dual-channel mode:
    • Simple (single channel): "github:NixOS/nixpkgs/nixpkgs-unstable"
    • Dual channel (mixed stable/unstable):
      {
        stable = "pkgs";                 # Identifier for stable packages
        unstable = "pkgs-unstable";      # Identifier for unstable packages
      }
    • In dual-channel mode, nixosConfigPath is scanned for .nix files to determine package sources, and flake refs are auto-detected from your flake.lock
  • explicitPackagesOnly: Only report updates for packages explicitly defined in your config files (default: true in dual-channel mode, false otherwise)
  • lightweightExcludePatterns: Shell patterns for generated store outputs to skip before version parsing (default: [ "*-fish-completions" ]). Outputs like atuin-18.7.1-fish-completions otherwise have their suffix read as part of the version, showing a phantom update. Keep patterns anchored β€” *-completions would also drop real packages such as nix-bash-completions.

Lightweight mode features:

  • Home-manager packages: Automatically detected and included (no config needed)
  • Dual-channel support: Parses your nix configs to determine which packages are stable vs unstable
  • Caching: Parse results are cached and only refreshed when flake.lock changes

You can also modify these environment variables or set them at the top of the script to customize behavior:

Common variables (both modes):

  • UPDATE_INTERVAL: Time in seconds between update checks (default: 3599)
  • CACHE_DIR: Directory for storing cache files (default: ~/.cache)
  • NOTIFICATIONS_ENABLED: Set to "false" to disable desktop notifications (default: "true")
  • SKIP_AFTER_BOOT: Whether to skip update checks right after boot/resume (default: true)
  • GRACE_PERIOD: Time in seconds to wait after boot/resume before checking (default: 60)
  • CLOCK_FORMAT: Clock format for tooltip timestamps: "24h" | "12h" (default: "24h")
  • INPUT_CHECKER_MODE: How to handle stale inputs: "disabled" | "show" | "count" (default: "disabled")
  • INPUT_CHECKER_PINNED: How to handle pinned inputs: "disabled" | "show" | "count" (default: "disabled")

Full mode variables:

  • NIXOS_CONFIG_PATH: Path to your NixOS configuration (default: ~/.config/nixos)
  • UPDATE_LOCK_FILE: Whether to update the lock file directly or use a temporary copy (default: false)

Lightweight mode variables:

  • FLAKE_DIR: Path to flake directory - used for reading flake.lock and scanning .nix files (default: ~/.config/nixos)
  • NIXPKGS_CHANNEL: Nixpkgs flake ref for single-channel mode (e.g., "github:NixOS/nixpkgs/nixpkgs-unstable")
  • DUAL_CHANNEL_MODE: Set to "true" to enable dual-channel detection from flake.lock (default: "false")
  • EXPLICIT_PACKAGES_ONLY: Only report updates for packages explicitly in config files (default: "true" when DUAL_CHANNEL_MODE is enabled, "false" otherwise). This filters out system dependencies and provides more accurate results.
  • STABLE_IDENTIFIER: Identifier for stable packages in dual-channel mode (default: "pkgs")
  • UNSTABLE_IDENTIFIER: Identifier for unstable packages in dual-channel mode (default: "pkgs-unstable")

πŸ”„ Toggle Functionality

The script supports toggling update checks on/off. When disabled, it will show the last known state without performing new checks:

  • To toggle: Run update-checker toggle
  • The toggle state is preserved across restarts
  • When disabled, the module shows "disabled" state with the last check timestamp

🎨 Waybar Integration

If you're using the Home Manager module, the waybar configuration is automatically provided through config.programs.waybar-nixos-updates.waybarConfig. Otherwise, configure manually:

To configure manually, add one of the following configurations to your Waybar config (~/.config/waybar/config).

In json (if adding directly to the config file):

"custom/nix-updates": {
    "exec": "$HOME/bin/update-checker", // <--- path to script
    "signal": 12,
    "on-click": "$HOME/bin/update-checker toggle", // toggle update checking
    "on-click-right": "$HOME/bin/update-checker refresh", // force an update
    "interval": 3600, // refresh every hour
    "tooltip": true,
    "return-type": "json",
    "format": "{} {icon}",
    "format-icons": {
        "has-updates": "󰚰", // icon when updates needed
        "updating": "ο‹±", // icon when updating
        "updated": "", // icon when all packages updated
        "disabled": "󰚰", // icon when update checking is disabled
        "error": "ξ™”" // icon when errot occurs
    },
},

In nix (if adding it "the nix way" through home-manager):

"custom/nix-updates" = {
  exec = "$HOME/bin/update-checker";  # Or "${pkgs.waybar-nixos-updates}/bin/update-checker" if using the flake
  signal = 12;
  on-click = "$HOME/bin/update-checker toggle";  # Toggle update checking
  on-click-right = "$HOME/bin/update-checker refresh";  # Force an update
  interval = 3600;
  tooltip = true;
  return-type = "json";
  format = "{} {icon}";
  format-icons = {
    has-updates = "󰚰";
    updating = "ο‹±";
    updated = "";
    disabled = "󰚰";
    error = "ξ™”";
  };
};

Note: If using the Home Manager module, you can simply reference the pre-configured waybar settings:

programs.waybar.settings.mainBar."custom/nix-updates" = 
  config.programs.waybar-nixos-updates.waybarConfig;

To style use the #custom-nix-updates ID in your Waybar styles file (~/.config/waybar/styles.css). For more information see the Waybar wiki.

πŸ’‘ Complete Configuration Example

Here's a complete example of using waybar-nixos-updates with Home Manager:

# flake.nix
{
  inputs = {
    nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
    home-manager.url = "github:nix-community/home-manager";
    waybar-nixos-updates.url = "github:yourusername/waybar-nixos-updates";
  };

  outputs = { self, nixpkgs, home-manager, waybar-nixos-updates, ... }: {
    nixosConfigurations.hostname = nixpkgs.lib.nixosSystem {
      system = "x86_64-linux";
      modules = [
        home-manager.nixosModules.home-manager
        {
          home-manager.users.youruser = { config, ... }: {
            imports = [ waybar-nixos-updates.homeManagerModules.default ];
            
            # Enable the waybar-nixos-updates module
            programs.waybar-nixos-updates = {
              enable = true;
              checkMode = "lightweight";  # or "full" for complete accuracy
              updateInterval = 3600;
              notifications = true;       # set to false to disable notifications
              
              # For single-channel (all packages from one nixpkgs):
              # nixpkgsChannel = "github:NixOS/nixpkgs/nixpkgs-unstable";
              
              # For dual-channel (mixed stable + unstable packages):
              # nixosConfigPath is used to scan .nix files for package sources
              nixpkgsChannel = {
                stable = "pkgs";
                unstable = "pkgs-unstable";
              };
            };
            
            # Configure Waybar
            programs.waybar = {
              enable = true;
              settings = {
                mainBar = {
                  modules-right = [ "custom/nix-updates" "clock" "battery" ];
                  "custom/nix-updates" = config.programs.waybar-nixos-updates.waybarConfig;
                };
              };
              style = ''
                #custom-nix-updates {
                  color: #89b4fa;
                  margin: 0 10px;
                }
                #custom-nix-updates.has-updates {
                  color: #f38ba8;
                  font-weight: bold;
                }
                #custom-nix-updates.updating {
                  color: #f9e2af;
                }
                #custom-nix-updates.disabled {
                  color: #6c7086;
                  opacity: 0.7;
                }
                #custom-nix-updates.error {
                  color: #eba0ac;
                }
              '';
            };
          };
        }
      ];
    };
  };
}

πŸ“€ Flake Outputs

The flake provides the following outputs:

  • packages.default: The waybar-nixos-updates package (full mode)
  • packages.lightweight: The lightweight checker package
  • homeManagerModules.default: Home Manager module for user-level configuration
  • nixosModules.default: NixOS module for system-level installation
  • apps.default: Direct execution of the update-checker script (full mode)
  • apps.lightweight: Direct execution of the lightweight-checker script

πŸ” Troubleshooting

Common Issues and Solutions

  1. Script not finding NixOS configuration

    • Ensure your configuration is at ~/.config/nixos or update the nixosConfigPath option
    • Verify your hostname matches your nixosConfiguration name: echo $HOSTNAME
  2. Icons not displaying

    • When using Home Manager module, icons are automatically installed to ~/.icons
    • For manual installation, ensure icons are in ~/.icons/ directory
    • Check that your notification daemon supports PNG icons
  3. Updates not being detected

    • Check network connectivity: ping -c 1 8.8.8.8
    • Verify nvd is installed: which nvd
    • Clear cache and force update: rm ~/.cache/nix-update-* && pkill -RTMIN+12 waybar
  4. "Check tooltip for detailed error message"

    • Hover over the waybar module to see the full error
    • Common causes: missing dependencies, flake evaluation errors, network issues
  5. Module shows "updating" indefinitely

    • In lightweight mode a background check may still be running; it holds ~/.cache/nix-update-check.lock for its duration
    • A check still holding the lock after 15 minutes is treated as wedged β€” its process group is killed and the next check proceeds. Override with MAX_CHECK_SECONDS.
    • Force a fresh check: update-checker refresh β€” this keeps the current count, tooltip and last-checked time on screen, and swaps the header's "Next check" for "Checking for updates..." while it runs
    • Refreshing while a check is already running shows a "Please Wait" notification instead of doing nothing, and does not queue a second check for when the current one finishes
    • In full mode, clear the updating flag: rm ~/.cache/nix-update-updating-flag
    • Restart waybar: pkill waybar && waybar &
  6. Configuration changes not taking effect

    • When using the wrapper script, restart waybar after rebuilding
    • Verify the correct script is being executed: check waybar config exec path

⚑ System Integration

No integration is required. The checker automatically detects both rebuilds and flake input updates on its own - you do not need to modify your rebuild script or nix flake update aliases, and there are no flag files to touch.

Automatic Rebuild Detection

The checker caches the /run/current-system path (in nix-update-system-path) and compares it on each run. When it changes, the system has been rebuilt. It then runs nvd diff on the two existing store paths (fast - no building) to distinguish a real package update from a config-only rebuild:

  • Package versions changed: the count is reset to 0 ("System updated"), and the next check runs on the normal updateInterval schedule.
  • Config-only rebuild: the existing package state is preserved, so a config change doesn't clear a pending update count.

Automatic Input-Update Detection

The checker caches a hash of your flake.lock (in nix-update-flake-lock-input-hash). When the hash changes - i.e. any input was updated via nix flake update, regardless of which command or alias ran - it triggers an immediate input re-check while preserving package state.

Because of the above, a rebuild/update script can be as simple as:

nixup =
  "pushd ~/.config/nixos &&
  echo \"NixOS rebuilding...\" &&
  sudo nixos-rebuild switch --upgrade --flake .#hyprnix &&
  popd";

Waybar will reflect the new state on its next poll (or immediately if you send it the RTMIN+12 signal via pkill -x -RTMIN+12 .waybar-wrapped).

ℹ️ Additional Information

Some additional things to expect in regards to 1) what notifications you'll receive, 2) what files will be written, 3) and how the script uses your network connection.

πŸ”” Notifications

These notifications require notify-send to be installed on your system. The script sends desktop notifications to keep you informed.

To disable notifications: Set notifications = false; in your Home Manager configuration, or set the environment variable NOTIFICATIONS_ENABLED="false".

Notifications include:

  • When starting an update check: "Checking for Updates - Please be patient"
  • When throttled due to recent checks: "Please Wait" with time until next check
  • When updates are found: "Update Check Complete" with the number of updates
  • When no updates are found: "Update Check Complete - No updates available"
  • When connectivity fails: "Update Check Failed - Not connected to the internet"
  • When an update fails: "Update Check Failed - Check tooltip for detailed error message"

πŸ’Ύ Cache Files

The script uses several cache files in your ~/.cache directory:

  • nix-update-state: Stores the current number of available updates
  • nix-update-last-run: Tracks when the last update check was performed
  • nix-update-tooltip: Contains the tooltip text with update details
  • nix-update-boot-marker: Used to detect system boot/resume events
  • nix-update-toggle: Stores the enabled/disabled state for update checking
  • nix-update-system-path: Caches the last-seen /run/current-system path, used to auto-detect rebuilds
  • nix-update-flake-lock-input-hash: Caches a hash of flake.lock, used to auto-detect input updates
  • nix-update-updating-flag: Signals that a check is mid-run (full mode only)
  • nix-update-check.lock: Held while a background check runs, so only one runs at a time (lightweight mode only)
  • nix-update-error: Present when the last check failed, holding the reason; makes the module show its error state instead of a healthy count. Removed by the next successful check.
  • nix-update-diagnostic / .prev: What the last two checks actually resolved β€” channels, package-to-channel mapping size, and for each reported package which channel it was compared against versus which one the mapping put it in. The previous run is kept because a wrong result is usually followed immediately by a correct one, which would otherwise overwrite the evidence. If the module reports something you don't expect, cat ~/.cache/nix-update-diagnostic.prev is the place to start.
  • nix-update-force-check: Set by refresh to request a check before the interval is up; cleared once that check starts

πŸ”’ Privacy and Security Considerations

The script checks network connectivity locally using the ip command to verify network interfaces and routing tables. This approach:

  • Does not send any external network requests for connectivity checking
  • Only checks local network configuration (interfaces and routes)
  • Performs actual network requests only when fetching updates from configured Nix repositories
  • Provides better privacy as no external connectivity checks are performed

🀝 Contributing

PRs are welcome! Please test your changes and ensure they work with both the flake installation methods and manual installation.

πŸ“œ License

This project is licensed under the MIT License - see the LICENSE file for details.

About

A Waybar update checking script for NixOS.

Resources

Stars

23 stars

Watchers

0 watching

Forks

Releases

Sponsor this project

Packages

Contributors

Languages