diff --git a/cmd/root.go b/cmd/root.go
index 21bba34b394aef78d07e2cdfdf5ebf333e86b95b..372f8875ea8647e2fa26b36e16b315929d4678e6 100644
--- a/cmd/root.go
+++ b/cmd/root.go
@@ -59,8 +59,6 @@ const (
 	// environment or the -f flag.
 	flagFile      = "file"
 	flagFileShort = "f"
-
-	componentsExtCodeKey = "__ksonnet/components"
 )
 
 var clientConfig clientcmd.ClientConfig
@@ -372,16 +370,17 @@ func expandEnvCmdObjs(cmd *cobra.Command, envSpec *envSpec, cwd metadata.AbsPath
 			return nil, err
 		}
 
-		libPath, envLibPath := manager.LibPaths(*envSpec.env)
+		libPath, envLibPath, envComponentPath := manager.LibPaths(*envSpec.env)
 		expander.FlagJpath = append([]string{string(libPath), string(envLibPath)}, expander.FlagJpath...)
 
 		if !filesPresent {
-			fileNames, err = manager.ComponentPaths()
+			componentPaths, err := manager.ComponentPaths()
 			if err != nil {
 				return nil, err
 			}
-			baseObjExtCode := fmt.Sprintf("%s=%s", componentsExtCodeKey, constructBaseObj(fileNames))
-			expander.ExtCodes = append([]string{baseObjExtCode})
+			baseObjExtCode := fmt.Sprintf("%s=%s", metadata.ComponentsExtCodeKey, constructBaseObj(componentPaths))
+			expander.ExtCodes = append([]string{baseObjExtCode}, expander.ExtCodes...)
+			fileNames = []string{string(envComponentPath)}
 		}
 	}
 
diff --git a/metadata/environment.go b/metadata/environment.go
index e9243996e26e66b3f79b357f9d8272e0410f9c0d..850cbc3bfb384f44c773e190878b44d8018deca8 100644
--- a/metadata/environment.go
+++ b/metadata/environment.go
@@ -16,9 +16,11 @@
 package metadata
 
 import (
+	"bytes"
 	"encoding/json"
 	"fmt"
 	"os"
+	"path"
 	"path/filepath"
 	"strings"
 
@@ -30,7 +32,8 @@ import (
 )
 
 const (
-	defaultEnvName = "default"
+	defaultEnvName  = "default"
+	metadataDirName = ".metadata"
 
 	schemaFilename        = "swagger.json"
 	extensionsLibFilename = "k.libsonnet"
@@ -87,11 +90,17 @@ func (m *manager) createEnvironment(name, uri string, extensionsLibData, k8sLibD
 		return err
 	}
 
+	metadataPath := appendToAbsPath(envPath, metadataDirName)
+	err = m.appFS.MkdirAll(string(metadataPath), defaultFolderPermissions)
+	if err != nil {
+		return err
+	}
+
 	log.Infof("Generating environment metadata at path '%s'", envPath)
 
 	// Generate the schema file.
 	log.Debugf("Generating '%s', length: %d", schemaFilename, len(specData))
-	schemaPath := appendToAbsPath(envPath, schemaFilename)
+	schemaPath := appendToAbsPath(metadataPath, schemaFilename)
 	err = afero.WriteFile(m.appFS, string(schemaPath), specData, defaultFilePermissions)
 	if err != nil {
 		log.Debugf("Failed to write '%s'", schemaFilename)
@@ -99,7 +108,7 @@ func (m *manager) createEnvironment(name, uri string, extensionsLibData, k8sLibD
 	}
 
 	log.Debugf("Generating '%s', length: %d", k8sLibFilename, len(k8sLibData))
-	k8sLibPath := appendToAbsPath(envPath, k8sLibFilename)
+	k8sLibPath := appendToAbsPath(metadataPath, k8sLibFilename)
 	err = afero.WriteFile(m.appFS, string(k8sLibPath), k8sLibData, defaultFilePermissions)
 	if err != nil {
 		log.Debugf("Failed to write '%s'", k8sLibFilename)
@@ -107,13 +116,24 @@ func (m *manager) createEnvironment(name, uri string, extensionsLibData, k8sLibD
 	}
 
 	log.Debugf("Generating '%s', length: %d", extensionsLibFilename, len(extensionsLibData))
-	extensionsLibPath := appendToAbsPath(envPath, extensionsLibFilename)
+	extensionsLibPath := appendToAbsPath(metadataPath, extensionsLibFilename)
 	err = afero.WriteFile(m.appFS, string(extensionsLibPath), extensionsLibData, defaultFilePermissions)
 	if err != nil {
 		log.Debugf("Failed to write '%s'", extensionsLibFilename)
 		return err
 	}
 
+	// Generate the environment .jsonnet file
+	overrideFileName := path.Base(name) + ".jsonnet"
+	overrideData := m.generateOverrideData()
+	log.Debugf("Generating '%s', length: %d", overrideFileName, len(overrideData))
+	overrideLibPath := appendToAbsPath(envPath, overrideFileName)
+	err = afero.WriteFile(m.appFS, string(overrideLibPath), overrideData, defaultFilePermissions)
+	if err != nil {
+		log.Debugf("Failed to write '%s'", overrideFileName)
+		return err
+	}
+
 	// Generate the environment spec file.
 	envSpecData, err := generateSpecData(uri)
 	if err != nil {
@@ -327,6 +347,17 @@ func (m *manager) generateKsonnetLibData(spec ClusterSpec) ([]byte, []byte, []by
 	return extensionsLibData, k8sLibData, text, err
 }
 
+func (m *manager) generateOverrideData() []byte {
+	var buf bytes.Buffer
+	buf.WriteString(fmt.Sprintf("local base = import \"%s\";\n", m.baseLibsonnetPath))
+	buf.WriteString(fmt.Sprintf("local k = import \"%s\";\n\n", extensionsLibFilename))
+	buf.WriteString("base + {\n")
+	buf.WriteString("  // Insert user-specified overrides here. For example if a component is named \"nginx-deployment\", you might have something like:\n")
+	buf.WriteString("  //   \"nginx-deployment\"+: k.deployment.mixin.metadata.labels({foo: \"bar\"})\n")
+	buf.WriteString("}\n")
+	return buf.Bytes()
+}
+
 func generateSpecData(uri string) ([]byte, error) {
 	// Format the spec json and return; preface keys with 2 space idents.
 	return json.MarshalIndent(EnvironmentSpec{URI: uri}, "", "  ")
diff --git a/metadata/environment_test.go b/metadata/environment_test.go
index 2d9d2d73925cf48b3a4dcc1758662fabce4fe242..e4350417444bb9338d35d40217a2b8c906a27041 100644
--- a/metadata/environment_test.go
+++ b/metadata/environment_test.go
@@ -180,3 +180,21 @@ func TestSetEnvironment(t *testing.T) {
 		t.Fatalf("Expected set URI to be \"%s\", got:\n  %s", set.URI, envSpec.URI)
 	}
 }
+
+func TestGenerateOverrideData(t *testing.T) {
+	m := mockEnvironments(t, "test-gen-override-data")
+
+	expected := `local base = import "test-gen-override-data/environments/base.libsonnet";
+local k = import "k.libsonnet";
+
+base + {
+  // Insert user-specified overrides here. For example if a component is named "nginx-deployment", you might have something like:
+  //   "nginx-deployment"+: k.deployment.mixin.metadata.labels({foo: "bar"})
+}
+`
+	result := m.generateOverrideData()
+
+	if string(result) != expected {
+		t.Fatalf("Expected to generate override file with data:\n%s\n,got:\n%s", expected, result)
+	}
+}
diff --git a/metadata/interface.go b/metadata/interface.go
index afdb46dfefe106e48243a9a02a5819a77c01f42d..5c22265ecbdcda1b86777f9dd50db32916e5ce9e 100644
--- a/metadata/interface.go
+++ b/metadata/interface.go
@@ -43,7 +43,7 @@ type Manager interface {
 	Root() AbsPath
 	ComponentPaths() (AbsPaths, error)
 	CreateComponent(name string, text string, templateType prototype.TemplateType) error
-	LibPaths(envName string) (libPath, envLibPath AbsPath)
+	LibPaths(envName string) (libPath, envLibPath, envComponentPath AbsPath)
 	CreateEnvironment(name, uri string, spec ClusterSpec) error
 	DeleteEnvironment(name string) error
 	GetEnvironments() ([]*Environment, error)
diff --git a/metadata/manager.go b/metadata/manager.go
index 55022151069a269c71fcacc720ce6c90be24172e..2792d4051854417282f48b97f2d434e07b43c6e1 100644
--- a/metadata/manager.go
+++ b/metadata/manager.go
@@ -38,6 +38,11 @@ const (
 	componentsDir   = "components"
 	environmentsDir = "environments"
 	vendorDir       = "vendor"
+
+	baseLibsonnetFile = "base.libsonnet"
+
+	// ComponentsExtCodeKey is the ExtCode key for component imports
+	ComponentsExtCodeKey = "__ksonnet/components"
 )
 
 type manager struct {
@@ -49,6 +54,8 @@ type manager struct {
 	componentsPath   AbsPath
 	environmentsPath AbsPath
 	vendorDir        AbsPath
+
+	baseLibsonnetPath AbsPath
 }
 
 func findManager(abs AbsPath, appFS afero.Fs) (*manager, error) {
@@ -115,6 +122,8 @@ func newManager(rootPath AbsPath, appFS afero.Fs) *manager {
 		componentsPath:   appendToAbsPath(rootPath, componentsDir),
 		environmentsPath: appendToAbsPath(rootPath, environmentsDir),
 		vendorDir:        appendToAbsPath(rootPath, vendorDir),
+
+		baseLibsonnetPath: appendToAbsPath(rootPath, environmentsDir, baseLibsonnetFile),
 	}
 }
 
@@ -169,8 +178,9 @@ func (m *manager) CreateComponent(name string, text string, templateType prototy
 	return afero.WriteFile(m.appFS, componentPath, []byte(text), defaultFilePermissions)
 }
 
-func (m *manager) LibPaths(envName string) (libPath, envLibPath AbsPath) {
-	return m.libPath, appendToAbsPath(m.environmentsPath, envName)
+func (m *manager) LibPaths(envName string) (libPath, envLibPath, envComponentPath AbsPath) {
+	envPath := appendToAbsPath(m.environmentsPath, envName)
+	return m.libPath, appendToAbsPath(envPath, metadataDirName), appendToAbsPath(envPath, path.Base(envName)+".jsonnet")
 }
 
 func (m *manager) createAppDirTree() error {
@@ -181,7 +191,7 @@ func (m *manager) createAppDirTree() error {
 		return fmt.Errorf("Could not create app; directory '%s' already exists", m.rootPath)
 	}
 
-	paths := []AbsPath{
+	dirPaths := []AbsPath{
 		m.rootPath,
 		m.ksonnetPath,
 		m.libPath,
@@ -190,11 +200,19 @@ func (m *manager) createAppDirTree() error {
 		m.vendorDir,
 	}
 
-	for _, p := range paths {
+	for _, p := range dirPaths {
 		if err := m.appFS.MkdirAll(string(p), defaultFolderPermissions); err != nil {
 			return err
 		}
 	}
 
-	return nil
+	return afero.WriteFile(m.appFS, string(m.baseLibsonnetPath), genBaseLibsonnetContent(), defaultFilePermissions)
+}
+
+func genBaseLibsonnetContent() []byte {
+	return []byte(`local components = std.extVar("` + ComponentsExtCodeKey + `");
+components + {
+  // Insert user-specified overrides here.
+}
+`)
 }
diff --git a/metadata/manager_test.go b/metadata/manager_test.go
index 66c8f2d76bae5a6908c3930039166e5e9487aaf7..c3f40ad2c73ba47220044bc07b0ac75ab4958a1a 100644
--- a/metadata/manager_test.go
+++ b/metadata/manager_test.go
@@ -17,6 +17,7 @@ package metadata
 import (
 	"fmt"
 	"os"
+	"path"
 	"sort"
 	"testing"
 
@@ -84,8 +85,10 @@ func TestInitSuccess(t *testing.T) {
 		}
 	}
 
-	envPath := appendToAbsPath(appPath, string(defaultEnvDir))
-	schemaPath := appendToAbsPath(envPath, schemaFilename)
+	envPath := appendToAbsPath(appPath, string(environmentsDir))
+	metadataPath := appendToAbsPath(appPath, string(defaultEnvDir), string(metadataDirName))
+
+	schemaPath := appendToAbsPath(metadataPath, schemaFilename)
 	bytes, err := afero.ReadFile(testFS, string(schemaPath))
 	if err != nil {
 		t.Fatalf("Failed to read swagger file at '%s':\n%v", schemaPath, err)
@@ -93,7 +96,7 @@ func TestInitSuccess(t *testing.T) {
 		t.Fatalf("Expected swagger file at '%s' to have value: '%s', got: '%s'", schemaPath, blankSwaggerData, actualSwagger)
 	}
 
-	k8sLibPath := appendToAbsPath(envPath, k8sLibFilename)
+	k8sLibPath := appendToAbsPath(metadataPath, k8sLibFilename)
 	k8sLibBytes, err := afero.ReadFile(testFS, string(k8sLibPath))
 	if err != nil {
 		t.Fatalf("Failed to read ksonnet-lib file at '%s':\n%v", k8sLibPath, err)
@@ -101,13 +104,21 @@ func TestInitSuccess(t *testing.T) {
 		t.Fatalf("Expected swagger file at '%s' to have value: '%s', got: '%s'", k8sLibPath, blankK8sLib, actualK8sLib)
 	}
 
-	extensionsLibPath := appendToAbsPath(envPath, extensionsLibFilename)
+	extensionsLibPath := appendToAbsPath(metadataPath, extensionsLibFilename)
 	extensionsLibBytes, err := afero.ReadFile(testFS, string(extensionsLibPath))
 	if err != nil {
 		t.Fatalf("Failed to read ksonnet-lib file at '%s':\n%v", extensionsLibPath, err)
 	} else if string(extensionsLibBytes) == "" {
 		t.Fatalf("Expected extension library file at '%s' to be non-empty", extensionsLibPath)
 	}
+
+	baseLibsonnetPath := appendToAbsPath(envPath, baseLibsonnetFile)
+	baseLibsonnetBytes, err := afero.ReadFile(testFS, string(baseLibsonnetPath))
+	if err != nil {
+		t.Fatalf("Failed to read base.libsonnet file at '%s':\n%v", baseLibsonnetPath, err)
+	} else if len(baseLibsonnetBytes) == 0 {
+		t.Fatalf("Expected base.libsonnet at '%s' to be non-empty", baseLibsonnetPath)
+	}
 }
 
 func TestFindSuccess(t *testing.T) {
@@ -200,6 +211,25 @@ func TestComponentPaths(t *testing.T) {
 	}
 }
 
+func TestLibPaths(t *testing.T) {
+	appName := "test-lib-paths"
+	expectedLibPath := path.Join(appName, libDir)
+	expectedEnvLibPath := path.Join(appName, environmentsDir, mockEnvName, metadataDirName)
+	expectedEnvComponentPath := path.Join(appName, environmentsDir, mockEnvName, path.Base(mockEnvName)+".jsonnet")
+	m := mockEnvironments(t, appName)
+
+	libPath, envLibPath, envComponentPath := m.LibPaths(mockEnvName)
+	if string(libPath) != expectedLibPath {
+		t.Fatalf("Expected lib path to be:\n  '%s'\n, got:\n  '%s'", expectedLibPath, libPath)
+	}
+	if string(envLibPath) != expectedEnvLibPath {
+		t.Fatalf("Expected environment lib path to be:\n  '%s'\n, got:\n  '%s'", expectedEnvLibPath, envLibPath)
+	}
+	if string(envComponentPath) != expectedEnvComponentPath {
+		t.Fatalf("Expected environment component path to be:\n  '%s'\n, got:\n  '%s'", expectedEnvComponentPath, envComponentPath)
+	}
+}
+
 func TestFindFailure(t *testing.T) {
 	findFailure := func(t *testing.T, currDir AbsPath) {
 		_, err := findManager(currDir, testFS)