diff --git a/pkg/kube/client.go b/pkg/kube/client.go
index 4a387d524decd6e734a9c73ec2c121eec644b990..e897aced649477a775f928c5be4cf340fcae8c9c 100644
--- a/pkg/kube/client.go
+++ b/pkg/kube/client.go
@@ -23,11 +23,12 @@ import (
 	goerrors "errors"
 	"fmt"
 	"io"
+	"k8s.io/apimachinery/pkg/api/meta"
 	"log"
 	"strings"
 	"time"
 
-	jsonpatch "github.com/evanphx/json-patch"
+	"github.com/evanphx/json-patch"
 	appsv1 "k8s.io/api/apps/v1"
 	appsv1beta1 "k8s.io/api/apps/v1beta1"
 	appsv1beta2 "k8s.io/api/apps/v1beta2"
@@ -60,6 +61,8 @@ const MissingGetHeader = "==> MISSING\nKIND\t\tNAME\n"
 // ErrNoObjectsVisited indicates that during a visit operation, no matching objects were found.
 var ErrNoObjectsVisited = goerrors.New("no objects visited")
 
+var metadataAccessor = meta.NewAccessor()
+
 // Client represents a client capable of communicating with the Kubernetes API.
 type Client struct {
 	cmdutil.Factory
@@ -308,6 +311,19 @@ func (c *Client) Update(namespace string, originalReader, targetReader io.Reader
 
 	for _, info := range original.Difference(target) {
 		c.Log("Deleting %q in %s...", info.Name, info.Namespace)
+
+		if err := info.Get(); err != nil {
+			c.Log("Unable to get obj %q, err: %s", info.Name, err)
+		}
+		annotations, err := metadataAccessor.Annotations(info.Object)
+		if err != nil {
+			c.Log("Unable to get annotations on %q, err: %s", info.Name, err)
+		}
+		if annotations != nil && annotations[ResourcePolicyAnno] == KeepPolicy {
+			c.Log("Skipping delete of %q due to annotation [%s=%s]", info.Name, ResourcePolicyAnno, KeepPolicy)
+			continue
+		}
+
 		if err := deleteResource(info); err != nil {
 			c.Log("Failed to delete %q, err: %s", info.Name, err)
 		}
diff --git a/pkg/kube/client_test.go b/pkg/kube/client_test.go
index de33881c8ec02ec645c32eb4c5bddbc8fa147122..aa21b937c522480097aa6009fff281eabf5f4996 100644
--- a/pkg/kube/client_test.go
+++ b/pkg/kube/client_test.go
@@ -151,6 +151,8 @@ func TestUpdate(t *testing.T) {
 				return newResponse(200, &listB.Items[1])
 			case p == "/namespaces/default/pods/squid" && m == "DELETE":
 				return newResponse(200, &listB.Items[1])
+			case p == "/namespaces/default/pods/squid" && m == "GET":
+				return newResponse(200, &listA.Items[2])
 			default:
 				t.Fatalf("unexpected request: %s %s", req.Method, req.URL.Path)
 				return nil, nil
@@ -183,6 +185,7 @@ func TestUpdate(t *testing.T) {
 		"/namespaces/default/pods/otter:GET",
 		"/namespaces/default/pods/dolphin:GET",
 		"/namespaces/default/pods:POST",
+		"/namespaces/default/pods/squid:GET",
 		"/namespaces/default/pods/squid:DELETE",
 	}
 	if len(expectedActions) != len(actions) {
@@ -194,6 +197,18 @@ func TestUpdate(t *testing.T) {
 			t.Errorf("expected %s request got %s", v, actions[k])
 		}
 	}
+
+	// Test resource policy is respected
+	actions = nil
+	listA.Items[2].ObjectMeta.Annotations = map[string]string{ResourcePolicyAnno: KeepPolicy}
+	if err := c.Update(v1.NamespaceDefault, objBody(&listA), objBody(&listB), false, false, 0, false); err != nil {
+		t.Fatal(err)
+	}
+	for _, v := range actions {
+		if v == "/namespaces/default/pods/squid:DELETE" {
+			t.Errorf("should not have deleted squid - it has helm.sh/resource-policy=keep")
+		}
+	}
 }
 
 func TestBuild(t *testing.T) {
diff --git a/pkg/kube/resource_policy.go b/pkg/kube/resource_policy.go
new file mode 100644
index 0000000000000000000000000000000000000000..45cebcba8f0f55add5ee3c177e92dce6884da380
--- /dev/null
+++ b/pkg/kube/resource_policy.go
@@ -0,0 +1,26 @@
+/*
+Copyright The Helm Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package kube
+
+// ResourcePolicyAnno is the annotation name for a resource policy
+const ResourcePolicyAnno = "helm.sh/resource-policy"
+
+// KeepPolicy is the resource policy type for keep
+//
+// This resource policy type allows resources to skip being deleted
+//   during an uninstallRelease action.
+const KeepPolicy = "keep"
diff --git a/pkg/tiller/resource_policy.go b/pkg/tiller/resource_policy.go
index cca2391d85c26a9c5d7337bb954c1ad19661ebc5..aa9c5d2bd01831b58dfe7f1b6c93e783ee700827 100644
--- a/pkg/tiller/resource_policy.go
+++ b/pkg/tiller/resource_policy.go
@@ -24,15 +24,6 @@ import (
 	"k8s.io/helm/pkg/tiller/environment"
 )
 
-// resourcePolicyAnno is the annotation name for a resource policy
-const resourcePolicyAnno = "helm.sh/resource-policy"
-
-// keepPolicy is the resource policy type for keep
-//
-// This resource policy type allows resources to skip being deleted
-//   during an uninstallRelease action.
-const keepPolicy = "keep"
-
 func filterManifestsToKeep(manifests []Manifest) ([]Manifest, []Manifest) {
 	remaining := []Manifest{}
 	keep := []Manifest{}
@@ -43,14 +34,14 @@ func filterManifestsToKeep(manifests []Manifest) ([]Manifest, []Manifest) {
 			continue
 		}
 
-		resourcePolicyType, ok := m.Head.Metadata.Annotations[resourcePolicyAnno]
+		resourcePolicyType, ok := m.Head.Metadata.Annotations[kube.ResourcePolicyAnno]
 		if !ok {
 			remaining = append(remaining, m)
 			continue
 		}
 
 		resourcePolicyType = strings.ToLower(strings.TrimSpace(resourcePolicyType))
-		if resourcePolicyType == keepPolicy {
+		if resourcePolicyType == kube.KeepPolicy {
 			keep = append(keep, m)
 		}