diff --git a/api-docs/swagger.yml b/api-docs/swagger.yml
index 4c18542a3c3cbc7c01e858bf07ec93d2f898d340..2f9b564bc4b16120fd81fca48b0fad15a227e6af 100644
--- a/api-docs/swagger.yml
+++ b/api-docs/swagger.yml
@@ -43,6 +43,23 @@ paths:
       responses:
         '200':
           description: OK
+    put:
+      summary: Update a function.
+      description: ''
+      consumes:
+        - application/json
+      produces:
+        - application/json
+      parameters:
+        - in: body
+          name: body
+          description: Function to update
+          required: true
+          schema:
+            $ref: '#/definitions/CreateFunctionRequest'
+      responses:
+        '200':
+          description: OK
     delete:
       summary: Remove a deployed function.
       description: ''
diff --git a/gateway/handlers/createhandler.go b/gateway/handlers/createhandler.go
index 85876d25e72f05de7d6589b069f213382611de01..f5c68734dcfcbda33fd267269cd81de2c8ddb258 100644
--- a/gateway/handlers/createhandler.go
+++ b/gateway/handlers/createhandler.go
@@ -21,8 +21,10 @@ import (
 	"github.com/docker/docker/registry"
 )
 
+var linuxOnlyConstraints = []string{"node.platform.os == linux"}
+
 // MakeNewFunctionHandler creates a new function (service) inside the swarm network.
-func MakeNewFunctionHandler(metricsOptions metrics.MetricOptions, c *client.Client, maxRestarts uint64) http.HandlerFunc {
+func MakeNewFunctionHandler(metricsOptions metrics.MetricOptions, c *client.Client, maxRestarts uint64, restartDelay time.Duration) http.HandlerFunc {
 	return func(w http.ResponseWriter, r *http.Request) {
 		defer r.Body.Close()
 		body, _ := ioutil.ReadAll(r.Body)
@@ -51,7 +53,7 @@ func MakeNewFunctionHandler(metricsOptions metrics.MetricOptions, c *client.Clie
 			}
 			options.EncodedRegistryAuth = auth
 		}
-		spec := makeSpec(&request, maxRestarts)
+		spec := makeSpec(&request, maxRestarts, restartDelay)
 
 		response, err := c.ServiceCreate(context.Background(), spec, options)
 		if err != nil {
@@ -64,8 +66,7 @@ func MakeNewFunctionHandler(metricsOptions metrics.MetricOptions, c *client.Clie
 	}
 }
 
-func makeSpec(request *requests.CreateFunctionRequest, maxRestarts uint64) swarm.ServiceSpec {
-	linuxOnlyConstraints := []string{"node.platform.os == linux"}
+func makeSpec(request *requests.CreateFunctionRequest, maxRestarts uint64, restartDelay time.Duration) swarm.ServiceSpec {
 	constraints := []string{}
 	if request.Constraints != nil && len(request.Constraints) > 0 {
 		constraints = request.Constraints
@@ -76,7 +77,6 @@ func makeSpec(request *requests.CreateFunctionRequest, maxRestarts uint64) swarm
 	nets := []swarm.NetworkAttachmentConfig{
 		{Target: request.Network},
 	}
-	restartDelay := time.Second * 5
 
 	spec := swarm.ServiceSpec{
 		TaskTemplate: swarm.TaskSpec{
@@ -100,13 +100,7 @@ func makeSpec(request *requests.CreateFunctionRequest, maxRestarts uint64) swarm
 	}
 
 	// TODO: request.EnvProcess should only be set if it's not nil, otherwise we override anything in the Docker image already
-	var env []string
-	if len(request.EnvProcess) > 0 {
-		env = append(env, fmt.Sprintf("fprocess=%s", request.EnvProcess))
-	}
-	for k, v := range request.EnvVars {
-		env = append(env, fmt.Sprintf("%s=%s", k, v))
-	}
+	env := buildEnv(request.EnvProcess, request.EnvVars)
 
 	if len(env) > 0 {
 		spec.TaskTemplate.ContainerSpec.Env = env
@@ -115,6 +109,17 @@ func makeSpec(request *requests.CreateFunctionRequest, maxRestarts uint64) swarm
 	return spec
 }
 
+func buildEnv(envProcess string, envVars map[string]string) []string {
+	var env []string
+	if len(envProcess) > 0 {
+		env = append(env, fmt.Sprintf("fprocess=%s", envProcess))
+	}
+	for k, v := range envVars {
+		env = append(env, fmt.Sprintf("%s=%s", k, v))
+	}
+	return env
+}
+
 // BuildEncodedAuthConfig for private registry
 func BuildEncodedAuthConfig(basicAuthB64 string, dockerImage string) (string, error) {
 	// extract registry server address
diff --git a/gateway/handlers/update_handler.go b/gateway/handlers/update_handler.go
index aa638e01e3d70b0a40806d8c6c7f55d083fae471..83c4e37d16dc7bcd7c18a62054aa5f96670f197d 100644
--- a/gateway/handlers/update_handler.go
+++ b/gateway/handlers/update_handler.go
@@ -1,16 +1,119 @@
 package handlers
 
 import (
+	"context"
+	"encoding/json"
+	"fmt"
+	"io/ioutil"
+	"log"
 	"net/http"
+	"time"
 
 	"github.com/alexellis/faas/gateway/metrics"
+	"github.com/alexellis/faas/gateway/requests"
+	"github.com/docker/docker/api/types"
+	"github.com/docker/docker/api/types/swarm"
 	"github.com/docker/docker/client"
 )
 
-// MakeUpdateFunctionHandler request to update an existing function with new configuration such as image, parameters etc.
-func MakeUpdateFunctionHandler(metricsOptions metrics.MetricOptions, c *client.Client, maxRestarts uint64) http.HandlerFunc {
+// MakeUpdateFunctionHandler request to update an existing function with new configuration such as image, envvars etc.
+func MakeUpdateFunctionHandler(metricsOptions metrics.MetricOptions, c *client.Client, maxRestarts uint64, restartDelay time.Duration) http.HandlerFunc {
 	return func(w http.ResponseWriter, r *http.Request) {
+		ctx := context.Background()
+
 		defer r.Body.Close()
-		w.WriteHeader(http.StatusNotImplemented)
+		body, _ := ioutil.ReadAll(r.Body)
+
+		request := requests.CreateFunctionRequest{}
+		err := json.Unmarshal(body, &request)
+		if err != nil {
+			log.Println("Error parsing request:", err)
+			w.WriteHeader(http.StatusBadRequest)
+			w.Write([]byte(err.Error()))
+			return
+		}
+
+		serviceInspectopts := types.ServiceInspectOptions{
+			InsertDefaults: true,
+		}
+
+		service, _, err := c.ServiceInspectWithRaw(ctx, request.Service, serviceInspectopts)
+		if err != nil {
+			log.Println("Error inspecting service", err)
+			w.WriteHeader(http.StatusNotFound)
+			w.Write([]byte(err.Error()))
+			return
+		}
+
+		updateSpec(&request, &service.Spec, maxRestarts, restartDelay)
+
+		updateOpts := types.ServiceUpdateOptions{}
+		updateOpts.RegistryAuthFrom = types.RegistryAuthFromSpec
+
+		if len(request.RegistryAuth) > 0 {
+			auth, err := BuildEncodedAuthConfig(request.RegistryAuth, request.Image)
+			if err != nil {
+				log.Println("Error building registry auth configuration:", err)
+				w.WriteHeader(http.StatusBadRequest)
+				w.Write([]byte("Invalid registry auth"))
+				return
+			}
+			updateOpts.EncodedRegistryAuth = auth
+		}
+
+		response, err := c.ServiceUpdate(ctx, service.ID, service.Version, service.Spec, updateOpts)
+		if err != nil {
+			log.Println("Error updating service:", err)
+			w.WriteHeader(http.StatusBadRequest)
+			w.Write([]byte("Update error: " + err.Error()))
+			return
+		}
+		log.Println(response.Warnings)
+	}
+}
+
+func updateSpec(request *requests.CreateFunctionRequest, spec *swarm.ServiceSpec, maxRestarts uint64, restartDelay time.Duration) {
+
+	constraints := []string{}
+	if request.Constraints != nil && len(request.Constraints) > 0 {
+		constraints = request.Constraints
+	} else {
+		constraints = linuxOnlyConstraints
+	}
+
+	nets := []swarm.NetworkAttachmentConfig{
+		{Target: request.Network},
+	}
+
+	spec.TaskTemplate.RestartPolicy.MaxAttempts = &maxRestarts
+	spec.TaskTemplate.RestartPolicy.Condition = swarm.RestartPolicyConditionAny
+	spec.TaskTemplate.RestartPolicy.Delay = &restartDelay
+	spec.TaskTemplate.ContainerSpec.Image = request.Image
+	spec.TaskTemplate.ContainerSpec.Labels = map[string]string{
+		"function": "true",
+		"uid":      fmt.Sprintf("%d", time.Now().Nanosecond()),
+	}
+	spec.TaskTemplate.Networks = nets
+	spec.TaskTemplate.Placement = &swarm.Placement{
+		Constraints: constraints,
+	}
+
+	spec.Annotations = swarm.Annotations{
+		Name: request.Service,
+	}
+
+	spec.RollbackConfig = &swarm.UpdateConfig{
+		FailureAction: "pause",
+	}
+
+	spec.UpdateConfig = &swarm.UpdateConfig{
+		Parallelism:   1,
+		FailureAction: "rollback",
+	}
+
+	env := buildEnv(request.EnvProcess, request.EnvVars)
+
+	if len(env) > 0 {
+		spec.TaskTemplate.ContainerSpec.Env = env
 	}
 }
diff --git a/gateway/server.go b/gateway/server.go
index d66880622861bf1e61ff1f00253afed5dd638559..5c6fb5db62b092e9aceff1b44ab236ab550b7cbb 100644
--- a/gateway/server.go
+++ b/gateway/server.go
@@ -92,13 +92,15 @@ func main() {
 
 		// How many times to reschedule a function.
 		maxRestarts := uint64(5)
+		// Delay between container restarts
+		restartDelay := time.Second * 5
 
 		faasHandlers.Proxy = internalHandlers.MakeProxy(metricsOptions, true, dockerClient, &logger)
 		faasHandlers.RoutelessProxy = internalHandlers.MakeProxy(metricsOptions, false, dockerClient, &logger)
 		faasHandlers.ListFunctions = internalHandlers.MakeFunctionReader(metricsOptions, dockerClient)
-		faasHandlers.DeployFunction = internalHandlers.MakeNewFunctionHandler(metricsOptions, dockerClient, maxRestarts)
+		faasHandlers.DeployFunction = internalHandlers.MakeNewFunctionHandler(metricsOptions, dockerClient, maxRestarts, restartDelay)
 		faasHandlers.DeleteFunction = internalHandlers.MakeDeleteFunctionHandler(metricsOptions, dockerClient)
-		faasHandlers.UpdateFunction = internalHandlers.MakeUpdateFunctionHandler(metricsOptions, dockerClient, maxRestarts)
+		faasHandlers.UpdateFunction = internalHandlers.MakeUpdateFunctionHandler(metricsOptions, dockerClient, maxRestarts, restartDelay)
 
 		faasHandlers.Alert = internalHandlers.MakeAlertHandler(internalHandlers.NewSwarmServiceQuery(dockerClient))
 
@@ -130,7 +132,7 @@ func main() {
 	r.HandleFunc("/system/functions", listFunctions).Methods("GET")
 	r.HandleFunc("/system/functions", faasHandlers.DeployFunction).Methods("POST")
 	r.HandleFunc("/system/functions", faasHandlers.DeleteFunction).Methods("DELETE")
-	r.HandleFunc("/system/functions", faasHandlers.UpdateFunction).Methods("UPDATE")
+	r.HandleFunc("/system/functions", faasHandlers.UpdateFunction).Methods("PUT")
 
 	if faasHandlers.QueuedProxy != nil {
 		r.HandleFunc("/async-function/{name:[-a-zA-Z_0-9]+}/", faasHandlers.QueuedProxy).Methods("POST")