Favicon

You are here: Home > Device Management > Apple > macOS > Scripts > Force Reboot

How to enforce reboots with a progressive escalation policy

macOS script that monitors uptime and escalates swiftDialog alerts to enforce reboots, with a forced 10-minute countdown after 13+ days without a restart.

5 min read

TL;DR

Automate repetitive tasks on managed devices using scripts in Applivery for efficient device management.

Security patches only take effect after a reboot. In practice, users postpone restarts indefinitely β€” and the longer a Device runs without rebooting, the more security updates accumulate in a pending state. Machines that have been on for weeks also tend to show degraded performance and memory pressure that a simple restart would resolve.

The problem with forcing a reboot with an abrupt shutdown is that it causes interruptions and frustration. This Script takes a more gradual approach: it monitors uptime and escalates progressively, starting with a discreet notification and only forcing a countdown when the Device has been running for 13 or more days. Users get enough warning to save their work; IT gets the assurance that reboots actually happen.

Alerts are displayed using swiftDialog (installed automatically if not present) with your company's branding, so users recognize them as legitimate IT communications.

Warning

The Applivery Agent for macOS must be installed and active on the Device. Learn more about the macOS Agent.

Requirements

Requirement Detail
Platform macOS 11.0 (Big Sur) or later
Execution privileges Root (default in Applivery)
Corporate branding /var/root/CompanyAssets/logo.png (optional, for branded dialogs)
swiftDialog Installed automatically if not already present

Escalation levels

The Script calculates the current system uptime in days and applies a four-level escalation policy:

Uptime Action
0–4 days No action
5–8 days macOS toast notification with an audible alert
9–12 days Dialog window with Restart now and Postpone (24h) options. Closes automatically after 14 minutes
13+ days Full-screen warning followed by a 10-minute countdown. The Mac restarts when the timer reaches zero, or the user clicks Restart now

Setup

1
Deploy your company logo (optional)

Before deploying this Script, make sure your company logo is available at /var/root/CompanyAssets/logo.png on each managed Device. The Script resizes and copies the logo to the swiftDialog resources folder automatically. Using the corporate logo ensures users recognize the alerts as IT communications and don't dismiss them as noise. You can distribute the file using Applivery File Management.

2
Create the Script

Once in theΒ Applivery Dashboard, follow the steps described here to create a Script. Paste the following Script into the editor, select Bash as the language, give it a descriptive name (e.g., Force Reboot Policy), and click Create.

#!/bin/bash

# ---
# Title: Force Reboot Policy (Uptime Enforcement)
# Description: Monitors macOS uptime and triggers escalating alerts via swiftDialog to force a restart.
# Author: Applivery
# Version: 1.2.0
# ---

# ==========================================
# 1. CLEANUP OLD INSTALLATIONS
# ==========================================
DIALOG_OLD="/Applications/Dialog.app"

if [ -d "$DIALOG_OLD" ]; then
  echo "β†’ Old Dialog.app found"
  pgrep -if "Dialog.app" && {
    echo "  β†’ Quitting Dialog..."
    pkill -if "Dialog.app" 2>/dev/null
    sleep 1
  }
  if sudo rm -rf "$DIALOG_OLD" 2>/dev/null; then
    echo "  β†’ Removed successfully"
  else
    echo "  β†’ Failed to remove legacy app."
  fi
else
  echo "β†’ No old Dialog.app present"
fi

# ==========================================
# 2. PRE-FLIGHT & BRANDING SETUP
# ==========================================
PATH=/usr/bin:/bin:/usr/sbin:/sbin

DIALOG_CLI="/usr/local/bin/dialog"
DIALOG_APP="/Library/Application Support/Dialog/Dialog.app"
DIALOG_ICON_DIR="/Library/Application Support/Dialog"
DIALOG_ICON="$DIALOG_ICON_DIR/Dialog.png"
BRAND_ICON="/var/root/CompanyAssets/logo.png"

needs_install=0
needs_reinstall=0

CURRENT_USER=$(stat -f %Su /dev/console)
USER_ID=$(id -u "$CURRENT_USER" 2>/dev/null || true)

get_swiftdialog_pkg_url() {
  local url
  url="$(/usr/bin/curl -fsSL -H "Accept: application/vnd.github+json" -H "User-Agent: Force_Reboot" \
    "https://api.github.com/repos/swiftDialog/swiftDialog/releases/latest" | \
    /usr/bin/sed -nE 's/.*"browser_download_url":"([^"]*\.pkg)".*/\1/p' | \
    /usr/bin/head -n 1)"
  [ -n "$url" ] && echo "$url" && return 0
  return 1
}

run_as_user() {
  if [ -z "$CURRENT_USER" ] || [ "$CURRENT_USER" = "loginwindow" ] || [ -z "$USER_ID" ]; then
    return 1
  fi
  launchctl asuser "$USER_ID" /usr/bin/sudo -u "$CURRENT_USER" -- "$@"
}

if [ ! -x "$DIALOG_CLI" ] || [ ! -d "$DIALOG_APP" ]; then
  needs_install=1
fi

mkdir -p "$DIALOG_ICON_DIR"
chmod 755 "$DIALOG_ICON_DIR"

if [ -f "$BRAND_ICON" ]; then
  tmp_brand="$(/usr/bin/mktemp /tmp/dialog_brand.XXXXXX.png)"
  if ! sips -z 512 512 "$BRAND_ICON" --out "$tmp_brand" >/dev/null 2>&1; then
    /bin/cp "$BRAND_ICON" "$tmp_brand"
  fi
  if [ -f "$tmp_brand" ]; then
    if [ ! -f "$DIALOG_ICON" ] || ! cmp -s "$tmp_brand" "$DIALOG_ICON"; then
      cp "$tmp_brand" "$DIALOG_ICON"
      chmod 644 "$DIALOG_ICON"
      chown root:wheel "$DIALOG_ICON" >/dev/null 2>&1
      needs_reinstall=1
    fi
  fi
  rm -f "$tmp_brand"
fi

# ==========================================
# 3. SWIFTDIALOG INSTALLATION
# ==========================================
if [ "$needs_install" -eq 1 ] || [ "$needs_reinstall" -eq 1 ]; then
  pkg_url="$(get_swiftdialog_pkg_url 2>/dev/null || true)"
  if [ -n "$pkg_url" ]; then
    pkg_path="/tmp/swiftDialog_$(date +%s).pkg"
    /usr/bin/curl -fL --retry 3 --retry-delay 1 "$pkg_url" -o "$pkg_path"
    installer -pkg "$pkg_path" -target /
    rm -f "$pkg_path"
  else
    echo "ERROR: Could not retrieve swiftDialog URL." >&2
    exit 1
  fi
fi

killall Dialog 2>/dev/null

# ==========================================
# 4. UPTIME CALCULATION
# ==========================================
current_unix_time="$(date '+%s')"
boot_time_unix="$(sysctl -n kern.boottime | awk -F 'sec = |, usec' '{ print $2; exit }')"
uptime_seconds="$(( current_unix_time - boot_time_unix ))"
uptime_days="$(( uptime_seconds / 86400 ))"

# TEST_UPTIME_DAYS="7" # Uncomment for testing

if [ -n "$TEST_UPTIME_DAYS" ]; then
  uptime_days="$TEST_UPTIME_DAYS"
fi

# ==========================================
# 5. ESCALATION LOGIC
# ==========================================

if [ "$uptime_days" -le 4 ]; then
  echo "Uptime: $uptime_days days. No action needed."
  exit 0

elif [ "$uptime_days" -ge 5 ] && [ "$uptime_days" -le 8 ]; then
  echo "Uptime: $uptime_days days. Showing Notification."
  run_as_user "$DIALOG_CLI" --notification \
    --title "$uptime_days days without a reboot!" \
    --message "Your Mac needs to restart to regain performance and apply security updates."
  afplay "/System/Library/Sounds/Sosumi.aiff"
  exit 0

elif [ "$uptime_days" -ge 9 ] && [ "$uptime_days" -le 12 ]; then
  echo "Uptime: $uptime_days days. Showing Dialog with Postpone."
  afplay "/System/Library/Sounds/Sosumi.aiff" &
  run_as_user "$DIALOG_CLI" \
    --title "Restart Required" \
    --message "*${uptime_days} days without a reboot!* \n\nPlease save your work and restart. If postponed, you will be reminded in 24 hours." \
    --icon "$DIALOG_ICON" \
    --button1text "Restart now" \
    --button2text "Postpone" \
    --timer 840 --width 650 --height 280 --position bottomright --ontop
  dialog_results=$?

elif [ "$uptime_days" -ge 13 ]; then
  echo "Uptime: $uptime_days days. Final warning."
  afplay "/System/Library/Sounds/Sosumi.aiff" & sleep 0.2 && afplay "/System/Library/Sounds/Sosumi.aiff" &

  run_as_user "$DIALOG_CLI" \
    --title "Restart Required" \
    --message "*${uptime_days} days without a reboot!* \n\n*After pressing I Understand, you will have 10 minutes to save your work.*" \
    --icon "$DIALOG_ICON" --button1text "I Understand" --width 650 --height 230 --blurscreen --ontop

  run_as_user "$DIALOG_CLI" \
    --title none --message "Computer will restart when the timer reaches zero." \
    --button1text "Restart now" --timer 600 --width 320 --height 110 --position bottomright --icon none --ontop
  dialog_results=$?
fi

# ==========================================
# 6. REBOOT EXECUTION
# ==========================================
if [ "$dialog_results" = "0" ] || [ "$dialog_results" = "4" ]; then
  echo "Rebooting now..."
  shutdown -r now
  sleep 2
  reboot
elif [ "$dialog_results" = "2" ]; then
  echo "User postponed the restart."
fi

exit 0
3
Assign the Script to a Device

Now, navigate to any of your Devices, select the Scripts tab, click on the + Assign Script button, and select the one you just created.

Note

You can also assign Scripts to Policies. To do this, navigate to the Policies section, select the desired Policy, and click on the Scripts tab. The process will be the same as when assigning it directly to an individual Device.

4
Choose the execution method
Method Behaviour Recommended?
Once Runs one time per Device. ❌ Not suitable β€” this Script needs to run continuously to monitor uptime.
Loop Runs repeatedly at the configured interval (15m, 1h, 6h, 1d, 7d). βœ… Recommended β€” select the daily interval (1d) to check uptime every 24 hours and escalate alerts progressively.
On demand Only runs when manually triggered. ❌ Not suitable for automated uptime enforcement.

This Script does not require any arguments. System uptime is calculated automatically at runtime. Click Add to save the assignment.


What users will see

Level 2 β€” Notification (days 5–8): A non-intrusive macOS notification appears in the upper-right corner with an audible alert. The user can dismiss it and continue working.

Level 3 β€” Dialog with postpone option (days 9–12): A dialog window appears in the bottom-right corner. The user can click Restart now or Postpone. The dialog closes automatically after 14 minutes.

Level 4 β€” Forced countdown (day 13+): A full-screen blurred warning appears first. After the user acknowledges it, a 10-minute countdown begins in the bottom-right corner. When it reaches zero β€” or the user clicks Restart now β€” the Mac restarts.


Testing the Script

To test a specific escalation level without waiting for the real uptime, uncomment the TEST_UPTIME_DAYS line in the Script and set it to the desired number of days:

TEST_UPTIME_DAYS="7"  # Uncomment for testing

Remember to comment it out again before deploying to production.


Available on GitHub

This Script is part of the Applivery Public Script Repository. A Device without a reboot is a patch without effect β€” this progressive escalation Policy keeps your fleet up to date without disrupting anyone.

Key Takeaways

  • Scripts automate repetitive tasks on managed devices.
  • Applivery allows creating, uploading, and assigning scripts.
  • Multiple execution methods are available (Once, Loop, On-demand).
  • A Public Script Repository provides ready-to-use scripts.