diff --git a/Makefile b/Makefile
index 7e1c5da..4d79416 100644
--- a/Makefile
+++ b/Makefile
@@ -15,18 +15,19 @@ help:
 install:
 	cd _build && \
 	install -d $(BIN_INSTALL_DIR)  && \
-	install -m 755 hestiacp-php-selector $(BIN_INSTALL_DIR) 
+	install -m 755 hestiacp-php-selector $(BIN_INSTALL_DIR) && \
+	install -m 755 hestiacp-php-admin $(BIN_INSTALL_DIR)
 
 targz:
 	mkdir $(PKG_NAME)-$(VERSION) && \
 	git rev-parse --abbrev-ref HEAD > $(PKG_NAME)-$(VERSION)/version
-	cp -r LICENSE src Makefile *.spec go.mod.prepare.sh go.* $(PKG_NAME)-$(VERSION)/ && \
+	cp -r LICENSE src Makefile *.spec go.mod.prepare.sh go.* installer.sh $(PKG_NAME)-$(VERSION)/ && \
 	tar zcvf $(PKG_NAME)-$(VERSION).tar.gz $(PKG_NAME)-$(VERSION) && \
 	rm -rf $(PKG_NAME)-$(VERSION)
 
 targzvendor:
 	mkdir $(PKG_NAME)-$(VERSION) && \
-	cp -r LICENSE src Makefile *.spec go.mod.prepare.sh go.* $(PKG_NAME)-$(VERSION)/ && \
+	cp -r LICENSE src Makefile *.spec go.mod.prepare.sh go.* installer.sh $(PKG_NAME)-$(VERSION)/ && \
 	pushd $(PKG_NAME)-$(VERSION) && \
 	$(GOENV) go mod vendor && \
 	popd && \
@@ -44,7 +45,9 @@ build:
 	cd _src && \
 	{ [ -e vendor ] || $(GOENV) go mod tidy; } && \
 	{ [ -e vendor ] || $(GOENV) go build -buildvcs=false $(GOLDFLAGS) -o ../_build/hestiacp-php-selector src/main/*.go; } && \
-	{ [ ! -e vendor ] || GO111MODULE=on GOPROXY=off go build -mod=vendor -buildvcs=false $(GOLDFLAGS) -o ../_build/hestiacp-php-selector src/main/*.go; }
+	{ [ ! -e vendor ] || GO111MODULE=on GOPROXY=off go build -mod=vendor -buildvcs=false $(GOLDFLAGS) -o ../_build/hestiacp-php-selector src/main/*.go; } && \
+	{ [ -e vendor ] || $(GOENV) go build -buildvcs=false $(GOLDFLAGS) -o ../_build/hestiacp-php-admin src/converter/*.go; } && \
+	{ [ ! -e vendor ] || GO111MODULE=on GOPROXY=off go build -mod=vendor -buildvcs=false $(GOLDFLAGS) -o ../_build/hestiacp-php-admin src/converter/*.go; }
 	
 check:
 	{ [ ! -e _src ] || rm -rf _src; } && \
@@ -54,7 +57,9 @@ check:
 	cd _src && \
 	{ [ -e vendor ] || $(GOENV) go mod tidy; } && \
 	{ [ -e vendor ] || $(GOENV) go test src/main/*.go; } && \
-	{ [ ! -e vendor ] || GO111MODULE=on GOPROXY=off go test -mod=vendor -buildvcs=false src/main/*.go; }
+	{ [ ! -e vendor ] || GO111MODULE=on GOPROXY=off go test -mod=vendor -buildvcs=false src/main/*.go; } && \
+	{ [ -e vendor ] || $(GOENV) go test src/converter/*.go; } && \
+	{ [ ! -e vendor ] || GO111MODULE=on GOPROXY=off go test -mod=vendor -buildvcs=false src/converter/*.go; }
 
 all: help
 
diff --git a/go.mod b/go.mod
index cee635c..adef91f 100644
--- a/go.mod
+++ b/go.mod
@@ -1,3 +1,10 @@
 module dev.brepo.ru/brepo/hestiacp-php-selector
 
 go 1.22.6
+
+require (
+	github.com/akamensky/argparse v1.4.0
+	github.com/gofrs/flock v0.12.1
+)
+
+require golang.org/x/sys v0.22.0 // indirect
diff --git a/go.sum b/go.sum
new file mode 100644
index 0000000..b07af4e
--- /dev/null
+++ b/go.sum
@@ -0,0 +1,6 @@
+github.com/akamensky/argparse v1.4.0 h1:YGzvsTqCvbEZhL8zZu2AiA5nq805NZh75JNj4ajn1xc=
+github.com/akamensky/argparse v1.4.0/go.mod h1:S5kwC7IuDcEr5VeXtGPRVZ5o/FdhcMlQz4IZQuw64xA=
+github.com/gofrs/flock v0.12.1 h1:MTLVXXHf8ekldpJk3AKicLij9MdwOWkZ+a/jHHZby9E=
+github.com/gofrs/flock v0.12.1/go.mod h1:9zxTsyu5xtJ9DK+1tFZyibEV7y3uwDxPPfbxeeHCoD0=
+golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI=
+golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
diff --git a/hestiacp-php-selector.spec b/hestiacp-php-selector.spec
index 5197a2b..463c36b 100644
--- a/hestiacp-php-selector.spec
+++ b/hestiacp-php-selector.spec
@@ -25,6 +25,10 @@ make build
 %install
 [ "$RPM_BUILD_ROOT" != "/" ] && rm -rf "$RPM_BUILD_ROOT"
 make DESTDIR=$RPM_BUILD_ROOT install
+%{__mkdir} -p "$RPM_BUILD_ROOT"%{_sysconfdir}/hestia_php_selector/system/
+touch "$RPM_BUILD_ROOT"%{_sysconfdir}/hestia_php_selector/system/lock
+%{__mkdir} -p "$RPM_BUILD_ROOT"/usr/local/hestia_php_selector/
+%{__install} -D -m 755 installer.sh "$RPM_BUILD_ROOT"/usr/local/hestia_php_selector/hestiacp_php_selector_installer
 
 %check
 make check
@@ -32,10 +36,24 @@ make check
 %clean
 [ "$RPM_BUILD_ROOT" != "/" ] && rm -rf "$RPM_BUILD_ROOT"
 
+%posttrans
+if [ $1 -eq 1 ]; then
+    /usr/local/hestia_php_selector/hestiacp_php_selector_installer install
+fi
+
+%postun
+if [ $1 -eq 0 ]; then
+    /usr/local/hestia_php_selector/hestiacp_php_selector_installer delete
+fi
+
+
 %files
 %defattr(-,root,root)
 %doc LICENSE
 %attr(0755, root, root) %{_bindir}/hestiacp-php-selector
+%attr(0755, root, root) %{_bindir}/hestiacp-php-admin
+%{_sysconfdir}/hestia_php_selector/*
+/usr/local/hestia_php_selector/*
 
 %changelog
 * Wed Aug 21 2024 Alexey Berezhok <a@bayrepo.ru> 0.1.0-1
diff --git a/installer.sh b/installer.sh
index bc2f211..6e42d5c 100644
--- a/installer.sh
+++ b/installer.sh
@@ -1,8 +1,34 @@
 #!/usr/bin/env bash
 
-echo "Try to find php-selector for hestiacp"
-update-alternatives --display php | grep hestiacp-php-selector
-if [ $? -ne 0 ]; then
-    echo "Register php-selector"
-    update-alternatives --install /usr/bin/php php /usr/bin/hestiacp-php-selector 1
-fi
\ No newline at end of file
+uid=$(id -u)
+if [ "$uid" != "0" ]; then
+    echo "Command must be executed as privileged user"
+    exit 0
+fi
+case "$1" in
+install)
+    echo "Try to find php-selector for hestiacp"
+    update-alternatives --display php | grep hestiacp-php-selector
+    if [ $? -ne 0 ]; then
+        echo "Register php-selector"
+        update-alternatives --install /usr/bin/php php /usr/bin/hestiacp-php-selector 1
+        if [ ! -e /etc/hestia_php_selector/system/php.path ]; then
+            mkdir -p /etc/hestia_php_selector/system/
+            current_path_to_php=$(readlink -f /usr/bin/php)
+            echo "$current_path_to_php" > /etc/hestia_php_selector/system/php.path
+            chmod 644 /etc/hestia_php_selector/system/php.path
+        fi
+        /usr/bin/hestiacp-php-admin add
+    fi
+;;
+delete)
+    php_sys=$(cat /etc/hestia_php_selector/system/php.path)
+    if [ -n "$php_sys" ]; then
+        update-alternatives --set php "$php_sys"
+    fi
+    /usr/bin/hestiacp-php-admin remove-all
+;;
+*)
+    echo "Unknown command"
+;;
+esac
\ No newline at end of file
diff --git a/src/converter/hestiacp-php-set-ver.go b/src/converter/hestiacp-php-set-ver.go
new file mode 100644
index 0000000..b12fdc9
--- /dev/null
+++ b/src/converter/hestiacp-php-set-ver.go
@@ -0,0 +1,318 @@
+package main
+
+import (
+	"bufio"
+	"errors"
+	"fmt"
+	"os"
+	"os/user"
+	"path"
+	"path/filepath"
+	"regexp"
+	"strconv"
+	"strings"
+
+	"github.com/akamensky/argparse"
+	flk "github.com/gofrs/flock"
+)
+
+const (
+	PATH_TO_CONFIG_NAME      = "/php.path"
+	PATH_TO_USER_CONFIG_ROOT = "/etc/hestia_php_selector/%s"
+	PATH_TO_USER_CONFIG      = "/usr/local/hestia/data/users/*/user.conf"
+	PATH_TO_USER_CONFIG_FMT  = "/usr/local/hestia/data/users/%s/user.conf"
+	PATH_TO_USER_CONFIG_FULL = PATH_TO_USER_CONFIG_ROOT + PATH_TO_CONFIG_NAME
+	PATH_TO_LOCAL_PHP_ROOT   = "php_sel"
+	PATH_TO_LOCAL_PHP        = PATH_TO_LOCAL_PHP_ROOT + PATH_TO_CONFIG_NAME
+	LOCK_PATH                = "lock"
+)
+
+func escapeUserName(username string) string {
+	username0 := strings.TrimSpace(username)
+	uname := strings.Split(username0, "/")
+	uname2 := uname[len(uname)-1]
+	if uname2 == "." || uname2 == ".." {
+		return ""
+	}
+	return uname2
+}
+
+func isExecOther(mode os.FileMode) bool {
+	return mode&0001 != 0
+}
+
+func getPHPVerFromConf(path string) (string, error) {
+	f, err := os.Open(path)
+	if err != nil {
+		return "", fmt.Errorf("user config reading error %s", err)
+	}
+	defer f.Close()
+	scanner := bufio.NewScanner(f)
+
+	for scanner.Scan() {
+		line := scanner.Text()
+		formatedLine := strings.SplitN(strings.TrimSpace(line), "=", 2)
+		if len(formatedLine) < 2 {
+			return "", fmt.Errorf("incorrect string formatting in config %s", line)
+		}
+		if strings.TrimSpace(formatedLine[0]) == "PHPCLI" {
+			result := strings.Trim(formatedLine[1], "' ")
+			return result, nil
+		}
+	}
+
+	if err := scanner.Err(); err != nil {
+		return "", fmt.Errorf("user config reading error %s", err)
+	}
+
+	return "", nil
+}
+
+func getUserPHPVerHestia(username string) (string, string, error) {
+	pathToConf := fmt.Sprintf(PATH_TO_USER_CONFIG_FMT, username)
+	phpVer, err := getPHPVerFromConf(pathToConf)
+	if err != nil {
+		return "", "", err
+	}
+	if phpVer == "" {
+		return "", "", nil
+	}
+	phpPath := "/usr/bin/php" + phpVer
+	if finfo, err := os.Stat(phpPath); err != nil {
+		return "", "", err
+	} else {
+		fmode := finfo.Mode()
+		if !isExecOther(fmode) {
+			return "", "", fmt.Errorf("not executable by others %s", phpPath)
+		}
+		if strings.Contains(finfo.Name(), "hestiacp-php-selector") {
+			return "", "", fmt.Errorf("infinite symbolic link")
+		}
+	}
+	return phpPath, phpVer, nil
+}
+
+func setUserPHPVer(username string, php_ver string) error {
+	reIsVer := regexp.MustCompile(`^(\d\.?\d+)`)
+	matches := reIsVer.FindStringSubmatch(php_ver)
+	if len(matches) > 1 {
+		phpver := strings.TrimSpace(matches[1])
+		phpPath := "/usr/bin/php" + phpver
+		if finfo, err := os.Stat(phpPath); err != nil {
+			return err
+		} else {
+			fmode := finfo.Mode()
+			if !isExecOther(fmode) {
+				return fmt.Errorf("not executable by others %s", phpPath)
+			}
+			if strings.Contains(finfo.Name(), "hestiacp-php-selector") {
+				return fmt.Errorf("infinite symbolic link")
+			}
+		}
+		return setUserPhpPathVer(username, phpver, phpPath)
+	} else {
+		reIsVer := regexp.MustCompile(`(\d\.?\d+)$`)
+		matches := reIsVer.FindStringSubmatch(php_ver)
+		vers := ""
+		if len(matches) > 1 {
+			vers = strings.TrimSpace(matches[1])
+		}
+		return setUserPhpPathVer(username, vers, php_ver)
+	}
+}
+
+func isFileExists(path string) (bool, bool, error) {
+	if fi, err := os.Stat(path); err == nil {
+		return true, fi.IsDir(), nil
+	} else if errors.Is(err, os.ErrNotExist) {
+		return false, false, nil
+	} else {
+		return false, false, err
+	}
+}
+
+func setUserPhpPathVer(username string, php_ver string, php_path string) error {
+	un := escapeUserName(username)
+	if un == "" {
+		return fmt.Errorf("incorrect username %s", username)
+	}
+	var root_path string
+	var uid int = -1
+	if un == "root" {
+		root_path = fmt.Sprintf(PATH_TO_USER_CONFIG_ROOT, "system")
+	} else {
+		user_found, err := user.Lookup(un)
+		if err != nil {
+			return err
+		}
+		root_path = path.Join(user_found.HomeDir, PATH_TO_LOCAL_PHP_ROOT)
+		Uid_conv, _ := strconv.ParseInt(user_found.Uid, 10, 64)
+		uid = int(Uid_conv)
+	}
+
+	exists, isdir, err := isFileExists(root_path)
+	if err != nil {
+		return err
+	}
+	if exists {
+		if !isdir {
+			err := os.RemoveAll(root_path)
+			if err != nil {
+				return err
+			}
+			err = os.Mkdir(root_path, 0755)
+			if err != nil {
+				return err
+			}
+		}
+	} else {
+		err := os.Mkdir(root_path, 0755)
+		if err != nil {
+			return err
+		}
+	}
+	path_to_php_config := path.Join(root_path, PATH_TO_CONFIG_NAME)
+	f, err := os.OpenFile(path_to_php_config, os.O_WRONLY, 0400)
+	if err != nil {
+		return err
+	}
+	f.WriteString(php_path)
+	f.Close()
+	return os.Chown(path_to_php_config, uid, -1)
+}
+
+func removeUserInfo(username string) error {
+	un := escapeUserName(username)
+	if un == "" {
+		return nil
+	}
+	if un == "root" {
+		return nil
+	}
+	user_found, err := user.Lookup(un)
+	if err != nil {
+		return err
+	}
+	root_path := path.Join(user_found.HomeDir, PATH_TO_LOCAL_PHP_ROOT)
+	return os.RemoveAll(root_path)
+}
+
+func inner_main() int {
+	parser := argparse.NewParser("php-selector", "Set version php cli for user")
+	vConvertExisting := parser.NewCommand("add", "Add existing users to php-selector")
+	vSetPhp := parser.NewCommand("set", "Set php for user: set username 74 or set username /usr/bin/php74")
+	vSetPhp_username := vSetPhp.StringPositional(&argparse.Options{Required: true, Help: "username"})
+	vSetPhp_ver := vSetPhp.StringPositional(&argparse.Options{Required: true, Help: "path to php or php version"})
+	vSetSystem := parser.NewCommand("system", "Set system php version")
+	vSetSystem_ver := vSetSystem.StringPositional(&argparse.Options{Required: true, Help: "path to php or php version"})
+	vRemoveUser := parser.NewCommand("remove", "remove php info for user (reset to system)")
+	vRemoveUser_username := vRemoveUser.StringPositional(&argparse.Options{Required: true, Help: "username to delete"})
+	vRemoveUserAll := parser.NewCommand("remove-all", "remove php info for all users")
+	err := parser.Parse(os.Args)
+	if err != nil {
+		fmt.Fprintln(os.Stderr, parser.Usage(err))
+		return 1
+	}
+
+	lock_file := path.Join(fmt.Sprintf(PATH_TO_USER_CONFIG_ROOT, "system"), LOCK_PATH)
+	lock := flk.New(lock_file)
+	err = lock.Lock()
+	if err != nil {
+		fmt.Fprintf(os.Stderr, "Error locking %s\n", err)
+		return 1
+	}
+	defer lock.Close()
+
+	if vConvertExisting.Happened() {
+		config_path, err := filepath.Glob(PATH_TO_USER_CONFIG)
+		if err != nil {
+			fmt.Fprintf(os.Stderr, "Error reading of hestia configs %s\n", err)
+			return 1
+		}
+		for _, v := range config_path {
+			reUserName := regexp.MustCompile(`^/usr/local/hestia/data/users/(.+)/user.conf`)
+			matches := reUserName.FindStringSubmatch(v)
+			if len(matches) > 1 {
+				uName := strings.TrimSpace(matches[1])
+				php_path, php_ver, err := getUserPHPVerHestia(uName)
+				if err != nil {
+					fmt.Fprintln(os.Stderr, err)
+					continue
+				}
+				err = setUserPhpPathVer(uName, php_ver, php_path)
+				if err != nil {
+					fmt.Fprintln(os.Stderr, err)
+					continue
+				}
+			}
+		}
+
+	} else if vSetPhp.Happened() {
+		username := strings.TrimSpace(*vSetPhp_username)
+		php_ver := strings.TrimSpace(*vSetPhp_ver)
+		if username == "" || php_ver == "" {
+			fmt.Fprintln(os.Stderr, "Username or php version shouldn't be empty")
+			return 1
+		}
+		err := setUserPHPVer(username, php_ver)
+		if err != nil {
+			fmt.Fprintln(os.Stderr, err)
+			return 1
+		}
+	} else if vSetSystem.Happened() {
+		php_ver := strings.TrimSpace(*vSetSystem_ver)
+		if php_ver == "" {
+			fmt.Fprintln(os.Stderr, "Username or php version shouldn't be empty")
+			return 1
+		}
+		err := setUserPHPVer("root", php_ver)
+		if err != nil {
+			fmt.Fprintln(os.Stderr, err)
+			return 1
+		}
+	} else if vRemoveUser.Happened() {
+		username := strings.TrimSpace(*vRemoveUser_username)
+		if username == "" {
+			fmt.Fprintln(os.Stderr, "Username shouldn't be empty")
+			return 1
+		}
+		err := removeUserInfo(username)
+		if err != nil {
+			fmt.Fprintln(os.Stderr, err)
+			return 1
+		}
+	} else if vRemoveUserAll.Happened() {
+		config_path, err := filepath.Glob(PATH_TO_USER_CONFIG)
+		if err != nil {
+			fmt.Fprintf(os.Stderr, "Error reading of hestia configs %s\n", err)
+			return 1
+		}
+		for _, v := range config_path {
+			reUserName := regexp.MustCompile(`^/usr/local/hestia/data/users/(.+)/user.conf`)
+			matches := reUserName.FindStringSubmatch(v)
+			if len(matches) > 1 {
+				uName := strings.TrimSpace(matches[1])
+				err := removeUserInfo(uName)
+				if err != nil {
+					fmt.Fprintln(os.Stderr, err)
+					return 1
+				}
+			}
+		}
+
+	}
+	return 0
+}
+
+func main() {
+	user, err := user.Current()
+	if err != nil {
+		fmt.Fprintf(os.Stderr, "User information error %s\n", err)
+		os.Exit(1)
+	}
+	if user.Username != "root" {
+		fmt.Fprintln(os.Stderr, "Program should start under privileged user")
+		os.Exit(1)
+	}
+	os.Exit(inner_main())
+}
diff --git a/src/main/phpselector.go b/src/main/phpselector.go
index 9ab8f67..2ad5c22 100644
--- a/src/main/phpselector.go
+++ b/src/main/phpselector.go
@@ -1,47 +1,40 @@
 package main
 
 import (
-	"bufio"
 	"fmt"
 	"os"
 	"os/user"
+	"path"
 	"strings"
 	"syscall"
 )
 
 const (
-	PATH_TO_USER_CONFIG = "/usr/local/hestia/data/users/%s/user.conf"
+	PATH_TO_USER_CONFIG_FULL = "/etc/hestia_php_selector/%s/php.path"
+	PATH_TO_LOCAL_PHP        = "php_sel/php.path"
 )
 
 func isExecOther(mode os.FileMode) bool {
 	return mode&0001 != 0
 }
 
-func getPHPVerFromConf(path string) (string, error) {
-	f, err := os.Open(path)
+func getPHPVerFromConf(path1 string) (string, error) {
+	_, err := os.Stat(path1)
 	if err != nil {
-		return "", fmt.Errorf("user config reading error %s", err)
-	}
-	defer f.Close()
-	scanner := bufio.NewScanner(f)
-
-	for scanner.Scan() {
-		line := scanner.Text()
-		formatedLine := strings.SplitN(strings.TrimSpace(line), "=", 2)
-		if len(formatedLine) < 2 {
-			return "", fmt.Errorf("incorrect string formatting in config %s", line)
-		}
-		if strings.TrimSpace(formatedLine[0]) == "PHPCLI" {
-			result := strings.Trim(formatedLine[1], "' ")
-			return result, nil
+		sys_path := fmt.Sprintf(PATH_TO_USER_CONFIG_FULL, "system")
+		if path1 == sys_path {
+			return "", fmt.Errorf("user config reading error %s", err)
+		} else {
+			return getPHPVerFromConf(sys_path)
 		}
 	}
-
-	if err := scanner.Err(); err != nil {
+	content, err := os.ReadFile(path1)
+	if err != nil {
 		return "", fmt.Errorf("user config reading error %s", err)
 	}
+	result := strings.Split(strings.Trim(string(content), "\n "), "\n")[0]
+	return result, nil
 
-	return "", nil
 }
 
 func getUserPHPVer() (string, error) {
@@ -49,24 +42,32 @@ func getUserPHPVer() (string, error) {
 	if err != nil {
 		return "", fmt.Errorf("user information error %s", err)
 	}
-	pathToConf := fmt.Sprintf(PATH_TO_USER_CONFIG, user.Username)
+	username := user.Username
+	if username == "root" {
+		username = "system"
+	}
+	var pathToConf string
+	if username == "system" {
+		pathToConf = fmt.Sprintf(PATH_TO_USER_CONFIG_FULL, username)
+	} else {
+		pathToConf = path.Join(user.HomeDir, PATH_TO_LOCAL_PHP)
+	}
 	phpVer, err := getPHPVerFromConf(pathToConf)
 	if err != nil {
 		return "", err
 	}
-	phpPath := "/usr/bin/php" + phpVer
-	if finfo, err := os.Stat(phpPath); err != nil {
+	if finfo, err := os.Stat(phpVer); err != nil {
 		return "", err
 	} else {
 		fmode := finfo.Mode()
 		if !isExecOther(fmode) {
-			return "", fmt.Errorf("not executable by others %s", phpPath)
+			return "", fmt.Errorf("not executable by others %s", phpVer)
 		}
 		if strings.Contains(finfo.Name(), "hestiacp-php-selector") {
 			return "", fmt.Errorf("infinite symbolic link")
 		}
 	}
-	return phpPath, nil
+	return phpVer, nil
 }
 
 func inner_main() int {