Skip to main content

Walkthrough: Build Up Your .platform/kubernetes/ Folder

The Platform Folder Anatomy page describes the finished .platform/kubernetes/ layout. This walkthrough builds that layout up one file at a time, so you can see what each piece adds.

The worked example is a bring-your-own ConfigMap: a plain Kubernetes ConfigMap you author yourself, wire into your base configuration, mount into your PlatformApplication, and then patch differently per environment. It's a small, concrete resource that exercises every part of the folder structure.

What You'll Learn

  • How to start from an empty .platform/kubernetes/ and add the base kustomization.yaml
  • What application.yaml and application_customizations.yaml do
  • How to bring your own ConfigMap and mount it into your application
  • How to add dev / stg / prd overlays and patch the ConfigMap per environment
  • How the rendered manifests reach your cluster through ArgoCD

Prerequisites

  • An application repository with a .platform/ folder (or an empty one to fill in).
  • Familiarity with the PlatformApplication resource — this walkthrough mounts a ConfigMap into one.
  • kubectl v1.21+ (or a standalone Kustomize v4+) to render the manifests locally with kubectl kustomize. The overlays use Kustomize's patches: field, which needs a newer Kustomize than the one kubectl bundled before v1.21.

The Big Picture

Everything under base/ is shared; each environment folder layers a small patch on top. You render one environment by pointing kubectl kustomize at its folder — e.g. kubectl kustomize .platform/kubernetes/dev (or .../stg, .../prd).


Step 1 — Start with the base kustomization.yaml

Create the base folder and its Kustomization. This file is the orchestrator — it lists the resources that make up your base configuration. Start with it empty; you'll add resources as you create them.

.platform/kubernetes/base/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization

resources:
- application.yaml
configurations:
- application_customizations.yaml
  • resources — the manifests Kustomize includes in the output.
  • configurations — extra transformer config; here it points at application_customizations.yaml (added next).

Step 2 — Add application.yaml and application_customizations.yaml

application.yaml is your main PlatformApplication manifest — the desired state of your app. Keep the common configuration here; environment-specific values come later via patches.

.platform/kubernetes/base/application.yaml
apiVersion: meta.p6m.dev/v1alpha1
kind: PlatformApplication
metadata:
name: web-application
spec:
deployment:
replicas: 3
image: my-app
config:
LOG_LEVEL: "info"

application_customizations.yaml tells Kustomize which field holds the container image, so CI image-tag updates land in the right place:

.platform/kubernetes/base/application_customizations.yaml
images:
- path: spec/deployment/image
kind: PlatformApplication
Two ways to configure your app

spec.config is a map of key/value pairs the operator injects as environment variables. That's ideal for a handful of settings. When you need to ship files — a properties file, a config document, TLS-adjacent data — bring your own ConfigMap and mount it, which is exactly what the next step does.

Step 3 — Bring your own ConfigMap

Author a plain Kubernetes ConfigMap. Nothing about it is platform-specific — this is the "bring your own" part.

.platform/kubernetes/base/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: web-application-config
data:
app.properties: |
log.level=info
feature.x.enabled=false
greeting.txt: "Hello from base"

Wire it into the base Kustomization by adding it to resources:

.platform/kubernetes/base/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization

resources:
- application.yaml
- configmap.yaml
configurations:
- application_customizations.yaml

Now mount it into the application. Add a volumeMounts entry to the PlatformApplication that points at the ConfigMap by name:

.platform/kubernetes/base/application.yaml
apiVersion: meta.p6m.dev/v1alpha1
kind: PlatformApplication
metadata:
name: web-application
spec:
deployment:
replicas: 3
image: my-app
volumeMounts:
- name: app-config
mountPath: /etc/app-config
source:
configMap:
name: web-application-config
config:
LOG_LEVEL: "info"

The platform-application-operator turns that volumeMounts entry into a pod volume and a container mount. The keys of your ConfigMap become files under mountPath (/etc/app-config/app.properties, /etc/app-config/greeting.txt). ConfigMap and Secret mounts default to read-only; set readOnly: false on the mount if your app needs to write there.

Config changes restart the pod automatically

Every Deployment the operator emits carries reloader.stakater.com/auto: "true". When you change the ConfigMap — including via the per-environment patch in the next step — Reloader rolls the Deployment so the pod picks up the new files. No manual restart needed.

Step 4 — Add environment overlays

Create one folder per environment. Each references ../base and layers on patches. Start with dev:

.platform/kubernetes/dev/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../base
patches:
- path: application_patch.yaml
- path: configmap_patch.yaml

Patch the application for dev — here, turning on debug logging and a feature flag:

.platform/kubernetes/dev/application_patch.yaml
apiVersion: meta.p6m.dev/v1alpha1
kind: PlatformApplication
metadata:
name: web-application
spec:
config:
LOG_LEVEL: "debug" # Override log level for dev
ENABLE_FEATURE_X: "true" # Enable feature X in dev

And patch the ConfigMap for dev. A patches entry matches the base resource by kind + name and strategic-merges the data keys you list — keys you don't mention keep their base values:

.platform/kubernetes/dev/configmap_patch.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: web-application-config
data:
greeting.txt: "Hello from dev"
app.properties: |
log.level=debug
feature.x.enabled=true

stg and prd follow the same shape with their own values — e.g. prd's configmap_patch.yaml sets greeting.txt: "Hello from production" and log.level=warn.

Step 5 — Render and verify

Render any environment locally to see the merged result:

kubectl kustomize .platform/kubernetes/dev

The dev output shows both patches applied — the ConfigMap carries the dev greeting and the PlatformApplication carries the dev config:

apiVersion: v1
data:
app.properties: |
log.level=debug
feature.x.enabled=true
greeting.txt: Hello from dev
kind: ConfigMap
metadata:
name: web-application-config
---
apiVersion: meta.p6m.dev/v1alpha1
kind: PlatformApplication
metadata:
name: web-application
spec:
config:
ENABLE_FEATURE_X: "true"
LOG_LEVEL: debug
deployment:
image: my-app
replicas: 3
volumeMounts:
- mountPath: /etc/app-config
name: app-config
source:
configMap:
name: web-application-config

Rendering stg or prd produces the same structure with that environment's values.

Step 6 — How it reaches the cluster

You don't apply these manifests by hand. The organization's .platform repository points ArgoCD at each environment overlay (see Platform Repository Anatomy and ArgoCD Integration). ArgoCD renders the overlay and applies the PlatformApplication and ConfigMap; the platform-application-operator then reconciles the PlatformApplication into a Deployment whose pod mounts your ConfigMap:

Mounts:
/etc/app-config from app-config (ro)
Volumes:
app-config:
Type: ConfigMap
Name: web-application-config

From here, editing configmap_patch.yaml in an environment and merging the change is all it takes — ArgoCD syncs the updated ConfigMap and Reloader restarts the pod onto the new files.

Final folder structure

.platform
└── kubernetes
├── base
│ ├── application.yaml
│ ├── application_customizations.yaml
│ ├── configmap.yaml
│ └── kustomization.yaml
├── dev
│ ├── application_patch.yaml
│ ├── configmap_patch.yaml
│ └── kustomization.yaml
├── stg
│ ├── application_patch.yaml
│ ├── configmap_patch.yaml
│ └── kustomization.yaml
└── prd
├── application_patch.yaml
├── configmap_patch.yaml
└── kustomization.yaml