Kubernetes Manifests
The manifests resource type lets you apply raw Kubernetes YAML manifests to your clusters. Manifests support Go templating for dynamic configuration.
Basic Usage
spec:
resources:
manifests:
- name: app-config
cluster: main
namespace: default
template: |
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
data:
environment: production
log_level: info Schema Reference
manifests:
- name: string # Required: Unique identifier for this manifest
cluster: string # Required: Target cluster name
namespace: string # Optional: Target namespace (default: default)
enabled: boolean # Optional: Enable/disable this manifest (default: true)
count: integer # Optional: Create multiple instances with {{ .Index }}
template: string # Required: Kubernetes manifest YAML with Go templates
waitFor: # Optional: Wait conditions
ready: boolean # Wait for resources to be ready
timeout: duration # Timeout for wait (default: 5m)
dependsOn: # Optional: Resource dependencies
- string # Names of resources that must be created first Templating
Manifests support full Go template syntax with access to variables, environment data, and other resources.
Variable Substitution
spec:
variables:
- name: app_replicas
type: integer
default: 3
- name: app_image
type: string
default: "nginx:1.25"
resources:
manifests:
- name: deployment
cluster: main
template: |
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-app
spec:
replicas: {{ .Variables.app_replicas }}
selector:
matchLabels:
app: web
template:
metadata:
labels:
app: web
spec:
containers:
- name: app
image: {{ .Variables.app_image }}
ports:
- containerPort: 80 Environment Context
Access environment-level data:
template: |
apiVersion: v1
kind: ConfigMap
metadata:
name: env-info
data:
environment_id: "{{ .Environment.ID }}"
environment_name: "{{ .Environment.Name }}"
domain: "{{ .Environment.Domain }}"
created_at: "{{ .Environment.CreatedAt }}" Resource References
Reference outputs from other resources:
spec:
resources:
secrets:
- name: db-password
type: generated
spec:
length: 24
manifests:
- name: db-secret
cluster: main
template: |
apiVersion: v1
kind: Secret
metadata:
name: database-credentials
type: Opaque
stringData:
password: "{{ .Resources.secrets.db-password.value }}" Per-Participant Manifests
Create resources for each participant using count and {{ .Index }}:
spec:
variables:
- name: participant_count
type: integer
default: 10
resources:
manifests:
# Create a namespace for each participant
- name: participant-namespaces
cluster: main
count: "{{ .Variables.participant_count }}"
template: |
apiVersion: v1
kind: Namespace
metadata:
name: participant-{{ .Index }}
labels:
teabar.dev/participant: "{{ .Index }}"
teabar.dev/environment: "{{ .Environment.ID }}"
# Create resource quotas for each participant
- name: participant-quotas
cluster: main
count: "{{ .Variables.participant_count }}"
template: |
apiVersion: v1
kind: ResourceQuota
metadata:
name: participant-quota
namespace: participant-{{ .Index }}
spec:
hard:
requests.cpu: "2"
requests.memory: 4Gi
limits.cpu: "4"
limits.memory: 8Gi
pods: "20" Note
.Index variable is zero-based. For participant 1, use participant-0.Multi-Document Manifests
Include multiple Kubernetes resources in a single manifest using YAML document separators:
manifests:
- name: monitoring-stack
cluster: main
namespace: monitoring
template: |
apiVersion: v1
kind: Namespace
metadata:
name: monitoring
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: prometheus
namespace: monitoring
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: prometheus
rules:
- apiGroups: [""]
resources: ["nodes", "pods", "services"]
verbs: ["get", "list", "watch"] Conditional Resources
Use Go template conditionals to include or exclude resources:
spec:
variables:
- name: enable_network_policies
type: boolean
default: true
resources:
manifests:
- name: network-policies
cluster: main
template: |
{{- if .Variables.enable_network_policies }}
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: deny-all
namespace: default
spec:
podSelector: {}
policyTypes:
- Ingress
- Egress
{{- end }} Wait Conditions
Wait for resources to be ready before continuing:
manifests:
- name: database
cluster: main
waitFor:
ready: true
timeout: 10m
template: |
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: postgres
spec:
serviceName: postgres
replicas: 1
selector:
matchLabels:
app: postgres
template:
metadata:
labels:
app: postgres
spec:
containers:
- name: postgres
image: postgres:15
ports:
- containerPort: 5432 Dependencies
Ensure manifests are applied in the correct order:
manifests:
# First: Create the namespace
- name: app-namespace
cluster: main
template: |
apiVersion: v1
kind: Namespace
metadata:
name: myapp
# Second: Create the ConfigMap (depends on namespace)
- name: app-config
cluster: main
dependsOn:
- app-namespace
template: |
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
namespace: myapp
data:
config.yaml: |
debug: false
# Third: Create the Deployment (depends on ConfigMap)
- name: app-deployment
cluster: main
dependsOn:
- app-config
template: |
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
namespace: myapp
spec:
replicas: 2
selector:
matchLabels:
app: myapp
template:
metadata:
labels:
app: myapp
spec:
containers:
- name: app
image: myapp:latest
volumeMounts:
- name: config
mountPath: /etc/app
volumes:
- name: config
configMap:
name: app-config Built-in Template Functions
Manifests have access to all Go template functions plus Teabar helpers:
| Function | Description | Example |
|---|---|---|
toYaml | Convert to YAML | {{ .data \| toYaml }} |
toJson | Convert to JSON | {{ .data \| toJson }} |
indent | Indent lines | {{ .content \| indent 4 }} |
nindent | Newline + indent | {{ .content \| nindent 4 }} |
b64enc | Base64 encode | {{ .secret \| b64enc }} |
b64dec | Base64 decode | {{ .encoded \| b64dec }} |
quote | Quote string | {{ .value \| quote }} |
lower | Lowercase | {{ .name \| lower }} |
upper | Uppercase | {{ .name \| upper }} |
replace | Replace string | {{ .name \| replace "-" "_" }} |
Example: Complex Templating
manifests:
- name: participant-rbac
cluster: main
count: "{{ .Variables.participant_count }}"
template: |
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: participant-role
namespace: participant-{{ .Index }}
rules:
{{- range .Variables.allowed_resources }}
- apiGroups: ["{{ .apiGroup | default "" }}"]
resources: {{ .resources | toJson }}
verbs: {{ .verbs | toJson }}
{{- end }}
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: participant-binding
namespace: participant-{{ .Index }}
subjects:
- kind: ServiceAccount
name: participant-{{ .Index }}
namespace: participant-{{ .Index }}
roleRef:
kind: Role
name: participant-role
apiGroup: rbac.authorization.k8s.io Best Practices
Use Namespaces
Always specify namespaces explicitly rather than relying on defaults:
# Good
manifests:
- name: config
cluster: main
namespace: myapp
template: |
apiVersion: v1
kind: ConfigMap
# ...
# Avoid
manifests:
- name: config
cluster: main
template: |
apiVersion: v1
kind: ConfigMap
metadata:
namespace: myapp # Harder to track Separate Concerns
Split complex deployments into logical manifest groups:
manifests:
- name: database-resources
cluster: main
template: |
# Database-related resources
- name: app-resources
cluster: main
dependsOn: [database-resources]
template: |
# Application resources
- name: ingress-resources
cluster: main
dependsOn: [app-resources]
template: |
# Ingress and networking Validate Templates
Use the CLI to validate your manifests before deploying:
# Validate blueprint including manifest templates
teactl blueprint validate my-blueprint.yaml
# Render manifests to see the output
teactl blueprint render my-blueprint.yaml --var participant_count=5 Tip
Related Resources
- Helm Releases - Deploy packaged applications
- Clusters - Provision Kubernetes clusters
- Templating - Complete templating reference
- Secrets - Manage sensitive data