diff --git a/docs/README_server.md b/docs/README_server.md
index 807b8100c9711e620c7b1d7f540b584b26e3eb01..b36219f3162716da2cfe1442ed2537359064bb00 100644
--- a/docs/README_server.md
+++ b/docs/README_server.md
@@ -366,6 +366,8 @@ These capabilities are called "VM access capabilities":
 | `/vms/{id}`                | delete a VM                  | DELETE |                                | `VM_DESTROY_ANY` | OR  | `VM_DESTROY`        |
 | `/vms/{id}`                | edit a VM                    | PUT    | name,cpus,ram,nic,usb          | `VM_EDIT_ANY`    | OR  | `VM_EDIT`           |
 | `/vms/{id}/start`          | start a VM                   | PUT    |                                | `VM_START_ANY`   | OR  | `VM_START`          |
+| `/vms/{id}/start`          | start a VM                   | PUT    |                                | `VM_START_ANY`   | OR  | `VM_START`          |
+| `/vms/{id}/startwithcreds` | start a VM with credentials  | PUT    | port,pwd                       | `VM_START_ANY`   | OR  | `VM_START`          |
 | `/vms/{id}/stop`           | kill a VM                    | PUT    |                                | `VM_STOP_ANY`    | OR  | `VM_STOP`           |
 | `/vms/{id}/reboot`         | reboot a VM                  | PUT    |                                | `VM_REBOOT_ANY`  | OR  | `VM_REBOOT`         |
 | `/vms/{id}/shutdown`       | gracefully shutdown a VM     | PUT    |                                | `VM_STOP_ANY`    | OR  | `VM_STOP`           |
diff --git a/src/client/nexus-cli/run_dev b/src/client/nexus-cli/run_dev
index eb756406f8ff6f98f40052902999e254a412c6c6..1ae9f45b43ce8ee5eef21517a9b13e6720417fda 100755
--- a/src/client/nexus-cli/run_dev
+++ b/src/client/nexus-cli/run_dev
@@ -1,4 +1,4 @@
 #!/bin/bash
 
-make prepare_dev
-go run . $@
+make prepare_dev || exit 1
+go run . "$@"
diff --git a/src/client/nexus-cli/run_prod b/src/client/nexus-cli/run_prod
index fffa9d9b7a5a8bbc2734289ba79b0904011292fe..a055bc20b30a5e96ba6aedc5819c440ceb4dda22 100755
--- a/src/client/nexus-cli/run_prod
+++ b/src/client/nexus-cli/run_prod
@@ -1,4 +1,4 @@
 #!/bin/bash
 
-make prepare_prod
-go run . $@
\ No newline at end of file
+make prepare_prod || exit 1
+go run . "$@"
\ No newline at end of file
diff --git a/src/client/nexus-cli/validate b/src/client/nexus-cli/validate
index 03a957ec93512f72d7652cd8f13188d04c2a6762..aefcf26ab774d46667125793a65910b41e1ebd48 100755
--- a/src/client/nexus-cli/validate
+++ b/src/client/nexus-cli/validate
@@ -1,7 +1,7 @@
 #!/bin/bash
 
+make prepare_dev
 go build .
-
 nexus_cli="./nexus-cli"
 
 partial_name="exam 328a2d0eff08"
diff --git a/src/client/nexush/run_dev b/src/client/nexush/run_dev
index eb756406f8ff6f98f40052902999e254a412c6c6..1ae9f45b43ce8ee5eef21517a9b13e6720417fda 100755
--- a/src/client/nexush/run_dev
+++ b/src/client/nexush/run_dev
@@ -1,4 +1,4 @@
 #!/bin/bash
 
-make prepare_dev
-go run . $@
+make prepare_dev || exit 1
+go run . "$@"
diff --git a/src/client/nexush/run_prod b/src/client/nexush/run_prod
index fffa9d9b7a5a8bbc2734289ba79b0904011292fe..a055bc20b30a5e96ba6aedc5819c440ceb4dda22 100755
--- a/src/client/nexush/run_prod
+++ b/src/client/nexush/run_prod
@@ -1,4 +1,4 @@
 #!/bin/bash
 
-make prepare_prod
-go run . $@
\ No newline at end of file
+make prepare_prod || exit 1
+go run . "$@"
\ No newline at end of file
diff --git a/src/server/consts/consts.go b/src/server/consts/consts.go
index 767f1d3875245e9473b4d8f196486754a2277d1c..804daca07e8fc1bbd940789f4839ac10a294bbdb 100644
--- a/src/server/consts/consts.go
+++ b/src/server/consts/consts.go
@@ -10,12 +10,17 @@ const (
     APIPortMax     = 1099
 
     VMSpiceMinPort = 1100
-    VMSpiceMaxPort = 65000
+    VMSpiceMaxPort = 65535
+
+    VMPwdLength = 8
+    VMPwdDigitCount = 4
+    VMPwdSymbolCount = 0
+    VMPwdRepeatChars = false
 
     MaxUploadSize = "30G"
 
-    // We estimate that KVM allows for this amount of RAM saving in %
-    // (due to page sharing across VMs).
+    // We estimate that KVM allows for this amount of RAM saving in % (due to page sharing across VMs).
+    // 30% seems to be a pretty conservative estimate.
     KsmRamSaving = 0.3
 
     // To prevent RAM saturation, we refuse running new VMs if more than
diff --git a/src/server/router/router.go b/src/server/router/router.go
index 5402ec0cc4d2ddc89acd5172d64216ab35f523c3..348ae345d1e0716898edc51be381f0c84b1dba7a 100644
--- a/src/server/router/router.go
+++ b/src/server/router/router.go
@@ -86,6 +86,7 @@ func (router *Router)Start(port int) {
     vmsGroup.DELETE("/:id", router.vms.DeleteVMByID)
     vmsGroup.PUT("/:id", router.vms.EditVMByID)
     vmsGroup.PUT("/:id/start", router.vms.StartVM)
+    vmsGroup.PUT("/:id/startwithcreds", router.vms.StartVMWithCreds)
     vmsGroup.PUT("/:id/stop", router.vms.KillVM)
     vmsGroup.PUT("/:id/shutdown", router.vms.ShutdownVM)
     vmsGroup.PUT("/:id/reboot", router.vms.RebootVM)
diff --git a/src/server/router/routerVMs.go b/src/server/router/routerVMs.go
index 4bd70fba2f343bd0452e54a61ea387e5c639be63..6467f70a8370b479e03bc6b11cb486cdd5545835 100644
--- a/src/server/router/routerVMs.go
+++ b/src/server/router/routerVMs.go
@@ -234,6 +234,32 @@ func (r *RouterVMs)StartVM(c echo.Context) error {
     })
 }
 
+// Starts a VM based on its ID using the specified credentials (port and password).
+// Requires to be the VM's owner, or either capability:
+// User cap: CAP_VM_START_ANY: any VM can be started.
+// VM access cap: CAP_VM_START: any of the VMs with this cap for the logged user can be started.
+// curl --cacert ca.pem -X PUT https://localhost:1077/vms/e41f3556-ca24-4658-bd79-8c85bd6bff59/startwithcreds -H "Authorization: Bearer <AccessToken>"
+func (r *RouterVMs)StartVMWithCreds(c echo.Context) error {
+    return r.performVMAction(c, caps.CAP_VM_START_ANY, caps.CAP_VM_START, func(c echo.Context, vm *vms.VM) error {
+
+        // Deserializes and validates client's parameters.
+        type Parameters struct {
+            port int              `json:"port"       validate:"required,gte=1100,lte=65535"`
+            pwd string            `json:"pwd"        validate:"required,min=8,max=64"`
+        }
+        p := new(Parameters)
+        if err := decodeJson(c, &p); err != nil {
+            return echo.NewHTTPError(http.StatusBadRequest, err.Error())
+        }
+
+        _, _, err := r.vms.StartVM(vm.GetID())
+        if err != nil {
+            return echo.NewHTTPError(http.StatusBadRequest, err.Error())
+        }
+        return c.JSONPretty(http.StatusOK, jsonMsg("OK"), "    ")
+    })
+}
+
 // Kills a VM based on its ID.
 // Requires to be the VM's owner, or either capability:
 // User cap: CAP_VM_STOP_ANY: any VM can be killed.
diff --git a/src/server/vms/vm.go b/src/server/vms/vm.go
index 203ab32e6dca0cf53cb1ff1639e29f0952c6e42a..327b2fa5659cdce1c104c226b32973c837d2782b 100644
--- a/src/server/vms/vm.go
+++ b/src/server/vms/vm.go
@@ -306,35 +306,26 @@ func (vm *VM)delete() error {
     return nil
 }
 
-// Starts a VM and returns the access password.
-// Password is randomly generated.
-func (vm *VM)start(port int, endofExecFn endOfExecCallback) (string, error) {
+// Starts a VM on the given port with the given password.
+func (vm *VM)start(port int, pwd string, endofExecFn endOfExecCallback) error {
     if vm.DiskBusy {
-        return "", errors.New("Failed starting VM: disk is busy")
-    }
-
-    // Generates a 8 characters long password with 4 digits, 0 symbols,
-    // allowing upper and lower case letters, disallowing repeat characters.
-    pwd, err := passwordGen.Generate(8, 4, 0, false, false)
-    if err != nil {
-        log.Error("Failed starting VM: password generation error: "+err.Error())
-        return "", errors.New("Failed starting VM: password generation error: "+err.Error())
+        return errors.New("Failed starting VM: disk is busy")
     }
 
     // Writes the password in a "secret" file.
     pwdFile, err := vm.writeSecretFile(pwd)
     if err != nil {
         log.Error("Failed starting VM: error creating secret file: "+err.Error())
-        return "", errors.New("Failed starting VM: error creating secret file: "+err.Error())
+        return errors.New("Failed starting VM: error creating secret file: "+err.Error())
     }
 
     if err = vm.runQEMU(port, pwd, pwdFile, endofExecFn); err != nil {
         vm.removeSecretFile()
         os.Remove(vm.qgaSock)  // If QEMU fails it's likely the Guest Agent file it created is still there.
-        return "", errors.New("Failed starting VM: error running QEMU: "+err.Error())
+        return errors.New("Failed starting VM: error running QEMU: "+err.Error())
     }
 
-    return pwd, nil
+    return nil
 }
 
 // Writes the specified password in Base64 in a "secret" file inside the VM directory.
diff --git a/src/server/vms/vms.go b/src/server/vms/vms.go
index 43dcaa9cf488e86aa7eff198453b44afd2957824..6d2edadd6acc7d3a428b1e8d1fc0a0b103ceb809 100644
--- a/src/server/vms/vms.go
+++ b/src/server/vms/vms.go
@@ -13,7 +13,7 @@ import (
     "nexus-server/paths"
     "nexus-server/utils"
     "nexus-server/logger"
-    "nexus-server/consts"
+    c "nexus-server/consts"
     "github.com/google/uuid"
 )
 
@@ -172,45 +172,66 @@ func (vms *VMs)AddVM(vm *VM) error {
     return nil
 }
 
-// Starts a VM by its ID.
-// Returns the port on which the VM is running and the access password.
-func (vms *VMs)StartVM(vmID uuid.UUID) (int, string, error) {
+// Starts a VM by its ID, using the specified port and password.
+func (vms *VMs)StartVMWithCreds(vmID uuid.UUID, port int, pwd string) error {
     vms.rwlock.Lock()
     defer vms.rwlock.Unlock()
 
     vm, err := vms.getVMUnsafe(vmID)
     if err != nil {
-        return 0, "", err
+        return err
     }
 
     vm.mutex.Lock()
     defer vm.mutex.Unlock()
 
     if vm.IsRunning() {
-        return 0, "", errors.New("Failed starting VM: VM already running")
+        return errors.New("Failed starting VM: VM already running")
     }
 
     totalRAM, availRAM, err := utils.GetRAM()
     if err != nil {
-        return -1, "", errors.New("Failed starting VM: failed obtaining memory info: "+err.Error())
+        return errors.New("Failed starting VM: failed obtaining memory info: "+err.Error())
     }
 
     // We estimate that KVM allows for ~30% RAM saving (due to page sharing across VMs).
-    estimatedVmRAM := int(math.Round(float64(vm.v.Ram)*(1.-consts.KsmRamSaving)))
+    estimatedVmRAM := int(math.Round(float64(vm.v.Ram)*(1.-c.KsmRamSaving)))
 
     vms.usedRAM += estimatedVmRAM
 
-    // Checks that at least 15% of the available RAM is left after the VM has started,
+    // Checks that enough available RAM would be left after the VM has started,
     // otherwise, refuses to run it in order to avoid RAM saturation.
-    if availRAM - vms.usedRAM <= int(math.Round(float64(totalRAM)*(1.-consts.RamUsageLimit))) {
+    if availRAM - vms.usedRAM <= int(math.Round(float64(totalRAM)*(1.-c.RamUsageLimit))) {
+        vms.usedRAM -= estimatedVmRAM
+        return errors.New("Failed starting VM: insufficient free RAM")
+    }
+
+    // This callback is called once the VM started with vm.start terminates.
+    endofExecFn := func (vm *VM) {
+        vm.mutex.Lock()
+        vm.removeSecretFile()
+        vm.resetStates()
+        vm.mutex.Unlock()
+        vms.rwlock.Lock()
+        vms.usedPorts[vm.Run.Port] = false
         vms.usedRAM -= estimatedVmRAM
-        return -1, "", errors.New("Failed starting VM: insufficient free RAM")
+        vms.rwlock.Unlock()
     }
 
+    if err = vm.start(port, pwd, endofExecFn); err != nil {
+        return err
+    }
+    return nil
+}
+
+// Starts a VM by its ID using randomly generated port number and password.
+// Returns the port on which the VM is running and the access password.
+func (vms *VMs)StartVM(vmID uuid.UUID) (int, string, error) {
+
     // Locates a free port randomly chosen between VMSpiceMinPort and VMSpiceMaxPort (inclusive).
     var port int
     for {
-        port = utils.Rand(consts.VMSpiceMinPort, consts.VMSpiceMaxPort)
+        port = utils.Rand(c.VMSpiceMinPort, c.VMSpiceMaxPort)
         if !vms.usedPorts[port] {
             if utils.IsPortAvailable(port) {
                 vms.usedPorts[port] = true
@@ -219,20 +240,18 @@ func (vms *VMs)StartVM(vmID uuid.UUID) (int, string, error) {
         }
     }
 
-    // This callback is called once the VM started with vm.start terminates.
-    endofExecFn := func (vm *VM) {
-        vm.mutex.Lock()
-        vm.removeSecretFile()
-        vm.resetStates()
-        vm.mutex.Unlock()
-        vms.rwlock.Lock()
-        vms.usedPorts[vm.Run.Port] = false
-        vms.usedRAM -= estimatedVmRAM
-        vms.rwlock.Unlock()
+    // Randomly generates a 8 characters long password with 4 digits, 0 symbols,
+    // allowing upper and lower case letters, disallowing repeat characters.
+    pwd, err := passwordGen.Generate(c.VMPwdLength, c.VMPwdDigitCount, c.VMPwdSymbolCount, false, c.VMPwdRepeatChars)
+    if err != nil {
+        log.Error("Failed starting VM: password generation error: "+err.Error())
+        return -1, "", errors.New("Failed starting VM: password generation error: "+err.Error())
     }
 
-    pwd, err := vm.start(port, endofExecFn)
-    return port, pwd, err
+    if err = vms.StartVMWithCreds(vmID, port, pwd); err != nil {
+        return -1, "", err
+    }
+    return port, pwd, nil
 }
 
 // Kills a VM by its ID.