diff --git a/client/client.go b/client/client.go
new file mode 100644
index 0000000000000000000000000000000000000000..dc5caf361dfc4f17754835a7b35d9a1e204e6887
--- /dev/null
+++ b/client/client.go
@@ -0,0 +1,207 @@
+// Copyright 2017 The ksonnet 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 client
+
+import (
+	"fmt"
+	"os"
+	"reflect"
+
+	"github.com/ksonnet/ksonnet/metadata"
+	str "github.com/ksonnet/ksonnet/strings"
+	"github.com/ksonnet/ksonnet/utils"
+	log "github.com/sirupsen/logrus"
+	"github.com/spf13/cobra"
+	"k8s.io/client-go/discovery"
+	"k8s.io/client-go/dynamic"
+	"k8s.io/client-go/tools/clientcmd"
+)
+
+// Config is a wrapper around client-go's ClientConfig
+type Config struct {
+	Overrides    *clientcmd.ConfigOverrides
+	LoadingRules *clientcmd.ClientConfigLoadingRules
+
+	Config clientcmd.ClientConfig
+}
+
+// NewClientConfig initializes a new client.Config with the provided loading rules and overrides.
+func NewClientConfig(overrides clientcmd.ConfigOverrides, loadingRules clientcmd.ClientConfigLoadingRules) *Config {
+	config := clientcmd.NewInteractiveDeferredLoadingClientConfig(&loadingRules, &overrides, os.Stdin)
+	return &Config{Overrides: &overrides, LoadingRules: &loadingRules, Config: config}
+}
+
+// NewDefaultClientConfig initializes a new ClientConfig with default loading rules and no overrides.
+func NewDefaultClientConfig() *Config {
+	overrides := clientcmd.ConfigOverrides{}
+	loadingRules := *clientcmd.NewDefaultClientConfigLoadingRules()
+	loadingRules.DefaultClientConfig = &clientcmd.DefaultClientConfig
+	config := clientcmd.NewInteractiveDeferredLoadingClientConfig(&loadingRules, &overrides, os.Stdin)
+
+	return &Config{Overrides: &overrides, LoadingRules: &loadingRules, Config: config}
+}
+
+// InitClient initializes a new ClientConfig given the specified environment
+// spec and returns the ClientPool, DiscoveryInterface, and namespace.
+func InitClient(env string) (dynamic.ClientPool, discovery.DiscoveryInterface, string, error) {
+	clientConfig := NewDefaultClientConfig()
+	return clientConfig.RestClient(&env)
+}
+
+// Namespace returns the namespace for the provided ClientConfig.
+func (c *Config) Namespace() (string, error) {
+	ns, _, err := c.Config.Namespace()
+	return ns, err
+}
+
+// RestClient returns the ClientPool, DiscoveryInterface, and Namespace based on the environment spec.
+func (c *Config) RestClient(envName *string) (dynamic.ClientPool, discovery.DiscoveryInterface, string, error) {
+	if envName != nil {
+		err := c.overrideCluster(*envName)
+		if err != nil {
+			return nil, nil, "", err
+		}
+	}
+
+	conf, err := c.Config.ClientConfig()
+	if err != nil {
+		return nil, nil, "", err
+	}
+
+	disco, err := discovery.NewDiscoveryClientForConfig(conf)
+	if err != nil {
+		return nil, nil, "", err
+	}
+
+	discoCache := utils.NewMemcachedDiscoveryClient(disco)
+	mapper := discovery.NewDeferredDiscoveryRESTMapper(discoCache, dynamic.VersionInterfaces)
+	pathresolver := dynamic.LegacyAPIPathResolverFunc
+
+	pool := dynamic.NewClientPool(conf, mapper, pathresolver)
+
+	ns, err := c.Namespace()
+	if err != nil {
+		return nil, nil, "", err
+	}
+
+	return pool, discoCache, ns, nil
+}
+
+// BindClientGoFlags binds client-go flags to the specified command. This way
+// any overrides to client-go flags will automatically update the client config.
+func (c *Config) BindClientGoFlags(cmd *cobra.Command) {
+	cmd.PersistentFlags().StringVar(&c.LoadingRules.ExplicitPath, "kubeconfig", "", "Path to a kubeconfig file. Alternative to env var $KUBECONFIG.")
+	clientcmd.BindOverrideFlags(c.Overrides, cmd.PersistentFlags(), clientcmd.RecommendedConfigOverrideFlags(""))
+}
+
+// ResolveContext returns the server and namespace of the cluster at the
+// provided context. If the context string is empty, the "default" context is
+// used.
+func (c *Config) ResolveContext(context string) (server, namespace string, err error) {
+	rawConfig, err := c.Config.RawConfig()
+	if err != nil {
+		return "", "", err
+	}
+
+	// use the default context where context is empty
+	if context == "" {
+		if rawConfig.CurrentContext == "" && len(rawConfig.Clusters) == 0 {
+			// User likely does not have a kubeconfig file.
+			return "", "", fmt.Errorf("No current context found. Make sure a kubeconfig file is present")
+		}
+		// Note: "" is a valid rawConfig.CurrentContext
+		context = rawConfig.CurrentContext
+	}
+
+	ctx := rawConfig.Contexts[context]
+	if ctx == nil {
+		return "", "", fmt.Errorf("context '%s' does not exist in the kubeconfig file", context)
+	}
+
+	log.Infof("Using context '%s' from the kubeconfig file specified at the environment variable $KUBECONFIG", context)
+	cluster, exists := rawConfig.Clusters[ctx.Cluster]
+	if !exists {
+		return "", "", fmt.Errorf("No cluster with name '%s' exists", ctx.Cluster)
+	}
+
+	return cluster.Server, ctx.Namespace, nil
+}
+
+// overrideCluster ensures that the server specified in the environment is
+// associated in the user's kubeconfig file during deployment to a ksonnet
+// environment. We will error out if it is not.
+//
+// If the environment server the user is attempting to deploy to is not the current
+// kubeconfig context, we must manually override the client-go --cluster flag
+// to ensure we are deploying to the correct cluster.
+func (c *Config) overrideCluster(envName string) error {
+	cwd, err := os.Getwd()
+	if err != nil {
+		return err
+	}
+
+	metadataManager, err := metadata.Find(cwd)
+	if err != nil {
+		return err
+	}
+
+	rawConfig, err := c.Config.RawConfig()
+	if err != nil {
+		return err
+	}
+
+	var servers = make(map[string]string)
+	for name, cluster := range rawConfig.Clusters {
+		server, err := str.NormalizeURL(cluster.Server)
+		if err != nil {
+			return err
+		}
+
+		servers[server] = name
+	}
+
+	//
+	// check to ensure that the environment we are trying to deploy to is
+	// created, and that the server is located in kubeconfig.
+	//
+
+	log.Debugf("Validating deployment at '%s' with server '%v'", envName, reflect.ValueOf(servers).MapKeys())
+	env, err := metadataManager.GetEnvironment(envName)
+	if err != nil {
+		return err
+	}
+
+	// TODO support multi-cluster deployment.
+	server, err := str.NormalizeURL(env.Destinations[0].Server)
+	if err != nil {
+		return err
+	}
+
+	if _, ok := servers[server]; ok {
+		clusterName := servers[server]
+		if c.Overrides.Context.Cluster == "" {
+			log.Debugf("Overwriting --cluster flag with '%s'", clusterName)
+			c.Overrides.Context.Cluster = clusterName
+		}
+		if c.Overrides.Context.Namespace == "" {
+			log.Debugf("Overwriting --namespace flag with '%s'", env.Destinations[0].Namespace)
+			c.Overrides.Context.Namespace = env.Destinations[0].Namespace
+		}
+		return nil
+	}
+
+	return fmt.Errorf("Attempting to deploy to environment '%s' at '%s', but cannot locate a server at that address", envName, env.Destinations[0].Server)
+}
diff --git a/cmd/apply.go b/cmd/apply.go
index e04c08fe0d4dae4727c553006c8468e2e565ee94..86504b0340fa5723b69531e0565fa8989bb8ee83 100644
--- a/cmd/apply.go
+++ b/cmd/apply.go
@@ -21,9 +21,14 @@ import (
 
 	"github.com/spf13/cobra"
 
+	"github.com/ksonnet/ksonnet/client"
 	"github.com/ksonnet/ksonnet/pkg/kubecfg"
 )
 
+var (
+	applyClientConfig *client.Config
+)
+
 const (
 	flagCreate = "create"
 	flagSkipGc = "skip-gc"
@@ -52,7 +57,8 @@ func init() {
 	RootCmd.AddCommand(applyCmd)
 
 	addEnvCmdFlags(applyCmd)
-	bindClientGoFlags(applyCmd)
+	applyClientConfig = client.NewDefaultClientConfig()
+	applyClientConfig.BindClientGoFlags(applyCmd)
 	bindJsonnetFlags(applyCmd)
 	applyCmd.PersistentFlags().Bool(flagCreate, true, "Option to create resources if they do not already exist on the cluster")
 	applyCmd.PersistentFlags().Bool(flagSkipGc, false, "Option to skip garbage collection, even with --"+flagGcTag+" specified")
@@ -94,6 +100,9 @@ var applyCmd = &cobra.Command{
 			return err
 		}
 
+		c.ClientConfig = applyClientConfig
+		c.Env = env
+
 		componentNames, err := flags.GetStringArray(flagComponent)
 		if err != nil {
 			return err
@@ -104,16 +113,6 @@ var applyCmd = &cobra.Command{
 			return err
 		}
 
-		c.ClientPool, c.Discovery, err = restClientPool(cmd, &env)
-		if err != nil {
-			return err
-		}
-
-		c.Namespace, err = namespace()
-		if err != nil {
-			return err
-		}
-
 		te := newCmdObjExpander(cmdObjExpanderConfig{
 			cmd:        cmd,
 			env:        env,
diff --git a/cmd/delete.go b/cmd/delete.go
index 1f91e4314fe225c8cf9337330ac7a9828e6e363f..e5123d4bcd56db0764e3c8cee756aacd89b3daf8 100644
--- a/cmd/delete.go
+++ b/cmd/delete.go
@@ -21,6 +21,7 @@ import (
 
 	"github.com/spf13/cobra"
 
+	"github.com/ksonnet/ksonnet/client"
 	"github.com/ksonnet/ksonnet/pkg/kubecfg"
 )
 
@@ -29,10 +30,15 @@ const (
 	deleteShortDesc = "Remove component-specified Kubernetes resources from remote clusters"
 )
 
+var (
+	deleteClientConfig *client.Config
+)
+
 func init() {
 	RootCmd.AddCommand(deleteCmd)
 	addEnvCmdFlags(deleteCmd)
-	bindClientGoFlags(deleteCmd)
+	deleteClientConfig = client.NewDefaultClientConfig()
+	deleteClientConfig.BindClientGoFlags(deleteCmd)
 	bindJsonnetFlags(deleteCmd)
 	deleteCmd.PersistentFlags().Int64(flagGracePeriod, -1, "Number of seconds given to resources to terminate gracefully. A negative value is ignored")
 }
@@ -66,15 +72,8 @@ var deleteCmd = &cobra.Command{
 			return err
 		}
 
-		c.ClientPool, c.Discovery, err = restClientPool(cmd, &env)
-		if err != nil {
-			return err
-		}
-
-		c.Namespace, err = namespace()
-		if err != nil {
-			return err
-		}
+		c.ClientConfig = deleteClientConfig
+		c.Env = env
 
 		te := newCmdObjExpander(cmdObjExpanderConfig{
 			cmd:        cmd,
diff --git a/cmd/diff.go b/cmd/diff.go
index 13bf1b8128c4bd27faac1c9f44f953eb71bb8e7b..7c2a6e14692d76fb5ff5daa3c5e47f67db59b270 100644
--- a/cmd/diff.go
+++ b/cmd/diff.go
@@ -20,14 +20,11 @@ import (
 	"os"
 	"strings"
 
-	"k8s.io/client-go/discovery"
-	"k8s.io/client-go/dynamic"
-
 	"github.com/spf13/afero"
 	"github.com/spf13/cobra"
 	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
-	"k8s.io/client-go/tools/clientcmd"
 
+	"github.com/ksonnet/ksonnet/client"
 	"github.com/ksonnet/ksonnet/metadata"
 	"github.com/ksonnet/ksonnet/pkg/kubecfg"
 )
@@ -206,12 +203,7 @@ func initDiffSingleEnv(fs afero.Fs, env, diffStrategy string, files []string, cm
 		return nil, err
 	}
 
-	c.Client.ClientPool, c.Client.Discovery, err = restClientPool(cmd, &env)
-	if err != nil {
-		return nil, err
-	}
-
-	c.Client.Namespace, err = namespace()
+	c.Client.ClientPool, c.Client.Discovery, c.Client.Namespace, err = client.InitClient(env)
 	if err != nil {
 		return nil, err
 	}
@@ -263,11 +255,12 @@ func initDiffRemotesCmd(fs afero.Fs, env1, env2, diffStrategy string, cmd *cobra
 		return nil, err
 	}
 
-	c.ClientA.ClientPool, c.ClientA.Discovery, c.ClientA.Namespace, err = setupClientConfig(&c.ClientA.Name, cmd)
+	c.ClientA.ClientPool, c.ClientA.Discovery, c.ClientA.Namespace, err = client.InitClient(c.ClientA.Name)
 	if err != nil {
 		return nil, err
 	}
-	c.ClientB.ClientPool, c.ClientB.Discovery, c.ClientB.Namespace, err = setupClientConfig(&c.ClientB.Name, cmd)
+
+	c.ClientB.ClientPool, c.ClientB.Discovery, c.ClientB.Namespace, err = client.InitClient(c.ClientB.Name)
 	if err != nil {
 		return nil, err
 	}
@@ -287,7 +280,7 @@ func initDiffRemoteCmd(fs afero.Fs, localEnv, remoteEnv, diffStrategy string, cm
 		return nil, err
 	}
 
-	c.Client.ClientPool, c.Client.Discovery, c.Client.Namespace, err = setupClientConfig(&remoteEnv, cmd)
+	c.Client.ClientPool, c.Client.Discovery, c.Client.Namespace, err = client.InitClient(remoteEnv)
 	if err != nil {
 		return nil, err
 	}
@@ -295,25 +288,6 @@ func initDiffRemoteCmd(fs afero.Fs, localEnv, remoteEnv, diffStrategy string, cm
 	return &c, nil
 }
 
-func setupClientConfig(env *string, cmd *cobra.Command) (dynamic.ClientPool, discovery.DiscoveryInterface, string, error) {
-	overrides := &clientcmd.ConfigOverrides{}
-	loadingRules := *clientcmd.NewDefaultClientConfigLoadingRules()
-	loadingRules.DefaultClientConfig = &clientcmd.DefaultClientConfig
-	config := clientcmd.NewInteractiveDeferredLoadingClientConfig(&loadingRules, overrides, os.Stdin)
-
-	clientPool, discovery, err := restClient(cmd, env, config, overrides)
-	if err != nil {
-		return nil, nil, "", err
-	}
-
-	namespace, err := namespaceFor(config, overrides)
-	if err != nil {
-		return nil, nil, "", err
-	}
-
-	return clientPool, discovery, namespace, nil
-}
-
 // expandEnvObjs finds and expands templates for an environment
 func expandEnvObjs(fs afero.Fs, cmd *cobra.Command, env string, manager metadata.Manager) ([]*unstructured.Unstructured, error) {
 	expander, err := newExpander(fs, cmd)
diff --git a/cmd/env.go b/cmd/env.go
index ca74f4986315c9f57b855cbe44e6641974705e77..830a61803c2fdeb3b02463eed06ae2d7f3828195 100644
--- a/cmd/env.go
+++ b/cmd/env.go
@@ -23,6 +23,7 @@ import (
 	"github.com/spf13/cobra"
 	"github.com/spf13/pflag"
 
+	"github.com/ksonnet/ksonnet/client"
 	"github.com/ksonnet/ksonnet/metadata"
 	"github.com/ksonnet/ksonnet/pkg/kubecfg"
 )
@@ -34,16 +35,20 @@ const (
 	flagEnvContext   = "context"
 )
 
-var envShortDesc = map[string]string{
-	"add":  "Add a new environment to a ksonnet application",
-	"list": "List all environments in a ksonnet application",
-	"rm":   "Delete an environment from a ksonnet application",
-	"set":  "Set environment-specific fields (name, namespace, server)",
-}
+var (
+	envClientConfig *client.Config
+	envShortDesc    = map[string]string{
+		"add":  "Add a new environment to a ksonnet application",
+		"list": "List all environments in a ksonnet application",
+		"rm":   "Delete an environment from a ksonnet application",
+		"set":  "Set environment-specific fields (name, namespace, server)",
+	}
+)
 
 func init() {
 	RootCmd.AddCommand(envCmd)
-	bindClientGoFlags(envCmd)
+	envClientConfig = client.NewDefaultClientConfig()
+	envClientConfig.BindClientGoFlags(envCmd)
 
 	envCmd.AddCommand(envAddCmd)
 	envCmd.AddCommand(envRmCmd)
@@ -368,7 +373,7 @@ func resolveEnvFlags(flags *pflag.FlagSet) (string, string, error) {
 
 	if server == "" {
 		// server is not provided -- use the context.
-		server, defaultNamespace, err = resolveContext(context)
+		server, defaultNamespace, err = envClientConfig.ResolveContext(context)
 		if err != nil {
 			return "", "", err
 		}
diff --git a/cmd/init.go b/cmd/init.go
index b8361112e8ae4f5df52b37754b01b78e34a444ba..52bd3dfd9f3ec4cbf344d36a30f37d9a59ba7194 100644
--- a/cmd/init.go
+++ b/cmd/init.go
@@ -21,6 +21,7 @@ import (
 	"os"
 	"path/filepath"
 
+	"github.com/ksonnet/ksonnet/client"
 	"github.com/ksonnet/ksonnet/pkg/kubecfg"
 	log "github.com/sirupsen/logrus"
 	"github.com/spf13/cobra"
@@ -31,13 +32,18 @@ const (
 	initShortDesc = "Initialize a ksonnet application"
 )
 
+var (
+	initClientConfig *client.Config
+)
+
 func init() {
 	RootCmd.AddCommand(initCmd)
 	// TODO: We need to make this default to checking the `kubeconfig` file.
 	initCmd.PersistentFlags().String(flagAPISpec, "version:v1.7.0",
 		"Manually specified Kubernetes API version. The corresponding OpenAPI spec is used to generate ksonnet's Kubernetes libraries")
 
-	bindClientGoFlags(initCmd)
+	initClientConfig = client.NewDefaultClientConfig()
+	initClientConfig.BindClientGoFlags(initCmd)
 	initCmd.Flags().String(flagInitDir, "", "Ksonnet application directory")
 }
 
diff --git a/cmd/root.go b/cmd/root.go
index 4311834cbac1f56f7455566053e8bd53801c1626..06faf1bd0109367a4747dc342f93381876c72f77 100644
--- a/cmd/root.go
+++ b/cmd/root.go
@@ -24,25 +24,19 @@ import (
 	"os"
 	"path"
 	"path/filepath"
-	"reflect"
 	"strings"
 
 	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
 
+	"github.com/ksonnet/ksonnet/component"
+	"github.com/ksonnet/ksonnet/metadata"
+	str "github.com/ksonnet/ksonnet/strings"
+	"github.com/ksonnet/ksonnet/template"
 	"github.com/pkg/errors"
 	log "github.com/sirupsen/logrus"
 	"github.com/spf13/afero"
 	"github.com/spf13/cobra"
 	"golang.org/x/crypto/ssh/terminal"
-	"k8s.io/client-go/discovery"
-	"k8s.io/client-go/dynamic"
-	"k8s.io/client-go/tools/clientcmd"
-
-	"github.com/ksonnet/ksonnet/component"
-	"github.com/ksonnet/ksonnet/metadata"
-	str "github.com/ksonnet/ksonnet/strings"
-	"github.com/ksonnet/ksonnet/template"
-	"github.com/ksonnet/ksonnet/utils"
 
 	// Register auth plugins
 	_ "k8s.io/client-go/plugin/pkg/client/auth"
@@ -66,22 +60,13 @@ const (
 )
 
 var (
-	clientConfig clientcmd.ClientConfig
-	overrides    clientcmd.ConfigOverrides
-	loadingRules clientcmd.ClientConfigLoadingRules
-	appFs        afero.Fs
+	appFs afero.Fs
 )
 
 func init() {
 	appFs = afero.NewOsFs()
 
 	RootCmd.PersistentFlags().CountP(flagVerbose, "v", "Increase verbosity. May be given multiple times.")
-
-	// The "usual" clientcmd/kubectl flags
-	loadingRules = *clientcmd.NewDefaultClientConfigLoadingRules()
-	loadingRules.DefaultClientConfig = &clientcmd.DefaultClientConfig
-	clientConfig = clientcmd.NewInteractiveDeferredLoadingClientConfig(&loadingRules, &overrides, os.Stdin)
-
 	RootCmd.PersistentFlags().Set("logtostderr", "true")
 }
 
@@ -95,13 +80,6 @@ func bindJsonnetFlags(cmd *cobra.Command) {
 	cmd.PersistentFlags().String(flagResolvFail, "warn", "Action when resolveImage fails. One of ignore,warn,error")
 }
 
-func bindClientGoFlags(cmd *cobra.Command) {
-	kflags := clientcmd.RecommendedConfigOverrideFlags("")
-	ep := &loadingRules.ExplicitPath
-	cmd.PersistentFlags().StringVar(ep, "kubeconfig", "", "Path to a kubeconfig file. Alternative to env var $KUBECONFIG.")
-	clientcmd.BindOverrideFlags(&overrides, cmd.PersistentFlags(), kflags)
-}
-
 // RootCmd is the root of cobra subcommand tree
 var RootCmd = &cobra.Command{
 	Use:   "ks",
@@ -133,53 +111,6 @@ application configuration to remote clusters.
 	},
 }
 
-// clientConfig.Namespace() is broken in client-go 3.0:
-// namespace in config erroneously overrides explicit --namespace
-func namespace() (string, error) {
-	return namespaceFor(clientConfig, &overrides)
-}
-
-func namespaceFor(c clientcmd.ClientConfig, overrides *clientcmd.ConfigOverrides) (string, error) {
-	if overrides.Context.Namespace != "" {
-		return overrides.Context.Namespace, nil
-	}
-	ns, _, err := clientConfig.Namespace()
-	return ns, err
-}
-
-// resolveContext returns the server and namespace of the cluster at the
-// provided context. If the context string is empty, the default context is
-// used.
-func resolveContext(context string) (server, namespace string, err error) {
-	rawConfig, err := clientConfig.RawConfig()
-	if err != nil {
-		return "", "", err
-	}
-
-	// use the default context where context is empty
-	if context == "" {
-		if rawConfig.CurrentContext == "" && len(rawConfig.Clusters) == 0 {
-			// User likely does not have a kubeconfig file.
-			return "", "", fmt.Errorf("No current context found. Make sure a kubeconfig file is present")
-		}
-		// Note: "" is a valid rawConfig.CurrentContext
-		context = rawConfig.CurrentContext
-	}
-
-	ctx := rawConfig.Contexts[context]
-	if ctx == nil {
-		return "", "", fmt.Errorf("context '%s' does not exist in the kubeconfig file", context)
-	}
-
-	log.Infof("Using context '%s' from the kubeconfig file specified at the environment variable $KUBECONFIG", context)
-	cluster, exists := rawConfig.Clusters[ctx.Cluster]
-	if !exists {
-		return "", "", fmt.Errorf("No cluster with name '%s' exists", ctx.Cluster)
-	}
-
-	return cluster.Server, ctx.Namespace, nil
-}
-
 func logLevel(verbosity int) log.Level {
 	switch verbosity {
 	case 0:
@@ -286,108 +217,12 @@ func dumpJSON(v interface{}) string {
 	return string(buf.Bytes())
 }
 
-func restClient(cmd *cobra.Command, envName *string, config clientcmd.ClientConfig, overrides *clientcmd.ConfigOverrides) (dynamic.ClientPool, discovery.DiscoveryInterface, error) {
-	if envName != nil {
-		err := overrideCluster(*envName, config, overrides)
-		if err != nil {
-			return nil, nil, err
-		}
-	}
-
-	conf, err := config.ClientConfig()
-	if err != nil {
-		return nil, nil, err
-	}
-
-	disco, err := discovery.NewDiscoveryClientForConfig(conf)
-	if err != nil {
-		return nil, nil, err
-	}
-
-	discoCache := utils.NewMemcachedDiscoveryClient(disco)
-	mapper := discovery.NewDeferredDiscoveryRESTMapper(discoCache, dynamic.VersionInterfaces)
-	pathresolver := dynamic.LegacyAPIPathResolverFunc
-
-	pool := dynamic.NewClientPool(conf, mapper, pathresolver)
-	return pool, discoCache, nil
-}
-
-func restClientPool(cmd *cobra.Command, envName *string) (dynamic.ClientPool, discovery.DiscoveryInterface, error) {
-	return restClient(cmd, envName, clientConfig, &overrides)
-}
-
 // addEnvCmdFlags adds the flags that are common to the family of commands
 // whose form is `[<env>|-f <file-name>]`, e.g., `apply` and `delete`.
 func addEnvCmdFlags(cmd *cobra.Command) {
 	cmd.PersistentFlags().StringArrayP(flagComponent, flagComponentShort, nil, "Name of a specific component (multiple -c flags accepted, allows YAML, JSON, and Jsonnet)")
 }
 
-// overrideCluster ensures that the server specified in the environment is
-// associated in the user's kubeconfig file during deployment to a ksonnet
-// environment. We will error out if it is not.
-//
-// If the environment server the user is attempting to deploy to is not the current
-// kubeconfig context, we must manually override the client-go --cluster flag
-// to ensure we are deploying to the correct cluster.
-func overrideCluster(envName string, clientConfig clientcmd.ClientConfig, overrides *clientcmd.ConfigOverrides) error {
-	cwd, err := os.Getwd()
-	if err != nil {
-		return err
-	}
-
-	metadataManager, err := metadata.Find(cwd)
-	if err != nil {
-		return err
-	}
-
-	rawConfig, err := clientConfig.RawConfig()
-	if err != nil {
-		return err
-	}
-
-	var servers = make(map[string]string)
-	for name, cluster := range rawConfig.Clusters {
-		server, err := str.NormalizeURL(cluster.Server)
-		if err != nil {
-			return err
-		}
-
-		servers[server] = name
-	}
-
-	//
-	// check to ensure that the environment we are trying to deploy to is
-	// created, and that the server is located in kubeconfig.
-	//
-
-	log.Debugf("Validating deployment at '%s' with server '%v'", envName, reflect.ValueOf(servers).MapKeys())
-	env, err := metadataManager.GetEnvironment(envName)
-	if err != nil {
-		return err
-	}
-
-	// TODO support multi-cluster deployment.
-	server, err := str.NormalizeURL(env.Destinations[0].Server)
-	if err != nil {
-		return err
-	}
-
-	if _, ok := servers[server]; ok {
-		clusterName := servers[server]
-		if overrides.Context.Cluster == "" {
-			log.Debugf("Overwriting --cluster flag with '%s'", clusterName)
-			overrides.Context.Cluster = clusterName
-		}
-		if overrides.Context.Namespace == "" {
-			log.Debugf("Overwriting --namespace flag with '%s'", env.Destinations[0].Namespace)
-			overrides.Context.Namespace = env.Destinations[0].Namespace
-		}
-		return nil
-	}
-
-	return fmt.Errorf("Attempting to deploy to environment '%s' at '%s', but cannot locate a server at that address", envName, env.Destinations[0].Server)
-}
-
 type cmdObjExpanderConfig struct {
 	fs         afero.Fs
 	cmd        *cobra.Command
diff --git a/cmd/validate.go b/cmd/validate.go
index f84b4cb6bd892ddf3b06a6408d42bfc1c3d2b64e..73fdbe02e165d2f0db67e1aedcf8b431965e3e42 100644
--- a/cmd/validate.go
+++ b/cmd/validate.go
@@ -21,6 +21,7 @@ import (
 
 	"github.com/spf13/cobra"
 
+	"github.com/ksonnet/ksonnet/client"
 	"github.com/ksonnet/ksonnet/pkg/kubecfg"
 )
 
@@ -28,11 +29,16 @@ const (
 	valShortDesc = "Check generated component manifests against the server's API"
 )
 
+var (
+	validateClientConfig *client.Config
+)
+
 func init() {
 	RootCmd.AddCommand(validateCmd)
 	addEnvCmdFlags(validateCmd)
 	bindJsonnetFlags(validateCmd)
-	bindClientGoFlags(validateCmd)
+	validateClientConfig = client.NewDefaultClientConfig()
+	validateClientConfig.BindClientGoFlags(validateCmd)
 }
 
 var validateCmd = &cobra.Command{
@@ -59,10 +65,8 @@ var validateCmd = &cobra.Command{
 			return err
 		}
 
-		_, c.Discovery, err = restClientPool(cmd, nil)
-		if err != nil {
-			return err
-		}
+		c.ClientConfig = validateClientConfig
+		c.Env = env
 
 		te := newCmdObjExpander(cmdObjExpanderConfig{
 			cmd:        cmd,
diff --git a/pkg/kubecfg/apply.go b/pkg/kubecfg/apply.go
index df6b3dcf0de95a0250a1695f2a4ae9abe21bcf37..6e49db65ff171f87f52ed30246c51f8730024c08 100644
--- a/pkg/kubecfg/apply.go
+++ b/pkg/kubecfg/apply.go
@@ -5,6 +5,7 @@ import (
 	"fmt"
 	"sort"
 
+	"github.com/ksonnet/ksonnet/client"
 	"github.com/ksonnet/ksonnet/utils"
 	log "github.com/sirupsen/logrus"
 	"k8s.io/apimachinery/pkg/api/errors"
@@ -39,18 +40,21 @@ const (
 
 // ApplyCmd represents the apply subcommand
 type ApplyCmd struct {
-	ClientPool dynamic.ClientPool
-	Discovery  discovery.DiscoveryInterface
-	Namespace  string
-
-	Create bool
-	GcTag  string
-	SkipGc bool
-	DryRun bool
+	ClientConfig *client.Config
+	Env          string
+	Create       bool
+	GcTag        string
+	SkipGc       bool
+	DryRun       bool
 }
 
 // Run applies the components to the designated environment cluster.
 func (c ApplyCmd) Run(apiObjects []*unstructured.Unstructured, wd string) error {
+	clientPool, discovery, namespace, err := c.ClientConfig.RestClient(&c.Env)
+	if err != nil {
+		return err
+	}
+
 	dryRunText := ""
 	if c.DryRun {
 		dryRunText = " (dry-run)"
@@ -65,10 +69,10 @@ func (c ApplyCmd) Run(apiObjects []*unstructured.Unstructured, wd string) error
 			utils.SetMetaDataAnnotation(obj, AnnotationGcTag, c.GcTag)
 		}
 
-		desc := fmt.Sprintf("%s %s", utils.ResourceNameFor(c.Discovery, obj), utils.FqName(obj))
+		desc := fmt.Sprintf("%s %s", utils.ResourceNameFor(discovery, obj), utils.FqName(obj))
 		log.Info("Updating ", desc, dryRunText)
 
-		rc, err := utils.ClientForResource(c.ClientPool, c.Discovery, obj, c.Namespace)
+		rc, err := utils.ClientForResource(clientPool, discovery, obj, namespace)
 		if err != nil {
 			return err
 		}
@@ -110,23 +114,23 @@ func (c ApplyCmd) Run(apiObjects []*unstructured.Unstructured, wd string) error
 	}
 
 	if c.GcTag != "" && !c.SkipGc {
-		version, err := utils.FetchVersion(c.Discovery)
+		version, err := utils.FetchVersion(discovery)
 		if err != nil {
 			return err
 		}
 
-		err = walkObjects(c.ClientPool, c.Discovery, metav1.ListOptions{}, func(o runtime.Object) error {
+		err = walkObjects(clientPool, discovery, metav1.ListOptions{}, func(o runtime.Object) error {
 			meta, err := meta.Accessor(o)
 			if err != nil {
 				return err
 			}
 			gvk := o.GetObjectKind().GroupVersionKind()
-			desc := fmt.Sprintf("%s %s (%s)", utils.ResourceNameFor(c.Discovery, o), utils.FqName(meta), gvk.GroupVersion())
+			desc := fmt.Sprintf("%s %s (%s)", utils.ResourceNameFor(discovery, o), utils.FqName(meta), gvk.GroupVersion())
 			log.Debugf("Considering %v for gc", desc)
 			if eligibleForGc(meta, c.GcTag) && !seenUids.Has(string(meta.GetUID())) {
 				log.Info("Garbage collecting ", desc, dryRunText)
 				if !c.DryRun {
-					err := gcDelete(c.ClientPool, c.Discovery, &version, o)
+					err := gcDelete(clientPool, discovery, &version, o)
 					if err != nil {
 						return err
 					}
diff --git a/pkg/kubecfg/delete.go b/pkg/kubecfg/delete.go
index b76841fc23983e727f738a2b0a9aadf45de0c2d5..d1f68bf333082b23794300668322a8a6c404b2cc 100644
--- a/pkg/kubecfg/delete.go
+++ b/pkg/kubecfg/delete.go
@@ -23,23 +23,25 @@ import (
 	"k8s.io/apimachinery/pkg/api/errors"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
-	"k8s.io/client-go/discovery"
-	"k8s.io/client-go/dynamic"
 
+	"github.com/ksonnet/ksonnet/client"
 	"github.com/ksonnet/ksonnet/utils"
 )
 
 // DeleteCmd represents the delete subcommand
 type DeleteCmd struct {
-	ClientPool dynamic.ClientPool
-	Discovery  discovery.DiscoveryInterface
-	Namespace  string
-
-	GracePeriod int64
+	ClientConfig *client.Config
+	Env          string
+	GracePeriod  int64
 }
 
 func (c DeleteCmd) Run(apiObjects []*unstructured.Unstructured) error {
-	version, err := utils.FetchVersion(c.Discovery)
+	clientPool, discovery, namespace, err := c.ClientConfig.RestClient(&c.Env)
+	if err != nil {
+		return err
+	}
+
+	version, err := utils.FetchVersion(discovery)
 	if err != nil {
 		return err
 	}
@@ -61,10 +63,10 @@ func (c DeleteCmd) Run(apiObjects []*unstructured.Unstructured) error {
 	}
 
 	for _, obj := range apiObjects {
-		desc := fmt.Sprintf("%s %s", utils.ResourceNameFor(c.Discovery, obj), utils.FqName(obj))
+		desc := fmt.Sprintf("%s %s", utils.ResourceNameFor(discovery, obj), utils.FqName(obj))
 		log.Info("Deleting ", desc)
 
-		client, err := utils.ClientForResource(c.ClientPool, c.Discovery, obj, c.Namespace)
+		client, err := utils.ClientForResource(clientPool, discovery, obj, namespace)
 		if err != nil {
 			return err
 		}
diff --git a/pkg/kubecfg/validate.go b/pkg/kubecfg/validate.go
index 4ef32dc04c7a8f5dc7aa2a4ea2cac976e587d476..275f780fa125925a83ac71a3aaf730445a2c66f7 100644
--- a/pkg/kubecfg/validate.go
+++ b/pkg/kubecfg/validate.go
@@ -21,26 +21,31 @@ import (
 
 	log "github.com/sirupsen/logrus"
 	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
-	"k8s.io/client-go/discovery"
 
+	"github.com/ksonnet/ksonnet/client"
 	"github.com/ksonnet/ksonnet/utils"
 )
 
 // ValidateCmd represents the validate subcommand
 type ValidateCmd struct {
-	Discovery discovery.DiscoveryInterface
+	ClientConfig *client.Config
+	Env          string
 }
 
 func (c ValidateCmd) Run(apiObjects []*unstructured.Unstructured, out io.Writer) error {
+	_, discovery, _, err := client.InitClient(c.Env)
+	if err != nil {
+		return err
+	}
 	hasError := false
 
 	for _, obj := range apiObjects {
-		desc := fmt.Sprintf("%s %s", utils.ResourceNameFor(c.Discovery, obj), utils.FqName(obj))
+		desc := fmt.Sprintf("%s %s", utils.ResourceNameFor(discovery, obj), utils.FqName(obj))
 		log.Info("Validating ", desc)
 
 		var allErrs []error
 
-		schema, err := utils.NewSwaggerSchemaFor(c.Discovery, obj.GroupVersionKind().GroupVersion())
+		schema, err := utils.NewSwaggerSchemaFor(discovery, obj.GroupVersionKind().GroupVersion())
 		if err != nil {
 			allErrs = append(allErrs, fmt.Errorf("Unable to fetch schema: %v", err))
 		} else {