diff --git a/cmd/phase/render.go b/cmd/phase/render.go
index 10c8f9946..6c3c83810 100644
--- a/cmd/phase/render.go
+++ b/cmd/phase/render.go
@@ -18,7 +18,7 @@ import (
 	"github.com/spf13/cobra"
 
 	"opendev.org/airship/airshipctl/pkg/config"
-	"opendev.org/airship/airshipctl/pkg/phase/render"
+	"opendev.org/airship/airshipctl/pkg/phase"
 )
 
 const (
@@ -35,48 +35,48 @@ airshipctl phase render initinfra -l app=helm,service=tiller -k Deployment
 
 // NewRenderCommand create a new command for document rendering
 func NewRenderCommand(cfgFactory config.Factory) *cobra.Command {
-	renderSettings := &render.Settings{}
+	filterOptions := &phase.FilterOptions{}
 	renderCmd := &cobra.Command{
 		Use:     "render PHASE_NAME",
 		Short:   "Render phase documents from model",
 		Example: renderExample,
 		Args:    cobra.ExactArgs(1),
 		RunE: func(cmd *cobra.Command, args []string) error {
-			return renderSettings.Render(cfgFactory, args[0], cmd.OutOrStdout())
+			return filterOptions.Render(cfgFactory, args[0], cmd.OutOrStdout())
 		},
 	}
 
-	addRenderFlags(renderSettings, renderCmd)
+	addRenderFlags(filterOptions, renderCmd)
 	return renderCmd
 }
 
 // addRenderFlags adds flags for document render sub-command
-func addRenderFlags(settings *render.Settings, cmd *cobra.Command) {
+func addRenderFlags(filterOptions *phase.FilterOptions, cmd *cobra.Command) {
 	flags := cmd.Flags()
 
 	flags.StringVarP(
-		&settings.Label,
+		&filterOptions.Label,
 		"label",
 		"l",
 		"",
 		"filter documents by Labels")
 
 	flags.StringVarP(
-		&settings.Annotation,
+		&filterOptions.Annotation,
 		"annotation",
 		"a",
 		"",
 		"filter documents by Annotations")
 
 	flags.StringVarP(
-		&settings.APIVersion,
+		&filterOptions.APIVersion,
 		"apiversion",
 		"g",
 		"",
 		"filter documents by API version")
 
 	flags.StringVarP(
-		&settings.Kind,
+		&filterOptions.Kind,
 		"kind",
 		"k",
 		"",
diff --git a/cmd/phase/render_test.go b/cmd/phase/render_test.go
index 86fa39da8..d3bae377d 100644
--- a/cmd/phase/render_test.go
+++ b/cmd/phase/render_test.go
@@ -17,44 +17,17 @@ package phase_test
 import (
 	"testing"
 
-	"github.com/stretchr/testify/require"
-
 	"opendev.org/airship/airshipctl/cmd/phase"
-	"opendev.org/airship/airshipctl/pkg/config"
 	"opendev.org/airship/airshipctl/testutil"
 )
 
 func TestRender(t *testing.T) {
-	cfg, cleanupCfg := testutil.InitConfig(t)
-	defer cleanupCfg(t)
-	cfg.CurrentContext = "def_ephemeral"
-	cfg.Manifests["test"] = &config.Manifest{
-		TargetPath:            "testdata",
-		PrimaryRepositoryName: "testRepo",
-		Repositories: map[string]*config.Repository{
-			"testRepo": {
-				URLString: "http://localhost",
-			},
-		},
-	}
-	ctx, err := cfg.GetContext("def_ephemeral")
-	require.NoError(t, err)
-	ctx.Manifest = "test"
-	settings := func() (*config.Config, error) {
-		return cfg, nil
-	}
-
 	tests := []*testutil.CmdTest{
 		{
 			Name:    "render-with-help",
 			CmdLine: "-h",
 			Cmd:     phase.NewRenderCommand(nil),
 		},
-		{
-			Name:    "render-with-multiple-labels",
-			CmdLine: "initinfra -l app=helm,name=tiller",
-			Cmd:     phase.NewRenderCommand(settings),
-		},
 	}
 	for _, tt := range tests {
 		testutil.RunTest(t, tt)
diff --git a/cmd/phase/testdata/TestRenderGoldenOutput/render-with-multiple-labels.golden b/cmd/phase/testdata/TestRenderGoldenOutput/render-with-multiple-labels.golden
deleted file mode 100644
index 9f0539f47..000000000
--- a/cmd/phase/testdata/TestRenderGoldenOutput/render-with-multiple-labels.golden
+++ /dev/null
@@ -1,52 +0,0 @@
----
-apiVersion: extensions/v1beta1
-kind: Deployment
-metadata:
-  annotations:
-    airshipit.org/clustertype: ephemeral
-  creationTimestamp: null
-  labels:
-    app: helm
-    name: tiller
-  name: tiller-deploy
-  namespace: kube-system
-spec:
-  replicas: 1
-  strategy: {}
-  template:
-    metadata:
-      creationTimestamp: null
-      labels:
-        app: helm
-        name: tiller
-    spec:
-      automountServiceAccountToken: true
-      containers:
-      - env:
-        - name: TILLER_NAMESPACE
-          value: kube-system
-        - name: TILLER_HISTORY_MAX
-          value: "0"
-        image: gcr.io/kubernetes-helm/tiller:v2.12.3
-        imagePullPolicy: IfNotPresent
-        livenessProbe:
-          httpGet:
-            path: /liveness
-            port: 44135
-          initialDelaySeconds: 1
-          timeoutSeconds: 1
-        name: tiller
-        ports:
-        - containerPort: 44134
-          name: tiller
-        - containerPort: 44135
-          name: http
-        readinessProbe:
-          httpGet:
-            path: /readiness
-            port: 44135
-          initialDelaySeconds: 1
-          timeoutSeconds: 1
-        resources: {}
-status: {}
-...
diff --git a/cmd/phase/testdata/ephemeral/initinfra/kustomization.yaml b/cmd/phase/testdata/ephemeral/initinfra/kustomization.yaml
deleted file mode 100644
index c1f4d1b64..000000000
--- a/cmd/phase/testdata/ephemeral/initinfra/kustomization.yaml
+++ /dev/null
@@ -1,2 +0,0 @@
-resources:
- - tiller.yaml
\ No newline at end of file
diff --git a/cmd/phase/testdata/ephemeral/initinfra/tiller.yaml b/cmd/phase/testdata/ephemeral/initinfra/tiller.yaml
deleted file mode 100644
index cdd081e39..000000000
--- a/cmd/phase/testdata/ephemeral/initinfra/tiller.yaml
+++ /dev/null
@@ -1,74 +0,0 @@
----
-apiVersion: extensions/v1beta1
-kind: Deployment
-metadata:
-  annotations:
-    airshipit.org/clustertype: ephemeral
-  creationTimestamp: null
-  labels:
-    app: helm
-    name: tiller
-  name: tiller-deploy
-  namespace: kube-system
-spec:
-  replicas: 1
-  strategy: {}
-  template:
-    metadata:
-      creationTimestamp: null
-      labels:
-        app: helm
-        name: tiller
-    spec:
-      automountServiceAccountToken: true
-      containers:
-      - env:
-        - name: TILLER_NAMESPACE
-          value: kube-system
-        - name: TILLER_HISTORY_MAX
-          value: "0"
-        image: gcr.io/kubernetes-helm/tiller:v2.12.3
-        imagePullPolicy: IfNotPresent
-        livenessProbe:
-          httpGet:
-            path: /liveness
-            port: 44135
-          initialDelaySeconds: 1
-          timeoutSeconds: 1
-        name: tiller
-        ports:
-        - containerPort: 44134
-          name: tiller
-        - containerPort: 44135
-          name: http
-        readinessProbe:
-          httpGet:
-            path: /readiness
-            port: 44135
-          initialDelaySeconds: 1
-          timeoutSeconds: 1
-        resources: {}
-status: {}
----
-apiVersion: v1
-kind: Service
-metadata:
-  annotations:
-    airshipit.org/clustertype: ephemeral
-  creationTimestamp: null
-  labels:
-    app: helm
-  name: tiller-deploy
-  namespace: kube-system
-spec:
-  ports:
-  - name: tiller
-    port: 44134
-    targetPort: tiller
-  selector:
-    app: helm
-    name: tiller
-  type: ClusterIP
-status:
-  loadBalancer: {}
-...
\ No newline at end of file
diff --git a/go.sum b/go.sum
index 4dca82838..622eda603 100644
--- a/go.sum
+++ b/go.sum
@@ -1507,6 +1507,7 @@ k8s.io/kube-openapi v0.0.0-20200121204235-bf4fb3bd569c/go.mod h1:GRQhZsXIAJ1xR0C
 k8s.io/kube-state-metrics v1.7.2/go.mod h1:U2Y6DRi07sS85rmVPmBFlmv+2peBcL8IWGjM+IjYA/E=
 k8s.io/kubectl v0.0.0-20191219154910-1528d4eea6dd h1:nZX5+wEqTu/EBIYjrZlFOA63z4+Zcy96lDkCZPU9a9c=
 k8s.io/kubectl v0.0.0-20191219154910-1528d4eea6dd/go.mod h1:9ehGcuUGjXVZh0qbYSB0vvofQw2JQe6c6cO0k4wu/Oo=
+k8s.io/kubernetes v1.13.0 h1:qTfB+u5M92k2fCCCVP2iuhgwwSOv1EkAkvQY1tQODD8=
 k8s.io/kubernetes v1.13.0/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk=
 k8s.io/metrics v0.0.0-20191214191643-6b1944c9f765/go.mod h1:5V7rewilItwK0cz4nomU0b3XCcees2Ka5EBYWS1HBeM=
 k8s.io/utils v0.0.0-20190308190857-21c4ce38f2a7/go.mod h1:8k8uAuAQ0rXslZKaEWd0c3oVhZz7sSzSiPnVZayjIX0=
diff --git a/pkg/bootstrap/isogen/executor.go b/pkg/bootstrap/isogen/executor.go
index 5f3f8abd0..b21313ff4 100644
--- a/pkg/bootstrap/isogen/executor.go
+++ b/pkg/bootstrap/isogen/executor.go
@@ -144,8 +144,10 @@ func (c *Executor) Validate() error {
 }
 
 // Render executor documents
-func (c *Executor) Render(_ io.Writer, _ ifc.RenderOptions) error {
-	return errors.ErrNotImplemented{}
+func (c *Executor) Render(w io.Writer, _ ifc.RenderOptions) error {
+	// will be implemented later
+	_, err := w.Write([]byte{})
+	return err
 }
 
 func handleError(ch chan<- events.Event, err error) {
diff --git a/pkg/clusterctl/client/executor.go b/pkg/clusterctl/client/executor.go
index 595eaee53..aa77f7e1d 100644
--- a/pkg/clusterctl/client/executor.go
+++ b/pkg/clusterctl/client/executor.go
@@ -182,6 +182,8 @@ func (c *ClusterctlExecutor) Validate() error {
 }
 
 // Render executor documents
-func (c *ClusterctlExecutor) Render(_ io.Writer, _ ifc.RenderOptions) error {
-	return errors.ErrNotImplemented{}
+func (c *ClusterctlExecutor) Render(w io.Writer, _ ifc.RenderOptions) error {
+	// will be implemented later
+	_, err := w.Write([]byte{})
+	return err
 }
diff --git a/pkg/clusterctl/client/executor_test.go b/pkg/clusterctl/client/executor_test.go
index 9de06b43c..6575e4a80 100644
--- a/pkg/clusterctl/client/executor_test.go
+++ b/pkg/clusterctl/client/executor_test.go
@@ -218,7 +218,6 @@ func TestExecutorRender(t *testing.T) {
 	sampleCfgDoc := executorDoc(t, "init")
 	bundle, err := document.NewBundleByPath("testdata/executor_init")
 	require.NoError(t, err)
-	expectedErr := airerrors.ErrNotImplemented{}
 
 	executor, err := cctlclient.NewExecutor(
 		ifc.ExecutorConfig{
@@ -229,7 +228,7 @@ func TestExecutorRender(t *testing.T) {
 	require.NoError(t, err)
 	actualOut := &bytes.Buffer{}
 	actualErr := executor.Render(actualOut, ifc.RenderOptions{})
-	assert.Equal(t, expectedErr, actualErr)
+	assert.Equal(t, nil, actualErr)
 }
 
 func makeDefaultHelper(t *testing.T) ifc.Helper {
diff --git a/pkg/k8s/applier/applier.go b/pkg/k8s/applier/applier.go
index 585a86fd1..a0130b9a3 100644
--- a/pkg/k8s/applier/applier.go
+++ b/pkg/k8s/applier/applier.go
@@ -93,7 +93,7 @@ func (a *Applier) ApplyBundle(bundle document.Bundle, ao ApplyOptions) {
 
 func (a *Applier) getInfos(bundleName string, bundle document.Bundle) ([]*resource.Info, error) {
 	if bundle == nil {
-		return nil, ErrApplyNilBundle{}
+		return nil, ErrNilBundle{}
 	}
 	selector := document.
 		NewSelector().
diff --git a/pkg/k8s/applier/applier_test.go b/pkg/k8s/applier/applier_test.go
index 5afa553ad..3a71542d4 100644
--- a/pkg/k8s/applier/applier_test.go
+++ b/pkg/k8s/applier/applier_test.go
@@ -94,7 +94,7 @@ func TestApplierRun(t *testing.T) {
 		},
 		{
 			name:           "bundle failure",
-			expectedString: "Cannot apply nil bundle",
+			expectedString: "nil bundle provided",
 			expectErr:      true,
 		},
 		{
diff --git a/pkg/k8s/applier/errors.go b/pkg/k8s/applier/errors.go
index 96efdf588..603b769a8 100644
--- a/pkg/k8s/applier/errors.go
+++ b/pkg/k8s/applier/errors.go
@@ -28,10 +28,10 @@ func (e ErrApply) Error() string {
 	return fmt.Sprintf("Applying of resources to kubernetes cluster has failed, errors are:\n%v", e.errors)
 }
 
-// ErrApplyNilBundle returned when nil bundle is passed to ApplyBundle function
-type ErrApplyNilBundle struct {
+// ErrNilBundle returned when bundle is nil
+type ErrNilBundle struct {
 }
 
-func (e ErrApplyNilBundle) Error() string {
-	return "Cannot apply nil bundle"
+func (e ErrNilBundle) Error() string {
+	return "nil bundle provided"
 }
diff --git a/pkg/k8s/applier/executor.go b/pkg/k8s/applier/executor.go
index 825622f26..ae3d08ee6 100644
--- a/pkg/k8s/applier/executor.go
+++ b/pkg/k8s/applier/executor.go
@@ -82,6 +82,9 @@ func NewExecutor(opts ExecutorOptions) (*Executor, error) {
 	if err != nil {
 		return nil, err
 	}
+	if opts.ExecutorBundle == nil {
+		return nil, ErrNilBundle{}
+	}
 	return &Executor{
 		Options:   opts,
 		apiObject: apiObj,
@@ -116,9 +119,6 @@ func (e *Executor) prepareApplier(ch chan events.Event) (*Applier, document.Bund
 	if err != nil {
 		return nil, nil, err
 	}
-	if e.Options.ExecutorBundle == nil {
-		return nil, nil, ErrApplyNilBundle{}
-	}
 	log.Debug("Filtering out documents that shouldn't be applied to kubernetes from document bundle")
 	bundle, err := e.Options.ExecutorBundle.SelectBundle(document.NewDeployToK8sSelector())
 	if err != nil {
@@ -139,6 +139,10 @@ func (e *Executor) Validate() error {
 }
 
 // Render document set
-func (e *Executor) Render(w io.Writer, _ ifc.RenderOptions) error {
-	return e.Options.ExecutorBundle.Write(w)
+func (e *Executor) Render(w io.Writer, o ifc.RenderOptions) error {
+	bundle, err := e.Options.ExecutorBundle.SelectBundle(o.FilterSelector)
+	if err != nil {
+		return err
+	}
+	return bundle.Write(w)
 }
diff --git a/pkg/k8s/applier/executor_test.go b/pkg/k8s/applier/executor_test.go
index de99caaeb..a6724edbe 100644
--- a/pkg/k8s/applier/executor_test.go
+++ b/pkg/k8s/applier/executor_test.go
@@ -167,7 +167,7 @@ func TestExecutorRun(t *testing.T) {
 		{
 			name:        "Nil bundle provided",
 			execDoc:     toKubernetesApply(t, ValidExecutorDoc),
-			containsErr: "Cannot apply nil bundle",
+			containsErr: "nil bundle provided",
 			kubeconf:    testKubeconfig(testValidKubeconfig),
 			helper:      makeDefaultHelper(t),
 			bundleFunc: func(t *testing.T) document.Bundle {
@@ -185,17 +185,22 @@ func TestExecutorRun(t *testing.T) {
 					ExecutorBundle:   tt.bundleFunc(t),
 					Kubeconfig:       tt.kubeconf,
 				})
-			require.NoError(t, err)
-			require.NotNil(t, exec)
-			ch := make(chan events.Event)
-			go exec.Run(ch, ifc.RunOptions{})
-			processor := events.NewDefaultProcessor(utils.Streams())
-			err = processor.Process(ch)
-			if tt.containsErr != "" {
+			if tt.name == "Nil bundle provided" {
 				require.Error(t, err)
 				assert.Contains(t, err.Error(), tt.containsErr)
 			} else {
-				assert.NoError(t, err)
+				require.NoError(t, err)
+				require.NotNil(t, exec)
+				ch := make(chan events.Event)
+				go exec.Run(ch, ifc.RunOptions{})
+				processor := events.NewDefaultProcessor(utils.Streams())
+				err = processor.Process(ch)
+				if tt.containsErr != "" {
+					require.Error(t, err)
+					assert.Contains(t, err.Error(), tt.containsErr)
+				} else {
+					assert.NoError(t, err)
+				}
 			}
 		})
 	}
diff --git a/pkg/phase/client.go b/pkg/phase/client.go
index e71681f53..0ca041aa0 100644
--- a/pkg/phase/client.go
+++ b/pkg/phase/client.go
@@ -15,6 +15,7 @@
 package phase
 
 import (
+	"io"
 	"path/filepath"
 
 	"k8s.io/apimachinery/pkg/runtime/schema"
@@ -131,6 +132,16 @@ func (p *phase) Validate() error {
 	return nil
 }
 
+// Render executor documents
+func (p *phase) Render(w io.Writer, options ifc.RenderOptions) error {
+	executor, err := p.Executor()
+	if err != nil {
+		return err
+	}
+
+	return executor.Render(w, options)
+}
+
 // DocumentRoot root that holds all the documents associated with the phase
 func (p *phase) DocumentRoot() string {
 	if p.apiObj.Config.DocumentEntryPoint == "" {
diff --git a/pkg/phase/client_test.go b/pkg/phase/client_test.go
index 424fea4e5..89d8886b2 100644
--- a/pkg/phase/client_test.go
+++ b/pkg/phase/client_test.go
@@ -177,11 +177,11 @@ var _ ifc.Executor = fakeExecutor{}
 type fakeExecutor struct {
 }
 
-func (e fakeExecutor) Render(w io.Writer, ro ifc.RenderOptions) error {
+func (e fakeExecutor) Render(_ io.Writer, _ ifc.RenderOptions) error {
 	return nil
 }
 
-func (e fakeExecutor) Run(ch chan events.Event, ro ifc.RunOptions) {
+func (e fakeExecutor) Run(ch chan events.Event, _ ifc.RunOptions) {
 	defer close(ch)
 }
 
diff --git a/pkg/phase/ifc/executor.go b/pkg/phase/ifc/executor.go
index 3cc8e0e48..340aa97ad 100644
--- a/pkg/phase/ifc/executor.go
+++ b/pkg/phase/ifc/executor.go
@@ -40,8 +40,10 @@ type RunOptions struct {
 	Timeout time.Duration
 }
 
-// RenderOptions is empty for now, but may hold things like format in future
-type RenderOptions struct{}
+// RenderOptions holds options for render method
+type RenderOptions struct {
+	FilterSelector document.Selector
+}
 
 // WaitOptions holds only timeout now, but may be extended in the future
 type WaitOptions struct {
diff --git a/pkg/phase/ifc/phase.go b/pkg/phase/ifc/phase.go
index 98643d909..223991fd5 100644
--- a/pkg/phase/ifc/phase.go
+++ b/pkg/phase/ifc/phase.go
@@ -15,18 +15,20 @@
 package ifc
 
 import (
+	"io"
+
 	"opendev.org/airship/airshipctl/pkg/api/v1alpha1"
 	"opendev.org/airship/airshipctl/pkg/cluster/clustermap"
 )
 
 // Phase provides a way to interact with a phase
-// TODO add render method
 type Phase interface {
 	Validate() error
 	Run(RunOptions) error
 	DocumentRoot() string
 	Details() (string, error)
 	Executor() (Executor, error)
+	Render(io.Writer, RenderOptions) error
 }
 
 // ID uniquely identifies the phase
diff --git a/pkg/phase/render/render.go b/pkg/phase/render.go
similarity index 53%
rename from pkg/phase/render/render.go
rename to pkg/phase/render.go
index 2e6e5da0d..ccc24cac1 100644
--- a/pkg/phase/render/render.go
+++ b/pkg/phase/render.go
@@ -12,7 +12,7 @@
  limitations under the License.
 */
 
-package render
+package phase
 
 import (
 	"io"
@@ -20,26 +20,40 @@ import (
 
 	"opendev.org/airship/airshipctl/pkg/config"
 	"opendev.org/airship/airshipctl/pkg/document"
+	"opendev.org/airship/airshipctl/pkg/phase/ifc"
 )
 
+// FilterOptions holds filters for selector
+type FilterOptions struct {
+	// Label filters documents by label string
+	Label string
+	// Annotation filters documents by annotation string
+	Annotation string
+	// APIVersion filters documents by API group and version
+	APIVersion string
+	// Kind filters documents by document kind
+	Kind string
+}
+
 // Render prints out filtered documents
-func (s *Settings) Render(cfgFactory config.Factory, phaseName string, out io.Writer) error {
+func (fo *FilterOptions) Render(cfgFactory config.Factory, phaseName string, out io.Writer) error {
 	cfg, err := cfgFactory()
 	if err != nil {
 		return err
 	}
 
-	path, err := cfg.CurrentContextEntryPoint(phaseName)
+	helper, err := NewHelper(cfg)
 	if err != nil {
 		return err
 	}
 
-	docBundle, err := document.NewBundleByPath(path)
+	client := NewClient(helper)
+	phase, err := client.PhaseByID(ifc.ID{Name: phaseName})
 	if err != nil {
 		return err
 	}
 
-	groupVersion := strings.Split(s.APIVersion, "/")
+	groupVersion := strings.Split(fo.APIVersion, "/")
 	group := ""
 	version := groupVersion[0]
 	if len(groupVersion) > 1 {
@@ -47,11 +61,7 @@ func (s *Settings) Render(cfgFactory config.Factory, phaseName string, out io.Wr
 		version = strings.Join(groupVersion[1:], "/")
 	}
 
-	sel := document.NewSelector().ByLabel(s.Label).ByAnnotation(s.Annotation).ByGvk(group, version, s.Kind)
-	filteredBundle, err := docBundle.SelectBundle(sel)
-	if err != nil {
-		return err
-	}
+	sel := document.NewSelector().ByLabel(fo.Label).ByAnnotation(fo.Annotation).ByGvk(group, version, fo.Kind)
 
-	return filteredBundle.Write(out)
+	return phase.Render(out, ifc.RenderOptions{FilterSelector: sel})
 }
diff --git a/pkg/phase/render/type.go b/pkg/phase/render/type.go
deleted file mode 100644
index 65b68e211..000000000
--- a/pkg/phase/render/type.go
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- 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
-
-     https://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 render
-
-// Settings for document rendering
-type Settings struct {
-	// Label filters documents by label string
-	Label string
-	// Annotation filters documents by annotation string
-	Annotation string
-	// APIVersion filters documents by API group and version
-	APIVersion string
-	// Kind filters documents by document kind
-	Kind string
-}
diff --git a/pkg/phase/render/render_test.go b/pkg/phase/render_test.go
similarity index 88%
rename from pkg/phase/render/render_test.go
rename to pkg/phase/render_test.go
index 206b86a0b..2c4935fbd 100644
--- a/pkg/phase/render/render_test.go
+++ b/pkg/phase/render_test.go
@@ -12,7 +12,7 @@
  limitations under the License.
 */
 
-package render_test
+package phase_test
 
 import (
 	"bytes"
@@ -25,7 +25,7 @@ import (
 	"github.com/stretchr/testify/require"
 
 	"opendev.org/airship/airshipctl/pkg/config"
-	"opendev.org/airship/airshipctl/pkg/phase/render"
+	"opendev.org/airship/airshipctl/pkg/phase"
 	"opendev.org/airship/airshipctl/testutil"
 )
 
@@ -34,22 +34,23 @@ func TestRender(t *testing.T) {
 	dummyManifest := rs.Manifests["dummy_manifest"]
 	dummyManifest.TargetPath = "testdata"
 	dummyManifest.SubPath = ""
+	dummyManifest.MetadataPath = "metadata.yaml"
 	fixturePath := "phase"
 	tests := []struct {
 		name       string
-		settings   *render.Settings
+		settings   *phase.FilterOptions
 		expResFile string
 		expErr     error
 	}{
 		{
 			name:       "No Filters",
-			settings:   &render.Settings{},
+			settings:   &phase.FilterOptions{},
 			expResFile: "noFilter.yaml",
 			expErr:     nil,
 		},
 		{
 			name: "All Filters",
-			settings: &render.Settings{
+			settings: &phase.FilterOptions{
 				Label:      "airshipit.org/deploy-k8s=false",
 				Annotation: "airshipit.org/clustertype=ephemeral",
 				APIVersion: "metal3.io/v1alpha1",
@@ -60,7 +61,7 @@ func TestRender(t *testing.T) {
 		},
 		{
 			name: "Multiple Labels",
-			settings: &render.Settings{
+			settings: &phase.FilterOptions{
 				Label: "airshipit.org/deploy-k8s=false, airshipit.org/ephemeral-node=true",
 			},
 			expResFile: "multiLabels.yaml",
@@ -68,7 +69,7 @@ func TestRender(t *testing.T) {
 		},
 		{
 			name: "Malformed Label",
-			settings: &render.Settings{
+			settings: &phase.FilterOptions{
 				Label: "app=(",
 			},
 			expResFile: "",
diff --git a/pkg/phase/render/testdata/ephemeral/phase/baremetal.yaml b/pkg/phase/testdata/ephemeral/phase/baremetal.yaml
similarity index 100%
rename from pkg/phase/render/testdata/ephemeral/phase/baremetal.yaml
rename to pkg/phase/testdata/ephemeral/phase/baremetal.yaml
diff --git a/pkg/phase/render/testdata/ephemeral/phase/kustomization.yaml b/pkg/phase/testdata/ephemeral/phase/kustomization.yaml
similarity index 100%
rename from pkg/phase/render/testdata/ephemeral/phase/kustomization.yaml
rename to pkg/phase/testdata/ephemeral/phase/kustomization.yaml
diff --git a/pkg/phase/render/testdata/expected/allFilters.yaml b/pkg/phase/testdata/expected/allFilters.yaml
similarity index 100%
rename from pkg/phase/render/testdata/expected/allFilters.yaml
rename to pkg/phase/testdata/expected/allFilters.yaml
diff --git a/pkg/phase/render/testdata/expected/multiLabels.yaml b/pkg/phase/testdata/expected/multiLabels.yaml
similarity index 100%
rename from pkg/phase/render/testdata/expected/multiLabels.yaml
rename to pkg/phase/testdata/expected/multiLabels.yaml
diff --git a/pkg/phase/render/testdata/expected/noFilter.yaml b/pkg/phase/testdata/expected/noFilter.yaml
similarity index 100%
rename from pkg/phase/render/testdata/expected/noFilter.yaml
rename to pkg/phase/testdata/expected/noFilter.yaml
diff --git a/pkg/phase/render/testdata/expected/rawFilter.yaml b/pkg/phase/testdata/expected/rawFilter.yaml
similarity index 100%
rename from pkg/phase/render/testdata/expected/rawFilter.yaml
rename to pkg/phase/testdata/expected/rawFilter.yaml
diff --git a/pkg/phase/testdata/metadata.yaml b/pkg/phase/testdata/metadata.yaml
new file mode 100755
index 000000000..220af9c24
--- /dev/null
+++ b/pkg/phase/testdata/metadata.yaml
@@ -0,0 +1,2 @@
+phase:
+  path: phases
\ No newline at end of file
diff --git a/pkg/phase/testdata/phases/cluster-map.yaml b/pkg/phase/testdata/phases/cluster-map.yaml
new file mode 100755
index 000000000..d53fe7e30
--- /dev/null
+++ b/pkg/phase/testdata/phases/cluster-map.yaml
@@ -0,0 +1,11 @@
+---
+apiVersion: airshipit.org/v1alpha1
+kind: ClusterMap
+metadata:
+  labels:
+    airshipit.org/deploy-k8s: "false"
+  name: main-map
+map:
+  target-cluster:
+    parent: ephemeral-cluster
+  ephemeral-cluster: {}
\ No newline at end of file
diff --git a/pkg/phase/testdata/phases/executors.yaml b/pkg/phase/testdata/phases/executors.yaml
new file mode 100755
index 000000000..dcc6e279d
--- /dev/null
+++ b/pkg/phase/testdata/phases/executors.yaml
@@ -0,0 +1,12 @@
+---
+apiVersion: airshipit.org/v1alpha1
+kind: KubernetesApply
+metadata:
+  labels:
+    airshipit.org/deploy-k8s: "false"
+  name: kubernetes-apply
+config:
+  waitOptions:
+    timeout: 2000
+  pruneOptions:
+    prune: false
diff --git a/pkg/phase/testdata/phases/kustomization.yaml b/pkg/phase/testdata/phases/kustomization.yaml
new file mode 100755
index 000000000..7d5a69471
--- /dev/null
+++ b/pkg/phase/testdata/phases/kustomization.yaml
@@ -0,0 +1,4 @@
+resources:
+  - phases.yaml
+  - executors.yaml
+  - cluster-map.yaml
\ No newline at end of file
diff --git a/pkg/phase/testdata/phases/phases.yaml b/pkg/phase/testdata/phases/phases.yaml
new file mode 100755
index 000000000..d91c72000
--- /dev/null
+++ b/pkg/phase/testdata/phases/phases.yaml
@@ -0,0 +1,12 @@
+---
+apiVersion: airshipit.org/v1alpha1
+kind: Phase
+metadata:
+  name: phase
+  clusterName: ephemeral-cluster
+config:
+  executorRef:
+    apiVersion: airshipit.org/v1alpha1
+    kind: KubernetesApply
+    name: kubernetes-apply
+  documentEntryPoint: ephemeral/phase
diff --git a/tools/document/validate_site_docs.sh b/tools/document/validate_site_docs.sh
index cc7371c68..a823aac8c 100755
--- a/tools/document/validate_site_docs.sh
+++ b/tools/document/validate_site_docs.sh
@@ -12,7 +12,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-set -e
+set -xe
 
 # The root of the manifest structure to be validated.
 # This corresponds to the targetPath in an airshipctl config
@@ -109,7 +109,7 @@ for cluster in ephemeral target; do
         # removed since it was choking in certain cases and got to be more trouble than was worth.
         # This should be removed once we have a phase map that is smarter.
         # In the meantime, as new phases are added, please add them here as well.
-        phases="bootstrap initinfra controlplane baremetalhost workers workload tenant"
+        phases="initinfra-ephemeral controlplane-ephemeral initinfra-target workers-target"
 
         for phase in $phases; do
             # Guard against bootstrap or initinfra being missing, which could be the case for some configs