diff --git a/main.go b/main.go
index 95f8ff46fa37000ec90a95e3feca88893062f1ba..0e067caa95386c24776c0d44d44b4203bbf24c6f 100644
--- a/main.go
+++ b/main.go
@@ -30,6 +30,12 @@ const (
 var printer *message.Printer
 
 func main() {
+	err := acquirePID()
+	if err != nil {
+		log.Fatal(err)
+	}
+	defer releasePID()
+
 	conf := parseConfig()
 	initPrinter()
 
@@ -37,22 +43,28 @@ func main() {
 
 	b, err := bitmask.Init()
 	if err != nil {
-		log.Fatal(err)
+		log.Print(err)
+		return
 	}
 	defer b.Close()
 
-	checkAndInstallHelpers(b, notify)
+	err = checkAndInstallHelpers(b, notify)
+	if err != nil {
+		log.Printf("Is bitmask running? %v", err)
+		return
+	}
+
 	run(b, conf)
 }
 
-func checkAndInstallHelpers(b *bitmask.Bitmask, notify *notificator) {
+func checkAndInstallHelpers(b *bitmask.Bitmask, notify *notificator) error {
 	helpers, priviledge, err := b.VPNCheck()
 	if (err != nil && err.Error() == "nopolkit") || (err == nil && !priviledge) {
 		log.Printf("No polkit found")
 		notify.authAgent()
 	} else if err != nil {
 		notify.bitmaskNotRunning()
-		log.Fatal("Is bitmask running? ", err)
+		return err
 	}
 
 	if !helpers {
@@ -61,6 +73,7 @@ func checkAndInstallHelpers(b *bitmask.Bitmask, notify *notificator) {
 			log.Println("Error installing helpers: ", err)
 		}
 	}
+	return nil
 }
 
 func initPrinter() {
diff --git a/pid.go b/pid.go
new file mode 100644
index 0000000000000000000000000000000000000000..0636110159f8a8a35101ed5be393fef9d74fc241
--- /dev/null
+++ b/pid.go
@@ -0,0 +1,87 @@
+package main
+
+import (
+	"fmt"
+	"io/ioutil"
+	"os"
+	"path/filepath"
+	"strconv"
+	"syscall"
+
+	"0xacab.org/leap/bitmask-systray/bitmask"
+)
+
+var pidFile = filepath.Join(bitmask.ConfigPath, "systray.pid")
+
+func acquirePID() error {
+	pid := syscall.Getpid()
+	current, err := getPID()
+	if err != nil {
+		return err
+	}
+
+	if current != 0 && current != pid {
+		proc, err := os.FindProcess(current)
+		if err != nil {
+			return err
+		}
+		err = proc.Signal(syscall.Signal(0))
+		if err == nil {
+			return fmt.Errorf("Another systray is running with pid: %d", current)
+		}
+	}
+
+	return setPID(pid)
+}
+
+func releasePID() error {
+	pid := syscall.Getpid()
+	current, err := getPID()
+	if err != nil {
+		return err
+	}
+	if current != 0 && current != pid {
+		return fmt.Errorf("Can't release pid file, is not own by this process")
+	}
+
+	if current == pid {
+		return os.Remove(pidFile)
+	}
+	return nil
+}
+
+func getPID() (int, error) {
+	_, err := os.Stat(pidFile)
+	if os.IsNotExist(err) {
+		return 0, nil
+	}
+	if err != nil {
+		return 0, err
+	}
+
+	file, err := os.Open(pidFile)
+	if err != nil {
+		return 0, err
+	}
+	defer file.Close()
+
+	b, err := ioutil.ReadAll(file)
+	if err != nil {
+		return 0, err
+	}
+	if len(b) == 0 {
+		return 0, nil
+	}
+	return strconv.Atoi(string(b))
+}
+
+func setPID(pid int) error {
+	file, err := os.Create(pidFile)
+	if err != nil {
+		return err
+	}
+	defer file.Close()
+
+	_, err = file.WriteString(fmt.Sprintf("%d", pid))
+	return err
+}
diff --git a/systray.go b/systray.go
index e76763a68079d087ff007538d99522c9c8257f51..d72562c0391b4bfd5d285191659c29763ab2e498 100644
--- a/systray.go
+++ b/systray.go
@@ -116,7 +116,10 @@ func (bt *bmTray) onReady() {
 
 			case <-mQuit.ClickedCh:
 				systray.Quit()
+				// XXX: this a hack as quit doesn't work
+				//      this should be done by defer in the main function
 				bt.bm.Close()
+				releasePID()
 				log.Println("Quit now...")
 				os.Exit(0)