diff --git a/src-tauri/src/core/verge.rs b/src-tauri/src/core/verge.rs
index 3a1077a2781b266fc7c67888c4b1857e94a5dceb..eab6350ada13da51b826e047d03d83a7f0efc3a0 100644
--- a/src-tauri/src/core/verge.rs
+++ b/src-tauri/src/core/verge.rs
@@ -1,6 +1,6 @@
-use crate::utils::{config, dirs, startup, sysopt::SysProxyConfig};
+use crate::utils::{config, dirs, sysopt::SysProxyConfig};
+use auto_launch::{AutoLaunch, AutoLaunchBuilder};
 use serde::{Deserialize, Serialize};
-use std::path::PathBuf;
 use tauri::api::path::resource_dir;
 
 /// ### `verge.yaml` schema
@@ -14,7 +14,7 @@ pub struct VergeConfig {
   pub theme_blur: Option<bool>,
 
   /// can the app auto startup
-  pub enable_self_startup: Option<bool>,
+  pub enable_auto_launch: Option<bool>,
 
   /// set system proxy
   pub enable_system_proxy: Option<bool>,
@@ -49,7 +49,7 @@ pub struct Verge {
 
   pub cur_sysproxy: Option<SysProxyConfig>,
 
-  pub exe_path: Option<PathBuf>,
+  pub auto_launch: Option<AutoLaunch>,
 }
 
 impl Default for Verge {
@@ -64,7 +64,7 @@ impl Verge {
       config: VergeConfig::new(),
       old_sysproxy: None,
       cur_sysproxy: None,
-      exe_path: None,
+      auto_launch: None,
     }
   }
 
@@ -101,53 +101,61 @@ impl Verge {
     }
   }
 
-  /// set the exe_path
-  pub fn set_exe_path(&mut self, package_info: &tauri::PackageInfo) {
-    let exe = if cfg!(target_os = "windows") {
-      "clash-verge.exe"
-    } else {
-      "clash-verge"
-    };
-    let path = resource_dir(package_info).unwrap().join(exe);
-    self.exe_path = Some(path);
+  /// init the auto launch
+  pub fn init_launch(&mut self, package_info: &tauri::PackageInfo) {
+    let app_name = "clash-verge";
+    let app_path = get_app_path(app_name);
+    let app_path = resource_dir(package_info).unwrap().join(app_path);
+    let app_path = app_path.as_os_str().to_str().unwrap();
+
+    let auto = AutoLaunchBuilder::new()
+      .set_app_name(app_name)
+      .set_app_path(app_path)
+      .build();
+
+    self.auto_launch = Some(auto);
   }
 
   /// sync the startup when run the app
-  pub fn sync_startup(&self) -> Result<(), String> {
-    let enable = self.config.enable_self_startup.clone().unwrap_or(false);
-
+  pub fn sync_launch(&self) -> Result<(), String> {
+    let enable = self.config.enable_auto_launch.clone().unwrap_or(false);
     if !enable {
       return Ok(());
     }
-    if self.exe_path.is_none() {
-      return Err("should init the exe_path first".into());
+
+    if self.auto_launch.is_none() {
+      return Err("should init the auto launch first".into());
     }
 
-    let exe_path = self.exe_path.clone().unwrap();
-    match startup::get_startup(&exe_path) {
-      Ok(sys_enable) => {
-        if sys_enable || (!sys_enable && startup::set_startup(true, &exe_path).is_ok()) {
-          Ok(())
-        } else {
-          Err("failed to sync startup".into())
-        }
+    let auto_launch = self.auto_launch.clone().unwrap();
+
+    let is_enabled = auto_launch.is_enabled().unwrap_or(false);
+    if !is_enabled {
+      if let Err(_) = auto_launch.enable() {
+        return Err("failed to enable auto-launch".into());
       }
-      Err(_) => Err("failed to get system startup info".into()),
     }
+
+    Ok(())
   }
 
   /// update the startup
-  fn update_startup(&mut self, enable: bool) -> Result<(), String> {
-    let conf_enable = self.config.enable_self_startup.clone().unwrap_or(false);
+  fn update_launch(&mut self, enable: bool) -> Result<(), String> {
+    let conf_enable = self.config.enable_auto_launch.clone().unwrap_or(false);
 
     if enable == conf_enable {
       return Ok(());
     }
-    if self.exe_path.is_none() {
-      return Err("should init the exe_path first".into());
-    }
-    let exe_path = self.exe_path.clone().unwrap();
-    match startup::set_startup(enable, &exe_path) {
+
+    let auto_launch = self.auto_launch.clone().unwrap();
+
+    let result = if enable {
+      auto_launch.enable()
+    } else {
+      auto_launch.disable()
+    };
+
+    match result {
       Ok(_) => Ok(()),
       Err(_) => Err("failed to set system startup info".into()),
     }
@@ -166,10 +174,10 @@ impl Verge {
     }
 
     // should update system startup
-    if patch.enable_self_startup.is_some() {
-      let enable = patch.enable_self_startup.unwrap();
-      self.update_startup(enable)?;
-      self.config.enable_self_startup = Some(enable);
+    if patch.enable_auto_launch.is_some() {
+      let enable = patch.enable_auto_launch.unwrap();
+      self.update_launch(enable)?;
+      self.config.enable_auto_launch = Some(enable);
     }
 
     // should update system proxy
@@ -187,7 +195,7 @@ impl Verge {
     }
 
     // todo
-    // should update system proxt too
+    // should update system proxy too
     if patch.system_proxy_bypass.is_some() {
       self.config.system_proxy_bypass = patch.system_proxy_bypass;
     }
@@ -195,3 +203,14 @@ impl Verge {
     self.config.save_file()
   }
 }
+
+// Get the target app_path
+fn get_app_path(app_name: &str) -> String {
+  #[cfg(target_os = "linux")]
+  let ext = "";
+  #[cfg(target_os = "macos")]
+  let ext = ".app";
+  #[cfg(target_os = "windows")]
+  let ext = ".exe";
+  String::from(app_name) + ext
+}
diff --git a/src-tauri/src/utils/mod.rs b/src-tauri/src/utils/mod.rs
index 2ba9a57147609560414b4e061423ae198fff168f..a5fe22b512db49ab13f48191046650c884d7742e 100644
--- a/src-tauri/src/utils/mod.rs
+++ b/src-tauri/src/utils/mod.rs
@@ -4,5 +4,4 @@ pub mod fetch;
 pub mod init;
 pub mod resolve;
 pub mod server;
-pub mod startup;
 pub mod sysopt;
diff --git a/src-tauri/src/utils/resolve.rs b/src-tauri/src/utils/resolve.rs
index 281fecbb62a47877c19393fb3942d90d6ae48ac7..96cb9c45697a4ed656b2ab4dbade75e664b6f83b 100644
--- a/src-tauri/src/utils/resolve.rs
+++ b/src-tauri/src/utils/resolve.rs
@@ -9,6 +9,7 @@ pub fn resolve_setup(app: &App) {
   let window = app.get_window("main").unwrap();
   window.set_shadow(true);
 
+  // enable system blur
   use tauri_plugin_vibrancy::Vibrancy;
   #[cfg(target_os = "windows")]
   window.apply_blur();
@@ -42,9 +43,9 @@ pub fn resolve_setup(app: &App) {
     log::error!("{}", err);
   }
 
-  verge.set_exe_path(app.package_info());
   verge.init_sysproxy(clash.info.port.clone());
-  if let Err(err) = verge.sync_startup() {
+  verge.init_launch(app.package_info());
+  if let Err(err) = verge.sync_launch() {
     log::error!("{}", err);
   }
 }
diff --git a/src-tauri/src/utils/startup.rs b/src-tauri/src/utils/startup.rs
deleted file mode 100644
index 224e795eed1746ec65d8f92eb0287fb683c610b0..0000000000000000000000000000000000000000
--- a/src-tauri/src/utils/startup.rs
+++ /dev/null
@@ -1,74 +0,0 @@
-use std::io;
-use std::path::PathBuf;
-
-static APP_KEY: &str = "ClashVerge";
-
-/// get the startup value
-/// whether as same as the exe_path
-#[cfg(target_os = "windows")]
-pub fn get_startup(exe_path: &PathBuf) -> io::Result<bool> {
-  use winreg::enums::*;
-  use winreg::RegKey;
-
-  let hkcu = RegKey::predef(HKEY_CURRENT_USER);
-  let cur_var = hkcu.open_subkey_with_flags(
-    "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run",
-    KEY_READ,
-  )?;
-
-  match cur_var.get_value::<String, _>(APP_KEY) {
-    Ok(path) => {
-      let exe_path = exe_path.clone();
-      let exe_path = exe_path.as_os_str().to_str().unwrap();
-      Ok(path == exe_path)
-    }
-    Err(_) => Ok(false),
-  }
-}
-
-/// set the startup on windows
-/// delete the reg key if disabled
-#[cfg(target_os = "windows")]
-pub fn set_startup(enable: bool, exe_path: &PathBuf) -> io::Result<()> {
-  use winreg::enums::*;
-  use winreg::RegKey;
-
-  let hkcu = RegKey::predef(HKEY_CURRENT_USER);
-  let cur_var = hkcu.open_subkey_with_flags(
-    "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run",
-    KEY_SET_VALUE,
-  )?;
-
-  match enable {
-    true => {
-      let exe_path = exe_path.clone();
-      let exe_path = exe_path.as_os_str().to_str().unwrap();
-      cur_var.set_value::<&str, _>(APP_KEY, &exe_path)
-    }
-    false => cur_var.delete_value(APP_KEY),
-  }
-}
-
-/// todo
-#[cfg(target_os = "macos")]
-pub fn get_startup(exe_path: &PathBuf) -> io::Result<bool> {
-  Ok(true)
-}
-
-/// todo
-#[cfg(target_os = "macos")]
-pub fn set_startup(enable: bool, exe_path: &PathBuf) -> io::Result<()> {
-  Ok(())
-}
-
-#[cfg(target_os = "windows")]
-#[test]
-fn test() {
-  let path = PathBuf::from(r"D:\Software\Clash Verge\clash-verge.exe");
-
-  assert!(set_startup(true, &path).is_ok());
-  assert_eq!(get_startup(&path).unwrap(), true);
-
-  assert!(set_startup(false, &path).is_ok());
-  assert_eq!(get_startup(&path).unwrap(), false);
-}