diff --git a/api/client/node/accept.go b/api/client/node/accept.go
index e0b8c86a84..da9e8f14d5 100644
--- a/api/client/node/accept.go
+++ b/api/client/node/accept.go
@@ -21,8 +21,9 @@ func newAcceptCommand(dockerCli *client.DockerCli) *cobra.Command {
 }
 
 func runAccept(dockerCli *client.DockerCli, nodes []string) error {
-	accept := func(node *swarm.Node) {
+	accept := func(node *swarm.Node) error {
 		node.Spec.Membership = swarm.NodeMembershipAccepted
+		return nil
 	}
 	success := func(nodeID string) {
 		fmt.Fprintf(dockerCli.Out(), "Node %s accepted in the swarm.\n", nodeID)
diff --git a/api/client/node/demote.go b/api/client/node/demote.go
index af6c4c30eb..058471daaf 100644
--- a/api/client/node/demote.go
+++ b/api/client/node/demote.go
@@ -21,8 +21,9 @@ func newDemoteCommand(dockerCli *client.DockerCli) *cobra.Command {
 }
 
 func runDemote(dockerCli *client.DockerCli, nodes []string) error {
-	demote := func(node *swarm.Node) {
+	demote := func(node *swarm.Node) error {
 		node.Spec.Role = swarm.NodeRoleWorker
+		return nil
 	}
 	success := func(nodeID string) {
 		fmt.Fprintf(dockerCli.Out(), "Manager %s demoted in the swarm.\n", nodeID)
diff --git a/api/client/node/opts.go b/api/client/node/opts.go
index cd160252d9..381e1dfcea 100644
--- a/api/client/node/opts.go
+++ b/api/client/node/opts.go
@@ -4,18 +4,37 @@ import (
 	"fmt"
 	"strings"
 
+	"github.com/docker/docker/opts"
+	runconfigopts "github.com/docker/docker/runconfig/opts"
 	"github.com/docker/engine-api/types/swarm"
 )
 
 type nodeOptions struct {
+	annotations
 	role         string
 	membership   string
 	availability string
 }
 
+type annotations struct {
+	name   string
+	labels opts.ListOpts
+}
+
+func newNodeOptions() *nodeOptions {
+	return &nodeOptions{
+		annotations: annotations{
+			labels: opts.NewListOpts(nil),
+		},
+	}
+}
+
 func (opts *nodeOptions) ToNodeSpec() (swarm.NodeSpec, error) {
 	var spec swarm.NodeSpec
 
+	spec.Annotations.Name = opts.annotations.name
+	spec.Annotations.Labels = runconfigopts.ConvertKVStringsToMap(opts.annotations.labels.GetAll())
+
 	switch swarm.NodeRole(strings.ToLower(opts.role)) {
 	case swarm.NodeRoleWorker:
 		spec.Role = swarm.NodeRoleWorker
diff --git a/api/client/node/promote.go b/api/client/node/promote.go
index b3e0f4d7e5..760ec6f62b 100644
--- a/api/client/node/promote.go
+++ b/api/client/node/promote.go
@@ -21,8 +21,9 @@ func newPromoteCommand(dockerCli *client.DockerCli) *cobra.Command {
 }
 
 func runPromote(dockerCli *client.DockerCli, nodes []string) error {
-	promote := func(node *swarm.Node) {
+	promote := func(node *swarm.Node) error {
 		node.Spec.Role = swarm.NodeRoleManager
+		return nil
 	}
 	success := func(nodeID string) {
 		fmt.Fprintf(dockerCli.Out(), "Node %s promoted to a manager in the swarm.\n", nodeID)
diff --git a/api/client/node/update.go b/api/client/node/update.go
index 840182de0c..5cbdd908d8 100644
--- a/api/client/node/update.go
+++ b/api/client/node/update.go
@@ -5,6 +5,8 @@ import (
 
 	"github.com/docker/docker/api/client"
 	"github.com/docker/docker/cli"
+	"github.com/docker/docker/opts"
+	runconfigopts "github.com/docker/docker/runconfig/opts"
 	"github.com/docker/engine-api/types/swarm"
 	"github.com/spf13/cobra"
 	"github.com/spf13/pflag"
@@ -12,7 +14,7 @@ import (
 )
 
 func newUpdateCommand(dockerCli *client.DockerCli) *cobra.Command {
-	var opts nodeOptions
+	nodeOpts := newNodeOptions()
 
 	cmd := &cobra.Command{
 		Use:   "update [OPTIONS] NODE",
@@ -24,9 +26,12 @@ func newUpdateCommand(dockerCli *client.DockerCli) *cobra.Command {
 	}
 
 	flags := cmd.Flags()
-	flags.StringVar(&opts.role, flagRole, "", "Role of the node (worker/manager)")
-	flags.StringVar(&opts.membership, flagMembership, "", "Membership of the node (accepted/rejected)")
-	flags.StringVar(&opts.availability, flagAvailability, "", "Availability of the node (active/pause/drain)")
+	flags.StringVar(&nodeOpts.role, flagRole, "", "Role of the node (worker/manager)")
+	flags.StringVar(&nodeOpts.membership, flagMembership, "", "Membership of the node (accepted/rejected)")
+	flags.StringVar(&nodeOpts.availability, flagAvailability, "", "Availability of the node (active/pause/drain)")
+	flags.Var(&nodeOpts.annotations.labels, flagLabelAdd, "Add or update a node label (key=value)")
+	labelKeys := opts.NewListOpts(nil)
+	flags.Var(&labelKeys, flagLabelRemove, "Remove a node label if exists")
 	return cmd
 }
 
@@ -37,7 +42,7 @@ func runUpdate(dockerCli *client.DockerCli, flags *pflag.FlagSet, nodeID string)
 	return updateNodes(dockerCli, []string{nodeID}, mergeNodeUpdate(flags), success)
 }
 
-func updateNodes(dockerCli *client.DockerCli, nodes []string, mergeNode func(node *swarm.Node), success func(nodeID string)) error {
+func updateNodes(dockerCli *client.DockerCli, nodes []string, mergeNode func(node *swarm.Node) error, success func(nodeID string)) error {
 	client := dockerCli.Client()
 	ctx := context.Background()
 
@@ -47,7 +52,10 @@ func updateNodes(dockerCli *client.DockerCli, nodes []string, mergeNode func(nod
 			return err
 		}
 
-		mergeNode(&node)
+		err = mergeNode(&node)
+		if err != nil {
+			return err
+		}
 		err = client.NodeUpdate(ctx, node.ID, node.Version, node.Spec)
 		if err != nil {
 			return err
@@ -57,22 +65,51 @@ func updateNodes(dockerCli *client.DockerCli, nodes []string, mergeNode func(nod
 	return nil
 }
 
-func mergeNodeUpdate(flags *pflag.FlagSet) func(*swarm.Node) {
-	return func(node *swarm.Node) {
+func mergeNodeUpdate(flags *pflag.FlagSet) func(*swarm.Node) error {
+	return func(node *swarm.Node) error {
 		spec := &node.Spec
 
 		if flags.Changed(flagRole) {
-			str, _ := flags.GetString(flagRole)
+			str, err := flags.GetString(flagRole)
+			if err != nil {
+				return err
+			}
 			spec.Role = swarm.NodeRole(str)
 		}
 		if flags.Changed(flagMembership) {
-			str, _ := flags.GetString(flagMembership)
+			str, err := flags.GetString(flagMembership)
+			if err != nil {
+				return err
+			}
 			spec.Membership = swarm.NodeMembership(str)
 		}
 		if flags.Changed(flagAvailability) {
-			str, _ := flags.GetString(flagAvailability)
+			str, err := flags.GetString(flagAvailability)
+			if err != nil {
+				return err
+			}
 			spec.Availability = swarm.NodeAvailability(str)
 		}
+		if spec.Annotations.Labels == nil {
+			spec.Annotations.Labels = make(map[string]string)
+		}
+		if flags.Changed(flagLabelAdd) {
+			labels := flags.Lookup(flagLabelAdd).Value.(*opts.ListOpts).GetAll()
+			for k, v := range runconfigopts.ConvertKVStringsToMap(labels) {
+				spec.Annotations.Labels[k] = v
+			}
+		}
+		if flags.Changed(flagLabelRemove) {
+			keys := flags.Lookup(flagLabelRemove).Value.(*opts.ListOpts).GetAll()
+			for _, k := range keys {
+				// if a key doesn't exist, fail the command explicitly
+				if _, exists := spec.Annotations.Labels[k]; !exists {
+					return fmt.Errorf("key %s doesn't exist in node's labels", k)
+				}
+				delete(spec.Annotations.Labels, k)
+			}
+		}
+		return nil
 	}
 }
 
@@ -80,4 +117,6 @@ const (
 	flagRole         = "role"
 	flagMembership   = "membership"
 	flagAvailability = "availability"
+	flagLabelAdd     = "label-add"
+	flagLabelRemove  = "label-rm"
 )
diff --git a/docs/reference/commandline/node_update.md b/docs/reference/commandline/node_update.md
index 32e8582d2c..ed3dc60d02 100644
--- a/docs/reference/commandline/node_update.md
+++ b/docs/reference/commandline/node_update.md
@@ -19,6 +19,8 @@ Update a node
 Options:
       --availability string   Availability of the node (active/pause/drain)
       --help                  Print usage
+      --label-add value       Add or update a node label (key=value) (default [])
+      --label-rm value        Remove a node label if exists (default [])
       --membership string     Membership of the node (accepted/rejected)
       --role string           Role of the node (worker/manager)
 ```