diff --git a/README.md b/README.md
index c31c3168580ca8250225f5373e319fbe09b73794..4c70dcf3710f191b4d3f4fa8a862908ae3776f19 100644
--- a/README.md
+++ b/README.md
@@ -198,6 +198,7 @@ Here is the content of `vm.json`:
             "VM_LIST":1,
             "VM_START":1,
             "VM_STOP":1,
+            "VM_REBOOT":1,
             "VM_DESTROY":1,
             "VM_EDIT":1,
             "VM_SET_ACCESS":1,
@@ -207,7 +208,8 @@ Here is the content of `vm.json`:
         "lukeskywalker@force.net" : {
             "VM_LIST":1,
             "VM_START":1,
-            "VM_STOP":1
+            "VM_STOP":1,
+            "VM_REBOOT":1
         },
         "student@nexus.org" : {
             "VM_LIST":1
@@ -280,7 +282,8 @@ The table below lists all potential capabilities associated to a user:
 | VM_DESTROY_ANY  | Can destoy **ANY** VM                             |
 | VM_EDIT_ANY     | Can edit **ANY** VM                               |
 | VM_START_ANY    | Can start **ANY** VM                              |
-| VM_STOP_ANY     | Can stop **ANY** VM                               |
+| VM_STOP_ANY     | Can kill/shutdown **ANY** VM                      |
+| VM_REBOOT_ANY   | Can Reboot **ANY** VM                             |
 | VM_LIST_ANY     | Can list **ANY** VM                               |
 | VM_READFS_ANY   | Can export files from **ANY** VM                  |
 | VM_WRITEFS_ANY  | Can import files into **ANY** VM                  |
@@ -307,7 +310,8 @@ These capabilities are called "VM access capabilities":
 | VM_DESTROY    | User can destroy the VM                                            |
 | VM_EDIT       | User can edit the VM                                               |
 | VM_START      | User can start the VM                                              |
-| VM_STOP       | User can stop/shutdown the VM                                      |
+| VM_STOP       | User can kill/shutdown the VM                                      |
+| VM_REBOOT     | User can reboot the VM                                             |
 | VM_LIST       | User can list or attach to the VM                                  |
 | VM_READFS     | User can export files from the VM                                  |
 | VM_WRITEFS    | User can import files into the VM                                  |
@@ -361,7 +365,8 @@ Legend for the "Done" column:
 |  x   | `/vms`                   | returns VMs that can be listed                 | GET    |            | `VM_LIST_ANY`    | OR  | `VM_LIST`           |
 |  x   | `/vms/start`             | returns VMs that can be started                | GET    |            | `VM_START_ANY`   | OR  | `VM_START`          |
 |  x   | `/vms/attach`            | returns VMs that can be attached to            | GET    |            | `VM_LIST_ANY`    | OR  | `VM_LIST`           |
-|  x   | `/vms/stop`              | returns VMs that can be stopped                | GET    |            | `VM_STOP_ANY`    | OR  | `VM_STOP`           |
+|  x   | `/vms/stop`              | returns VMs that can be killed/shutdown        | GET    |            | `VM_STOP_ANY`    | OR  | `VM_STOP`           |
+|  x   | `/vms/reboot`            | returns VMs that can be rebooted               | GET    |            | `VM_REBOOT_ANY`  | OR  | `VM_REBOOT`         |
 |  x   | `/vms/edit`              | returns VMs that can be edited                 | GET    |            | `VM_EDIT_ANY`    | OR  | `VM_EDIT`           |
 |  x   | `/vms/editaccess`        | returns VMs that can have their access changed | GET    |            | `VM_SET_ACCESS`  | AND | `VM_SET_ACCESS`     |
 |  x   | `/vms/del`               | returns VMs that can be deleted                | GET    |            | `VM_DESTROY_ANY` | OR  | `VM_DESTROY`        |
@@ -374,7 +379,8 @@ Legend for the "Done" column:
 |  x   | `/vms/{id}`                | delete a VM                  | DELETE |                            | `VM_DESTROY_ANY` | OR  | `VM_DESTROY`        |
 |  x   | `/vms/{id}`                | edit a VM                    | PUT    | name,cpus,ram,nic          | `VM_EDIT_ANY`    | OR  | `VM_EDIT`           |
 |  x   | `/vms/{id}/start`          | start a VM                   | PUT    |                            | `VM_START_ANY`   | OR  | `VM_START`          |
-|  x   | `/vms/{id}/stop`           | stop a VM (by force)         | PUT    |                            | `VM_STOP_ANY`    | OR  | `VM_STOP`           |
+|  x   | `/vms/{id}/stop`           | kill a VM                    | PUT    |                            | `VM_STOP_ANY`    | OR  | `VM_STOP`           |
+|  x   | `/vms/{id}/reboot`         | reboot a VM                  | PUT    |                            | `VM_REBOOT_ANY`  | OR  | `VM_REBOOT`         |
 |  x   | `/vms/{id}/shutdown`       | gracefully shutdown a VM     | PUT    |                            | `VM_STOP_ANY`    | OR  | `VM_STOP`           |
 |  x   | `/vms/{id}/access/{email}` | set VM access for a user     | PUT    | caps                       | `VM_SET_ACCESS`  | AND | `VM_SET_ACCESS`     |
 |  x   | `/vms/{id}/access/{email}` | del VM access for a user     | DELETE |                            | `VM_SET_ACCESS`  | AND | `VM_SET_ACCESS`     |
diff --git a/conf/users.json b/conf/users.json
index a10cbd12008b8ef3a4bbadedae50787322345aeb..a407f1f5d1509234d407b079c1591f77d50db215 100644
--- a/conf/users.json
+++ b/conf/users.json
@@ -14,6 +14,7 @@
             "VM_EDIT_ANY":1,
             "VM_START_ANY":1,
             "VM_STOP_ANY":1,
+            "VM_REBOOT_ANY":1,
             "VM_LIST_ANY":1,
             "VM_READFS_ANY":1,
             "VM_WRITEFS_ANY":1,
diff --git a/src/caps/caps.go b/src/caps/caps.go
index ecd7e3579c30fbe2f25fb7d92cba11a824cdff8b..2a5c3380a60865d0d5e6063da1e4245d4450644d 100644
--- a/src/caps/caps.go
+++ b/src/caps/caps.go
@@ -15,6 +15,8 @@ const (
 	CAP_VM_START_ANY         = "VM_START_ANY"
 	CAP_VM_STOP              = "VM_STOP"
 	CAP_VM_STOP_ANY          = "VM_STOP_ANY"
+	CAP_VM_REBOOT            = "VM_REBOOT"
+	CAP_VM_REBOOT_ANY        = "VM_REBOOT_ANY"
 	CAP_VM_CREATE            = "VM_CREATE"
 	CAP_VM_DESTROY           = "VM_DESTROY"
 	CAP_VM_DESTROY_ANY       = "VM_DESTROY_ANY"
@@ -47,6 +49,7 @@ var userCaps = Capabilities {
 	CAP_VM_EDIT_ANY: 1,
 	CAP_VM_START_ANY: 1,
 	CAP_VM_STOP_ANY: 1,
+	CAP_VM_REBOOT_ANY: 1,
 	CAP_VM_LIST_ANY: 1,
 	CAP_VM_SET_ACCESS: 1,
 	CAP_VM_READFS_ANY: 1,
@@ -68,6 +71,7 @@ var VMAccessCaps = Capabilities {
 	CAP_VM_EDIT: 1,
 	CAP_VM_START: 1,
 	CAP_VM_STOP: 1,
+	CAP_VM_REBOOT: 1,
 	CAP_VM_LIST: 1,
 	CAP_VM_READFS: 1,
 	CAP_VM_WRITEFS: 1,
diff --git a/src/qga/qga.go b/src/qga/qga.go
index 0322d60ca79ab8510000abb87e26fbafbc63ac99..3cba9c4af0d4719d47fe1bc7534f8f0a281f6ea4 100644
--- a/src/qga/qga.go
+++ b/src/qga/qga.go
@@ -18,6 +18,10 @@ func New() *QGACon {
 	return &QGACon{}
 }
 
+// TODO
+// Rewrite most of this code to use a json serializer
+// instead of this ugly-dirty "json as string" code!
+
 // sockAddr is the path to a UNIX domain socket.
 func (q *QGACon)Open(sockAddr string) error {
 	con, err := net.Dial("unix", sockAddr)
@@ -43,6 +47,12 @@ func (q *QGACon)SendReboot() error {
 	return q.SendCmd("reboot")
 }
 
+// Send a forced reboot command to the guest OS.
+func (q *QGACon)SendForcedReboot() error {
+	_, err := q.sock.Write([]byte(`{"execute":"guest-exec", "arguments":{"path":"reboot", "arg": ["--force"]}}`))
+	return err
+}
+
 // Send a command to the guest OS.
 func (q *QGACon)SendCmd(cmd string) error {
 	_, err := q.sock.Write([]byte(`{"execute":"guest-exec", "arguments":{"path":"`+cmd+`"}}`))
diff --git a/src/router/router.go b/src/router/router.go
index d86337ffcecdd8bf5826e2f6e610873edf4efee5..89454ff2f1617eac0d68d8b104bd2444070cce88 100644
--- a/src/router/router.go
+++ b/src/router/router.go
@@ -76,6 +76,7 @@ func (router *Router)Start(port int) {
 	vmsGroup.GET("/del", router.vms.GetDeletableVMs)
 	vmsGroup.GET("/start", router.vms.GetStartableVMs)
 	vmsGroup.GET("/stop", router.vms.GetStoppableVMs)
+	vmsGroup.GET("/reboot", router.vms.GetRebootableVMs)
 	vmsGroup.GET("/edit", router.vms.GetEditableVMs)
 	vmsGroup.GET("/editaccess", router.vms.GetEditableVMAccessVMs)
 	vmsGroup.GET("/exportdir", router.vms.GetDirExportableVMs)
@@ -85,8 +86,9 @@ 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/stop", router.vms.StopVM)
+	vmsGroup.PUT("/:id/stop", router.vms.KillVM)
 	vmsGroup.PUT("/:id/shutdown", router.vms.ShutdownVM)
+	vmsGroup.PUT("/:id/reboot", router.vms.RebootVM)
 	vmsGroup.PUT("/:id/access/:email", router.vms.SetVMAccessForUser)
 	vmsGroup.DELETE("/:id/access/:email", router.vms.DeleteVMAccessForUser)
 	vmsGroup.GET("/:id/exportdir", router.vms.ExportVMDir)
diff --git a/src/router/routerVMs.go b/src/router/routerVMs.go
index 5aa34e2690b969260f3e9f5c55d1a961b72523a1..3ff8641bcbc1f80fb5da06611b9a8ebaaaeb6687 100644
--- a/src/router/routerVMs.go
+++ b/src/router/routerVMs.go
@@ -83,6 +83,17 @@ func (r *RouterVMs)GetStoppableVMs(c echo.Context) error {
 	})
 }
 
+// Returns VMs that are running and that can be rebooted.
+// Requires either capability:
+// User cap: CAP_VM_REBOOT_ANY: returns all VMs.
+// VM access cap: CAP_VM_REBOOT: returns all VMs with this cap for the logged user.
+// curl --cacert ca.pem -X GET https://localhost:1077/vms/reboot -H "Authorization: Bearer <AccessToken>"
+func (r *RouterVMs)GetRebootableVMs(c echo.Context) error {
+	return r.performVMsList(c, caps.CAP_VM_REBOOT_ANY, caps.CAP_VM_REBOOT, func(vm vms.VM) bool {
+		return vm.IsRunning()
+	})
+}
+
 // Returns VMs that can be edited.
 // Requires either capability:
 // User cap: CAP_VM_EDIT_ANY: returns all VMs.
@@ -216,24 +227,24 @@ func (r *RouterVMs)StartVM(c echo.Context) error {
 	})
 }
 
-// Stops (by force) a VM based on its ID.
+// Kills a VM based on its ID.
 // Requires either capability:
-// User cap: CAP_VM_STOP_ANY: any VM can be stopped.
-// VM access cap: CAP_VM_STOP: any of the VMs with this cap for the logged user can be stopped.
+// User cap: CAP_VM_STOP_ANY: any VM can be killed.
+// VM access cap: CAP_VM_STOP: any of the VMs with this cap for the logged user can be killed.
 // curl --cacert ca.pem -X PUT https://localhost:1077/vms/e41f3556-ca24-4658-bd79-8c85bd6bff59/stop -H "Authorization: Bearer <AccessToken>"
-func (r *RouterVMs)StopVM(c echo.Context) error {
+func (r *RouterVMs)KillVM(c echo.Context) error {
 	return r.performVMAction(c, caps.CAP_VM_STOP_ANY, caps.CAP_VM_STOP, func(c echo.Context, vm *vms.VM) error {
-		if err := r.vms.StopVM(vm.ID); err != nil {
+		if err := r.vms.KillVM(vm.ID); err != nil {
 			return echo.NewHTTPError(http.StatusNotFound, err.Error())
 		}
 		return c.JSONPretty(http.StatusOK, jsonMsg("OK"), "    ")
 	})
 }
 
-// Gracefully stops a VM based on its ID.
+// Gracefully shutdown a VM based on its ID.
 // Requires either capability:
-// User cap: CAP_VM_STOP_ANY: any VM can be stopped.
-// VM access cap: CAP_VM_STOP: any of the VMs with this cap for the logged user can be stopped.
+// User cap: CAP_VM_STOP_ANY: any VM can be shutdown.
+// VM access cap: CAP_VM_STOP: any of the VMs with this cap for the logged user can be shutdown.
 // curl --cacert ca.pem -X PUT https://localhost:1077/vms/e41f3556-ca24-4658-bd79-8c85bd6bff59/shutdown -H "Authorization: Bearer <AccessToken>"
 func (r *RouterVMs)ShutdownVM(c echo.Context) error {
 	return r.performVMAction(c, caps.CAP_VM_STOP_ANY, caps.CAP_VM_STOP, func(c echo.Context, vm *vms.VM) error {
@@ -244,6 +255,20 @@ func (r *RouterVMs)ShutdownVM(c echo.Context) error {
 	})
 }
 
+// Reboot a VM based on its ID.
+// Requires either capability:
+// User cap: CAP_VM_REBOOT_ANY: any VM can be rebooted.
+// VM access cap: CAP_VM_REBOOT: any of the VMs with this cap for the logged user can be rebooted.
+// curl --cacert ca.pem -X PUT https://localhost:1077/vms/e41f3556-ca24-4658-bd79-8c85bd6bff59/stop -H "Authorization: Bearer <AccessToken>"
+func (r *RouterVMs)RebootVM(c echo.Context) error {
+	return r.performVMAction(c, caps.CAP_VM_REBOOT_ANY, caps.CAP_VM_REBOOT, func(c echo.Context, vm *vms.VM) error {
+		if err := r.vms.RebootVM(vm.ID); err != nil {
+			return echo.NewHTTPError(http.StatusNotFound, err.Error())
+		}
+		return c.JSONPretty(http.StatusOK, jsonMsg("OK"), "    ")
+	})
+}
+
 // Edit a VM' specs: name, cpus, ram, nic
 // Requires either capability:
 // User cap: CAP_VM_EDIT_ANY: any VM can be edited.
diff --git a/src/version/version.go b/src/version/version.go
index 184ca5149d993c759986ef72d1fa20b679bd5c93..4bad79398d5274c865c75830e9bc2cad3dd066f6 100644
--- a/src/version/version.go
+++ b/src/version/version.go
@@ -6,8 +6,8 @@ import (
 
 const (
 	major  = 1
-	minor  = 0
-	bugfix = 1
+	minor  = 1
+	bugfix = 0
 )
 
 type Version struct {
diff --git a/src/vms/vm.go b/src/vms/vm.go
index 769bbd329dbff23fb2b09004f9b9f7852868c7eb..88ae4516472b544c7586de913e78b808be351934 100644
--- a/src/vms/vm.go
+++ b/src/vms/vm.go
@@ -327,8 +327,8 @@ func (vm *VM)removeSecretFile() {
 	os.Remove(pwdFile)
 }
 
-// Stops by force a running VM.
-func (vm *VM)stop() error {
+// Kills by force a running VM.
+func (vm *VM)kill() error {
 	if !vm.IsRunning() {
 		return errors.New("Failed stopping VM: VM is not running")
 	}
@@ -343,34 +343,71 @@ func (vm *VM)stop() error {
 	return nil
 }
 
-// Gracefully stops a running VM.
-// Uses QGA commands to talk to the VM, which means QEMU Guest Agent must be running
-// in the VM, otherwise the VM won't shutdown.
+// Gracefully shutdowns a running VM.
+// Uses QGA commands to talk to the VM, which means QEMU Guest Agent must be
+// running in the VM, otherwise it won't work.
 func (vm *VM)shutdown() error {
+	prefix := "Shutdown failed: "
+
 	if !vm.IsRunning() {
-		return errors.New("Shutdown failed: VM is not running")
+		return errors.New(prefix+"VM is not running")
 	}
 
 	if vm.DiskBusy {
-		return errors.New("Shutdown failed: VM disk is busy")
+		return errors.New(prefix+"VM disk is busy")
 	}
 
 	// Sends a QGA command to order the VM to shutdown.
 	con := qga.New()
 	if err := con.Open(vm.qgaSock); err != nil {
-		log.Error("VM shutdown failed (open): "+err.Error())
-		return errors.New("VM shutdown failed (open): "+err.Error())
+		log.Error(prefix+"(open): "+err.Error())
+		return errors.New(prefix+"(open): "+err.Error())
 	}	
 
 	if err := con.SendShutdown(); err != nil {
 		con.Close()
-		log.Error("VM shutdown failed (send): "+err.Error())
-		return errors.New("VM shutdown failed (send): "+err.Error())
+		log.Error(prefix+"(send): "+err.Error())
+		return errors.New(prefix+"(send): "+err.Error())
+	}
+
+	if err := con.Close(); err != nil {
+		log.Error(prefix+"(close): "+err.Error())
+		return errors.New(prefix+"(close): "+err.Error())
+	}
+
+	return nil
+}
+
+// Reboots a running VM.
+// Uses QGA commands to talk to the VM, which means QEMU Guest Agent must be
+// running in the VM, otherwise it won't work.
+func (vm *VM)reboot() error {
+	prefix := "Shutdown failed: "
+
+	if !vm.IsRunning() {
+		return errors.New(prefix+"VM is not running")
+	}
+
+	if vm.DiskBusy {
+		return errors.New(prefix+"VM disk is busy")
+	}
+
+	// Sends a QGA command to order the VM to shutdown.
+	con := qga.New()
+	if err := con.Open(vm.qgaSock); err != nil {
+		log.Error(prefix+"(open): "+err.Error())
+		return errors.New(prefix+"(open): "+err.Error())
+	}
+
+	if err := con.SendReboot(); err != nil {
+		con.Close()
+		log.Error(prefix+"(send): "+err.Error())
+		return errors.New(prefix+"(send): "+err.Error())
 	}
 
 	if err := con.Close(); err != nil {
-		log.Error("VM shutdown failed (close): "+err.Error())
-		return errors.New("VM shutdown failed (close): "+err.Error())
+		log.Error(prefix+"(close): "+err.Error())
+		return errors.New(prefix+"(close): "+err.Error())
 	}
 
 	return nil
diff --git a/src/vms/vms.go b/src/vms/vms.go
index 49ff2201dc2efd35c6ddfa0bbd88107f47c3cfac..109180db1b7158b615be1cb1ec71b482bbc23e99 100644
--- a/src/vms/vms.go
+++ b/src/vms/vms.go
@@ -222,8 +222,8 @@ func (vms *VMs)StartVM(vmID uuid.UUID) (int, string, error) {
 	return port, pwd, err
 }
 
-// Stops by force a VM by its ID.
-func (vms *VMs)StopVM(vmID uuid.UUID) error {
+// Kills a VM by its ID.
+func (vms *VMs)KillVM(vmID uuid.UUID) error {
 	vms.rwlock.RLock()
 	defer vms.rwlock.RUnlock()
 
@@ -233,7 +233,7 @@ func (vms *VMs)StopVM(vmID uuid.UUID) error {
 	}
 
 	vm.mutex.Lock()
-	err = vm.stop()
+	err = vm.kill()
 	vm.mutex.Unlock()
 	return err
 }
@@ -254,6 +254,22 @@ func (vms *VMs)ShutdownVM(vmID uuid.UUID) error {
 	return err
 }
 
+// Reboots a VM by its ID.
+func (vms *VMs)RebootVM(vmID uuid.UUID) error {
+	vms.rwlock.Lock()
+	defer vms.rwlock.Unlock()
+
+	vm, err := vms.getVMUnsafe(vmID)
+	if err != nil {
+		return err
+	}
+
+	vm.mutex.Lock()
+	err = vm.reboot()
+	vm.mutex.Unlock()
+	return err
+}
+
 // Returns true if the given template is used by a VM.
 func (vms *VMs)IsTemplateUsed(templateID string) bool {
 	vms.rwlock.RLock()