diff --git a/src/client/cmdVM/vmImportDir.go b/src/client/cmdVM/vmImportDir.go
index 60421183ea4bed76ebf6f094301a7c0dfac49e7d..1507bae0912072f61106b6f2556c354ac08ba0e4 100644
--- a/src/client/cmdVM/vmImportDir.go
+++ b/src/client/cmdVM/vmImportDir.go
@@ -3,7 +3,6 @@ package cmdVM
 import (
 	u "nexus-client/utils"
 	cu "nexus-common/utils"
-	"nexus-libclient/utils"
 	libclient "nexus-libclient/vm"
 	"os"
 )
@@ -67,7 +66,7 @@ func (cmd *ImportDir) Run(args []string) int {
 	}
 	tmpTarGzFile += ".tar.gz"
 	defer os.Remove(tmpTarGzFile)
-	if err := utils.TarGzDir(localDir, tmpTarGzFile); err != nil {
+	if err := cu.TarGzDir(localDir, tmpTarGzFile); err != nil {
 		u.PrintlnErr(err)
 		return 1
 	}
diff --git a/src/common/utils/files.go b/src/common/utils/files.go
index 600f5fb4a8901b27bffd29dc2ab5c3f952e05603..e83cab568bb4762b10382d985d22a52d077180d6 100644
--- a/src/common/utils/files.go
+++ b/src/common/utils/files.go
@@ -1,9 +1,15 @@
 package utils
 
 import (
+	"archive/tar"
+	"bytes"
+	"compress/gzip"
 	"errors"
+	"io"
+	"io/fs"
 	"os"
 	"path/filepath"
+	"strings"
 
 	"github.com/google/uuid"
 )
@@ -38,3 +44,107 @@ func CreateRandomEmptyFile() (string, error) {
 
 	return filePath, nil
 }
+
+// Creates a tar.gz archive of dir and all its files and subdirectories.
+// Note: dir can also be a file.
+// Source code slightly modified from: https://gist.github.com/mimoo/25fc9716e0f1353791f5908f94d6e726
+func TarGzDir(dir, archive string) error {
+	_, err := os.Stat(dir)
+	if errors.Is(err, fs.ErrNotExist) {
+		return errors.New("Error: \"" + dir + "\" does not exist")
+	}
+
+	var buf bytes.Buffer
+
+	gzWriter := gzip.NewWriter(&buf)
+	tarWriter := tar.NewWriter(gzWriter)
+
+	// Walks through every file in the directory
+	filepath.Walk(dir, func(file string, fi os.FileInfo, e error) error {
+		header, err := tar.FileInfoHeader(fi, file)
+		if err != nil {
+			return err
+		}
+
+		fileRel := strings.TrimPrefix(file, dir)
+		fileRel = filepath.Join(filepath.Base(dir), fileRel)
+		header.Name = filepath.ToSlash(fileRel)
+
+		if err := tarWriter.WriteHeader(header); err != nil {
+			return err
+		}
+		// if not a dir, write file content
+		if !fi.IsDir() {
+			data, err := os.Open(file)
+			if err != nil {
+				return err
+			}
+			if _, err := io.Copy(tarWriter, data); err != nil {
+				return err
+			}
+		}
+		return nil
+	})
+
+	if err := tarWriter.Close(); err != nil {
+		return err
+	}
+	if err := gzWriter.Close(); err != nil {
+		return err
+	}
+
+	fileToWrite, err := os.OpenFile(archive, os.O_CREATE|os.O_RDWR, os.FileMode(0750))
+	if err != nil {
+		return err
+	}
+	if _, err := io.Copy(fileToWrite, &buf); err != nil {
+		return err
+	}
+
+	return nil
+}
+
+// Returns the list of subdirectories present in dir.
+func GetSubDirs(dir string) ([]string, error) {
+	subDirs := []string{}
+
+	currentDir, err := os.Open(dir)
+	if err != nil {
+		return nil, err
+	}
+
+	// Retrieves all files entries in the directory (0 = all files in the directory).
+	files, err := currentDir.Readdir(0)
+	if err != nil {
+		currentDir.Close()
+		return nil, err
+	}
+
+	currentDir.Close()
+
+	// Loop over file entries
+	for _, f := range files {
+		if f.IsDir() {
+			subDirs = append(subDirs, filepath.Join(dir, f.Name()))
+		}
+	}
+
+	return subDirs, nil
+}
+
+func CopyFiles(source, dest string) error {
+	src, err := os.Open(source)
+	if err != nil {
+		return err
+	}
+	defer src.Close()
+
+	dst, err := os.Create(dest)
+	if err != nil {
+		return err
+	}
+	defer dst.Close()
+	_, err = io.Copy(dst, src)
+
+	return err
+}
diff --git a/src/libclient/go.mod b/src/libclient/go.mod
index c70650134288db7317bc630162112b449fd69d52..ce6b5963d314da076922d0b489c46e8c0a0697e4 100644
--- a/src/libclient/go.mod
+++ b/src/libclient/go.mod
@@ -6,9 +6,19 @@ replace nexus-common => ../common
 
 require nexus-common v0.0.0-00010101000000-000000000000
 
-require golang.org/x/net v0.27.0 // indirect
+require (
+	github.com/gabriel-vasile/mimetype v1.4.3 // indirect
+	github.com/go-playground/locales v0.14.1 // indirect
+	github.com/go-playground/universal-translator v0.18.1 // indirect
+	github.com/leodido/go-urn v1.4.0 // indirect
+	golang.org/x/crypto v0.25.0 // indirect
+	golang.org/x/net v0.27.0 // indirect
+	golang.org/x/sys v0.22.0 // indirect
+	golang.org/x/text v0.16.0 // indirect
+)
 
 require (
+	github.com/go-playground/validator/v10 v10.23.0
 	github.com/go-resty/resty/v2 v2.16.2
-	github.com/google/uuid v1.6.0 // indirect
+	github.com/google/uuid v1.6.0
 )
diff --git a/src/libclient/go.sum b/src/libclient/go.sum
index 5060da736650c97cec6eb978d30c4256168b46a3..58ff6fbbafcb9337813e3153a45c349b39ff4be2 100644
--- a/src/libclient/go.sum
+++ b/src/libclient/go.sum
@@ -1,6 +1,22 @@
+github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
+github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
+github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
+github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
+github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
+github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
+github.com/go-playground/validator/v10 v10.23.0 h1:/PwmTwZhS0dPkav3cdK9kV1FsAmrL8sThn8IHr/sO+o=
+github.com/go-playground/validator/v10 v10.23.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
 github.com/go-resty/resty/v2 v2.16.2 h1:CpRqTjIzq/rweXUt9+GxzzQdlkqMdt8Lm/fuK/CAbAg=
 github.com/go-resty/resty/v2 v2.16.2/go.mod h1:0fHAoK7JoBy/Ch36N8VFeMsK7xQOHhvWaC3iOktwmIU=
 github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
 github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
+github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
+golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30=
+golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M=
 golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys=
 golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE=
+golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI=
+golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
+golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
diff --git a/src/libclient/utils/files.go b/src/libclient/utils/files.go
deleted file mode 100644
index c8dcf2d4c0164d984efb4a7873485b0eb61fa4be..0000000000000000000000000000000000000000
--- a/src/libclient/utils/files.go
+++ /dev/null
@@ -1,72 +0,0 @@
-package utils
-
-import (
-	"archive/tar"
-	"bytes"
-	"compress/gzip"
-	"errors"
-	"io"
-	"io/fs"
-	"os"
-	"path/filepath"
-	"strings"
-)
-
-// Creates a tar.gz archive of dir and all its files and subdirectories.
-// Note: dir can also be a file.
-// Source code slightly modified from: https://gist.github.com/mimoo/25fc9716e0f1353791f5908f94d6e726
-func TarGzDir(dir, archive string) error {
-	_, err := os.Stat(dir)
-	if errors.Is(err, fs.ErrNotExist) {
-		return errors.New("Error: \"" + dir + "\" does not exist")
-	}
-
-	var buf bytes.Buffer
-
-	gzWriter := gzip.NewWriter(&buf)
-	tarWriter := tar.NewWriter(gzWriter)
-
-	// Walks through every file in the directory
-	filepath.Walk(dir, func(file string, fi os.FileInfo, err error) error {
-		header, err := tar.FileInfoHeader(fi, file)
-		if err != nil {
-			return err
-		}
-
-		fileRel := strings.TrimPrefix(file, dir)
-		fileRel = filepath.Join(filepath.Base(dir), fileRel)
-		header.Name = filepath.ToSlash(fileRel)
-
-		if err := tarWriter.WriteHeader(header); err != nil {
-			return err
-		}
-		// if not a dir, write file content
-		if !fi.IsDir() {
-			data, err := os.Open(file)
-			if err != nil {
-				return err
-			}
-			if _, err := io.Copy(tarWriter, data); err != nil {
-				return err
-			}
-		}
-		return nil
-	})
-
-	if err := tarWriter.Close(); err != nil {
-		return err
-	}
-	if err := gzWriter.Close(); err != nil {
-		return err
-	}
-
-	fileToWrite, err := os.OpenFile(archive, os.O_CREATE|os.O_RDWR, os.FileMode(0750))
-	if err != nil {
-		return err
-	}
-	if _, err := io.Copy(fileToWrite, &buf); err != nil {
-		return err
-	}
-
-	return nil
-}
diff --git a/src/server/utils/files.go b/src/server/utils/files.go
deleted file mode 100644
index 67351766868bc956970d6ff447e3ecdcefe1615a..0000000000000000000000000000000000000000
--- a/src/server/utils/files.go
+++ /dev/null
@@ -1,52 +0,0 @@
-package utils
-
-import (
-	"io"
-	"os"
-	"path/filepath"
-)
-
-// Returns the list of subdirectories present in dir.
-func GetSubDirs(dir string) ([]string, error) {
-	subDirs := []string{}
-
-	currentDir, err := os.Open(dir)
-	if err != nil {
-		return nil, err
-	}
-
-	// Retrieves all files entries in the directory (0 = all files in the directory).
-	files, err := currentDir.Readdir(0)
-	if err != nil {
-		currentDir.Close()
-		return nil, err
-	}
-
-	currentDir.Close()
-
-	// Loop over file entries
-	for _, f := range files {
-		if f.IsDir() {
-			subDirs = append(subDirs, filepath.Join(dir, f.Name()))
-		}
-	}
-
-	return subDirs, nil
-}
-
-func CopyFiles(source, dest string) error {
-	src, err := os.Open(source)
-	if err != nil {
-		return err
-	}
-	defer src.Close()
-
-	dst, err := os.Create(dest)
-	if err != nil {
-		return err
-	}
-	defer dst.Close()
-	_, err = io.Copy(dst, src)
-
-	return err
-}
diff --git a/src/server/utils/random.go b/src/server/utils/random.go
index 85942a09844daeace7a5019312dd742533547e99..5c8d3b068ef89e037250e19e505d27544ec69cbd 100644
--- a/src/server/utils/random.go
+++ b/src/server/utils/random.go
@@ -5,12 +5,15 @@ import (
 	"time"
 )
 
+var r *rand.Rand
+
 // Initializes the random number generator.
 func RandInit() {
-	rand.Seed(time.Now().UnixNano())
+	randSrc := rand.NewSource(time.Now().UnixNano())
+	r = rand.New(randSrc)
 }
 
 // Returns an int in the range [min,max] (both inclusive).
 func Rand(min, max int) int {
-	return rand.Intn(max-min+1) + min
+	return r.Intn(max-min+1) + min
 }
diff --git a/src/server/vms/template.go b/src/server/vms/template.go
index 234dc6cf36a557b540a39d0a575a32813e7c4ae5..ae45fad6d6713da6db6eaeda55b07da64409774e 100644
--- a/src/server/vms/template.go
+++ b/src/server/vms/template.go
@@ -4,8 +4,8 @@ import (
 	"encoding/json"
 	"errors"
 	"nexus-common/template"
+	"nexus-common/utils"
 	"nexus-server/exec"
-	"nexus-server/utils"
 	"os"
 	"path/filepath"
 	"time"
diff --git a/src/server/vms/templates.go b/src/server/vms/templates.go
index 8227293d63d9ba13f94aac4d74001a4193dbf846..d4e22f868269acd53c961db3da94b1f7210fe1fa 100644
--- a/src/server/vms/templates.go
+++ b/src/server/vms/templates.go
@@ -3,7 +3,7 @@ package vms
 import (
 	"errors"
 	t "nexus-common/template"
-	"nexus-server/utils"
+	"nexus-common/utils"
 	"os"
 	"path"
 	"path/filepath"
diff --git a/src/server/vms/vms.go b/src/server/vms/vms.go
index edb1a7060c2b3f594aafa0584de36dac7ad14b1f..bb379669877d71097950d06f2aa828ba8cca11ec 100644
--- a/src/server/vms/vms.go
+++ b/src/server/vms/vms.go
@@ -6,6 +6,7 @@ import (
 	"math"
 	"nexus-common/caps"
 	"nexus-common/params"
+	cu "nexus-common/utils"
 	vmc "nexus-common/vm"
 	"nexus-server/config"
 	"nexus-server/exec"
@@ -62,19 +63,19 @@ func InitVMs() error {
 	vms.usedPorts[conf.Core.APIDefaultPort] = true
 
 	errMsg := "Failed reading VMs directory: "
-	dirs1, err := utils.GetSubDirs(vmsDir)
+	dirs1, err := cu.GetSubDirs(vmsDir)
 	if err != nil {
 		return errors.New(errMsg + err.Error())
 	}
 
 	for d1 := range dirs1 {
-		dirs2, err := utils.GetSubDirs(dirs1[d1])
+		dirs2, err := cu.GetSubDirs(dirs1[d1])
 		if err != nil {
 			return errors.New(errMsg + err.Error())
 		}
 
 		for d2 := range dirs2 {
-			dirs3, err := utils.GetSubDirs(dirs2[d2])
+			dirs3, err := cu.GetSubDirs(dirs2[d2])
 			if err != nil {
 				return errors.New(errMsg + err.Error())
 			}