Deploy Kubernetes Resources from YAML with ConfigGroup

The kubernetes:yaml/v2:ConfigGroup resource, part of the Pulumi Kubernetes provider, creates multiple Kubernetes resources from YAML manifests, handling dependency ordering and reconciliation automatically. This guide focuses on three capabilities: file-based deployment, glob pattern matching, and inline YAML strings.

ConfigGroup requires a configured Kubernetes cluster and applies resources using your kubeconfig context. The examples are intentionally small. Combine them with your own manifest organization and dependency requirements.

Deploy manifests from local YAML files

Most Kubernetes deployments start with YAML manifests stored in version control, which ConfigGroup reads and applies.

import * as pulumi from "@pulumi/pulumi";
import * as k8s from "@pulumi/kubernetes";

const example = new k8s.yaml.v2.ConfigGroup("example", {
    files: ["./manifest.yaml"],
});
import pulumi
from pulumi_kubernetes.yaml.v2 import ConfigGroup

example = ConfigGroup(
    "example",
    files=["./manifest.yaml"]
)
package main

import (
	yamlv2 "github.com/pulumi/pulumi-kubernetes/sdk/v4/go/kubernetes/yaml/v2"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_, err := yamlv2.NewConfigGroup(ctx, "example", &yamlv2.ConfigGroupArgs{
			Files: pulumi.ToStringArray([]string{"manifest.yaml"}),
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using Pulumi;
using Pulumi.Kubernetes.Types.Inputs.Yaml.V2;
using Pulumi.Kubernetes.Yaml.V2;
using System.Collections.Generic;

return await Deployment.RunAsync(() =>
{
    var example = new ConfigGroup("example", new ConfigGroupArgs
    {
        Files = new[] { "./manifest.yaml" }
    });
});
package myproject;

import com.pulumi.Pulumi;
import com.pulumi.kubernetes.yaml.v2.ConfigGroup;
import com.pulumi.kubernetes.yaml.v2.ConfigGroupArgs;

public class App {
    public static void main(String[] args) {
        Pulumi.run(ctx -> {
            var example = new ConfigGroup("example", ConfigGroupArgs.builder()
                    .files("./manifest.yaml")
                    .build());
        });
    }
}
name: example
runtime: yaml
resources:
  example:
    type: kubernetes:yaml/v2:ConfigGroup
    properties:
      files:
      - ./manifest.yaml

The files property accepts paths to YAML manifests. ConfigGroup parses each file, creates the resources it defines, and waits for them to reconcile. Pulumi uses heuristics to determine application order, ensuring namespaces are created before namespaced resources.

Apply multiple manifests using glob patterns

Applications often organize manifests into directories, and glob patterns let you apply entire directories without listing each file.

import * as pulumi from "@pulumi/pulumi";
import * as k8s from "@pulumi/kubernetes";

const example = new k8s.yaml.v2.ConfigGroup("example", {
    yaml: `
    apiVersion: v1
    kind: ConfigMap
    metadata:
      name: my-map
    `
});
import pulumi
from pulumi_kubernetes.yaml.v2 import ConfigGroup

example = ConfigGroup(
    "example",
    yaml="""
apiVersion: v1
kind: ConfigMap
metadata:
  name: my-map
"""
)
package main

import (
	yamlv2 "github.com/pulumi/pulumi-kubernetes/sdk/v4/go/kubernetes/yaml/v2"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_, err := yamlv2.NewConfigGroup(ctx, "example", &yamlv2.ConfigGroupArgs{
			Yaml: pulumi.StringPtr(`
apiVersion: v1
kind: ConfigMap
metadata:
  name: my-map
`),
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using Pulumi;
using Pulumi.Kubernetes.Types.Inputs.Yaml.V2;
using Pulumi.Kubernetes.Yaml.V2;
using System.Collections.Generic;

return await Deployment.RunAsync(() =>
{
    var example = new ConfigGroup("example", new ConfigGroupArgs
    {
        Yaml = @"
            apiVersion: v1
            kind: ConfigMap
            metadata:
              name: my-map
            "
    });
});
package myproject;

import com.pulumi.Pulumi;
import com.pulumi.kubernetes.yaml.v2.ConfigGroup;
import com.pulumi.kubernetes.yaml.v2.ConfigGroupArgs;

public class App {
    public static void main(String[] args) {
        Pulumi.run(ctx -> {
            var example = new ConfigGroup("example", ConfigGroupArgs.builder()
                    .yaml("""
                        apiVersion: v1
                        kind: ConfigMap
                        metadata:
                          name: my-map
                        """
                    )
                    .build());
        });
    }
}
name: example
runtime: yaml
resources:
  example:
    type: kubernetes:yaml/v2:ConfigGroup
    properties:
      yaml: |
        apiVersion: v1
        kind: ConfigMap
        metadata:
          name: my-map        

Glob patterns like ./manifests/*.yaml match all YAML files in a directory. This approach scales better than listing individual files, especially when manifest counts grow or change frequently.

Embed YAML directly in Pulumi code

Small configurations can be embedded directly in your Pulumi program, avoiding separate manifest files.

import * as pulumi from "@pulumi/pulumi";
import * as k8s from "@pulumi/kubernetes";

const example = new k8s.yaml.v2.ConfigGroup("example", {
    objs: [
        {
            apiVersion: "v1",
            kind: "ConfigMap",
            metadata: {
                name: "my-map"
            }
        }
    ]
});
import pulumi
from pulumi_kubernetes.yaml.v2 import ConfigGroup

example = ConfigGroup(
    "example",
    objs=[
        {
            "apiVersion": "v1",
            "kind": "ConfigMap",
            "metadata": {
                "name": "my-map",
            },
        }
    ]
)
package main

import (
	yamlv2 "github.com/pulumi/pulumi-kubernetes/sdk/v4/go/kubernetes/yaml/v2"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_, err := yamlv2.NewConfigGroup(ctx, "example", &yamlv2.ConfigGroupArgs{
			Objs: pulumi.Array{
				pulumi.Map{
					"apiVersion": pulumi.String("v1"),
					"kind":       pulumi.String("ConfigMap"),
					"metadata": pulumi.Map{
						"name": pulumi.String("my-map"),
					},
				},
			},
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using Pulumi;
using Pulumi.Kubernetes.Types.Inputs.Yaml.V2;
using Pulumi.Kubernetes.Yaml.V2;
using System.Collections.Generic;

return await Deployment.RunAsync(() =>
{
    var example = new ConfigGroup("example", new ConfigGroupArgs
    {
        Objs = new[]
        {
            new Dictionary<string, object>
            {
                ["apiVersion"] = "v1",
                ["kind"] = "ConfigMap",
                ["metadata"] = new Dictionary<string, object>
                {
                    ["name"] = "my-map",
                },
            },
        },
    });
});
package myproject;

import java.util.Map;

import com.pulumi.Pulumi;
import com.pulumi.kubernetes.yaml.v2.ConfigGroup;
import com.pulumi.kubernetes.yaml.v2.ConfigGroupArgs;

public class App {
    public static void main(String[] args) {
        Pulumi.run(ctx -> {
            var example = new ConfigGroup("example", ConfigGroupArgs.builder()
                    .objs(Map.ofEntries(
                        Map.entry("apiVersion", "v1"),
                        Map.entry("kind", "ConfigMap"),
                        Map.entry("metadata", Map.ofEntries(
                            Map.entry("name", "my-map")
                        ))
                    ))
                    .build());
        });
    }
}
name: example
runtime: yaml
resources:
  example:
    type: kubernetes:yaml/v2:ConfigGroup
    properties:
      objs:
      - apiVersion: v1
        kind: ConfigMap
        metadata:
          name: my-map

The yaml property accepts a string containing Kubernetes YAML. This works well for simple resources like ConfigMaps or for generating manifests programmatically. For larger deployments, file-based approaches are more maintainable.

Beyond these examples

These snippets focus on specific ConfigGroup features: file-based and inline YAML deployment, and glob pattern matching for directories. They’re intentionally minimal rather than full application deployments.

The examples assume pre-existing infrastructure such as a Kubernetes cluster with configured kubeconfig, and YAML manifest files for file-based examples. They focus on how to load YAML rather than what resources to create.

To keep things focused, common ConfigGroup patterns are omitted, including:

  • Resource name prefixing (resourcePrefix)
  • Skipping reconciliation waits (skipAwait)
  • Explicit dependency ordering (config.kubernetes.io/depends-on annotation)
  • Structured object input (objs property)

These omissions are intentional: the goal is to illustrate how each YAML loading method works, not provide drop-in application modules. See the ConfigGroup resource reference for all available configuration options.

Let's deploy Kubernetes Resources from YAML with ConfigGroup

Get started with Pulumi Cloud, then follow our quick setup guide to deploy this infrastructure.

Try Pulumi Cloud for FREE

Frequently Asked Questions

Resource Ordering & Dependencies
How does Pulumi determine the order to create and delete resources?
Pulumi uses heuristics to determine the apply and delete order for resources within a ConfigGroup. It also waits for each resource to be fully reconciled unless skipAwait is enabled.
How do I explicitly control the order resources are created?
Use the config.kubernetes.io/depends-on annotation with comma-delimited resource references. For namespace-scoped resources, use <group>/namespaces/<namespace>/<kind>/<name>. For cluster-scoped resources, use <group>/<kind>/<name>. Use an empty string for the core group.
Can I reference resources outside the ConfigGroup in the depends-on annotation?
No, the config.kubernetes.io/depends-on annotation only supports references to resources within the same ConfigGroup. To create dependencies between ConfigGroups, use Pulumi’s dependsOn resource option.
Should I deploy Custom Resource Definitions in the same ConfigGroup as resources that use them?
No, it’s a best practice to deploy each application using its own ConfigGroup, especially when installing CRDs. Use the dependsOn resource option to ensure the CRD ConfigGroup is deployed before dependent resources.
YAML Input Methods
What are the different ways to supply Kubernetes manifests to ConfigGroup?

You have four options:

  1. Files - Use files with paths or URLs
  2. File patterns - Use files with glob patterns like ./manifests/*.yaml
  3. Literal YAML - Use yaml with a YAML string
  4. Objects - Use objs with resource configuration objects
How do I load multiple manifest files using a pattern?
Use glob patterns in the files property, such as files: ["./manifests/*.yaml"] to match all YAML files in a directory.
Configuration & Behavior
What does the skipAwait property do?
Setting skipAwait to true causes child resources to skip the await logic, meaning Pulumi won’t wait for resources to be fully reconciled before proceeding.
How do I customize the names of resources created by ConfigGroup?
Use the resourcePrefix property to set a prefix for auto-generated resource names. It defaults to the name of the ConfigGroup. For example, resourcePrefix: "foo" creates resources named foo-resourceName.