Kustomize は、Kubernetes の YAML を「共通部分(base)」と「環境ごとの差分(overlay)」に分けて書き、kustomize build で1つの YAML に組み立てるツールです。 テンプレート言語は使わず、元の YAML に「上書き」を重ねる方式です。kubectl にも組み込まれていて、kubectl apply -k で適用できます。

app/
├── base/
│   ├── kustomization.yaml
│   ├── deployment.yaml
│   └── service.yaml
└── overlays/
    ├── dev/
    │   └── kustomization.yaml
    └── prod/
        ├── kustomization.yaml
        └── resources-patch.yaml
kustomize build overlays/prod          # 組み立てた YAML を表示(クラスタには触らない)
kubectl kustomize overlays/prod        # kubectl 組み込み版で同じことをする
kubectl apply -k overlays/prod         # 組み立てて適用

よく使う書き方の早見表です(この記事で、それぞれ build の出力を確かめました)。

やりたいこと kustomization.yaml のフィールド 例
元の YAML を読み込む resources - ../../base
名前の前に環境名を付ける namePrefix / nameSuffix namePrefix: prod-
Namespace をそろえる namespace namespace: prod
イメージのタグ・レジストリを変える images newName・newTag・digest
レプリカ数を変える replicas - name: web / count: 3
ConfigMap・Secret を作る configMapGenerator / secretGenerator literals・files・envs
一部の項目を上書き・追加する patches strategic merge か JSON6902
ラベルを付ける labels includeSelectors・includeTemplates
機能を部品として足す components 監視の設定などを複数の overlay で共有

確認環境:kustomize v5.8.3(GitHub リリースの linux_amd64 バイナリ)、Linux。この記事の YAML はすべて kustomize build を実行して、出力をそのまま載せています。フィールドの仕様は Kustomize の公式リファレンス(kubectl.docs.kubernetes.io)で確かめました(確認日 2026年10月13日)。

本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。

(記載例)出典:株式会社RJC「Kustomizeの使い方|base・overlay・patchの書き方」

インストールと kubectl 組み込み版の違い

Kustomize には、単体のバイナリと、kubectl に組み込まれたものがあります。

単体の kustomize kubectl 組み込み(-k)
入手 GitHub リリースのバイナリ、Homebrew など kubectl に入っている
版 自分で選べる(この記事は v5.8.3) kubectl の版で決まる。README の表では kubectl v1.27 が kustomize v5.0.1
edit サブコマンド ある(kustomize edit set image など) ない
# Linux(amd64)。版は GitHub のリリース一覧で確かめる
curl -sSL -o kustomize.tgz \
  "https://github.com/kubernetes-sigs/kustomize/releases/download/kustomize%2Fv5.8.3/kustomize_v5.8.3_linux_amd64.tar.gz"
tar xzf kustomize.tgz
./kustomize version
# v5.8.3

kubectl に組み込まれた版は kubectl version --client の Kustomize Version で見られます。CI で build 結果を比べるときは、手元と CI で同じ版を使うと出力の違いが出にくくなります。

base を作る

base には、どの環境でも共通の YAML を置きます。

# base/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: web
spec:
  replicas: 1
  selector:
    matchLabels:
      app: web
  template:
    metadata:
      labels:
        app: web
    spec:
      containers:
        - name: web
          image: nginx:1.27
          ports:
            - containerPort: 80
          envFrom:
            - configMapRef:
                name: web-config
          resources:
            requests:
              cpu: 100m
              memory: 128Mi
# base/service.yaml
apiVersion: v1
kind: Service
metadata:
  name: web
spec:
  selector:
    app: web
  ports:
    - port: 80
      targetPort: 80
# base/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
  - deployment.yaml
  - service.yaml
labels:
  - pairs:
      app.kubernetes.io/name: web
    includeSelectors: false
    includeTemplates: true
configMapGenerator:
  - name: web-config
    literals:
      - LOG_LEVEL=info
      - FEATURE_X=false

kustomize build base の出力です(Deployment の後半は省略)。

apiVersion: v1
data:
  FEATURE_X: "false"
  LOG_LEVEL: info
kind: ConfigMap
metadata:
  labels:
    app.kubernetes.io/name: web
  name: web-config-8tbtckf5t2
---
apiVersion: v1
kind: Service
metadata:
  labels:
    app.kubernetes.io/name: web
  name: web
spec:
  ports:
  - port: 80
    targetPort: 80
  selector:
    app: web
---
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app.kubernetes.io/name: web
  name: web
spec:
  replicas: 1
  selector:
    matchLabels:
      app: web
  template:
    metadata:
      labels:
        app: web
        app.kubernetes.io/name: web
    spec:
      containers:
      - envFrom:
        - configMapRef:
            name: web-config-8tbtckf5t2

ここで分かることが3つあります。

  • ConfigMap の名前に -8tbtckf5t2 が付き、Deployment の configMapRef も自動で書き換わりました。 中身から作るハッシュ(後述)です。
  • labels の includeSelectors: false で、Service の selector と Deployment の matchLabels は変わっていません。 includeTemplates: true で、Pod のテンプレートのラベルには付きました。
  • 出力は ConfigMap → Service → Deployment の順に並び替えられています。

overlay を作る(prod)

overlay は resources で base を読み込み、差分だけを書きます。

# overlays/prod/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: prod
namePrefix: prod-
resources:
  - ../../base
images:
  - name: nginx
    newName: registry.example.com/web/nginx
    newTag: 1.27.3
replicas:
  - name: web
    count: 3
configMapGenerator:
  - name: web-config
    behavior: merge
    literals:
      - LOG_LEVEL=warn
patches:
  - path: resources-patch.yaml
  - target:
      kind: Deployment
      name: web
    patch: |-
      - op: add
        path: /spec/template/spec/containers/0/env
        value:
          - name: TZ
            value: Asia/Tokyo
# overlays/prod/resources-patch.yaml(strategic merge patch)
apiVersion: apps/v1
kind: Deployment
metadata:
  name: web
spec:
  template:
    spec:
      containers:
        - name: web
          resources:
            requests:
              cpu: 500m
              memory: 512Mi
            limits:
              memory: 512Mi

kustomize build overlays/prod の Deployment と ConfigMap です。

apiVersion: v1
data:
  FEATURE_X: "false"
  LOG_LEVEL: warn
kind: ConfigMap
metadata:
  labels:
    app.kubernetes.io/name: web
  name: prod-web-config-g954k2b5ht
  namespace: prod
---
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app.kubernetes.io/name: web
  name: prod-web
  namespace: prod
spec:
  replicas: 3
  selector:
    matchLabels:
      app: web
  template:
    metadata:
      labels:
        app: web
        app.kubernetes.io/name: web
    spec:
      containers:
      - env:
        - name: TZ
          value: Asia/Tokyo
        envFrom:
        - configMapRef:
            name: prod-web-config-g954k2b5ht
        image: registry.example.com/web/nginx:1.27.3
        name: web
        ports:
        - containerPort: 80
        resources:
          limits:
            memory: 512Mi
          requests:
            cpu: 500m
            memory: 512Mi
書いたもの 出力での変化
namePrefix: prod- web → prod-web、ConfigMap も prod-web-config-…、参照も追従
namespace: prod すべてのリソースに namespace: prod
images nginx:1.27 → registry.example.com/web/nginx:1.27.3
replicas 1 → 3
configMapGenerator(behavior: merge) LOG_LEVEL だけ warn に。FEATURE_X は base の値が残った
strategic merge patch resources が置き換わり、limits が追加された
JSON6902 patch env に TZ が追加された

patches の name: web は、namePrefix を付ける前の名前(base の名前)で書きます。 出力の名前は prod-web ですが、web と書いて当たりました。

dev の overlay も同じ形で作り、違いを比べられます。

# overlays/dev/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: dev
namePrefix: dev-
resources:
  - ../../base
configMapGenerator:
  - name: web-config
    behavior: merge
    literals:
      - LOG_LEVEL=debug
      - FEATURE_X=true
diff <(kustomize build overlays/dev) <(kustomize build overlays/prod)

patches の書き方:strategic merge と JSON6902

patches には2つの形式があります。昔の patchesStrategicMerge・patchesJson6902 は、どちらも patches 1つにまとまりました。

strategic merge patch JSON6902 patch
書き方 元と同じ形の YAML の一部 op・path・value の並び
対象の決め方 patch の中の kind・metadata.name(target は省略できる) target が必須
リストの扱い containers などは name で突き合わせてマージ 番号(/containers/0)で指定
向いている場面 項目の追加・上書き、コンテナの追加 1か所だけ変える、削除する、リストの特定の位置

ファイルで書く(path)か、その場で書く(patch: |-)かは、どちらの形式でも選べます。

target の指定項目

項目 例 注意
kind Deployment
name web、web-.* 前後が固定された正規表現。web は web だけに一致
namespace prod 正規表現
labelSelector app=web リソースの metadata.labels に対して判定
annotationSelector team=shop
group / version apps / v1

labelSelector は、Pod テンプレートのラベルではなく、リソース自身の metadata.labels で判定されます。 base の Deployment は metadata.labels に app: web を持っていないため、labelSelector: app=web の patch はどれにも当たらず、エラーも出ないまま何も変わりませんでした。 当たったかどうかは build の出力で確かめます。

strategic merge でできること

patches:
  # コンテナを追加(名前が違うので追加になる)
  - patch: |-
      apiVersion: apps/v1
      kind: Deployment
      metadata:
        name: web
      spec:
        template:
          spec:
            containers:
              - name: log-agent
                image: busybox:1.37
                args: ["sh", "-c", "tail -F /var/log/app.log"]
  # リソースごと消す
  - patch: |-
      $patch: delete
      apiVersion: v1
      kind: Service
      metadata:
        name: web

build すると、Service が消え、containers は log-agent が先頭、web が2番目になりました。このあとに /spec/template/spec/containers/0/... の JSON6902 patch を書くと、log-agent を指してしまいます。 実際、containers/0/resources を消す patch は次のエラーになりました。

Error: error in remove for path: '/spec/template/spec/containers/0/resources': Unable to remove nonexistent key: resources: missing value

コンテナが複数あるときは、番号で指す JSON6902 より、name で突き合わせる strategic merge のほうが崩れにくくなります。

JSON6902 の op 一覧

op 意味 例
add 追加。リストの末尾は /-(ports/- で 2つ目の port が足された) path: /spec/template/spec/containers/0/ports/-
replace 置き換え。その項目が無いとエラー path: /spec/replicas
remove 削除。無いとエラー path: /spec/template/spec/containers/0/resources
copy / move from から複製・移動 from: /metadata/labels
test 値が一致するか確かめる。違えば testing value /spec/replicas failed: test failed で build が止まった path: /spec/replicas・value: 1

configMapGenerator と secretGenerator

Generator は、ファイルや値から ConfigMap・Secret を作り、名前の後ろに中身のハッシュを付けます。 中身を変えると名前が変わり、それを参照する Deployment の Pod テンプレートも変わるので、ConfigMap を変えただけで Pod が入れ替わります。

configMapGenerator:
  - name: web-config
    envs:
      - app.env               # KEY=VALUE の行をそのまま data に
  - name: nginx-conf
    files:
      - default.conf=nginx.conf   # キー名=ファイル名
    options:
      disableNameSuffixHash: true  # ハッシュを付けない
secretGenerator:
  - name: db-secret
    literals:
      - password=s3cr3t
    type: Opaque

build の結果です。

生成物 名前 data
web-config(envs) web-config-4k4hb6fd6t DB_HOST: db、DB_PORT: "5432"
nginx-conf(files・ハッシュなし) nginx-conf キー default.conf に nginx.conf の中身
db-secret db-secret-25k92892fb password: czNjcjN0(base64)

app.env の DB_PORT を 5433 に変えて build し直すと、名前が web-config-h828979gh6 に変わり、Deployment の参照も同じ名前になりました。 disableNameSuffixHash: true にした ConfigMap は、中身を変えても Pod は入れ替わりません。

overlay で base の ConfigMap を変えるときは behavior を指定します。

behavior 動き
create(既定) 新しく作る。base に同じ名前があると can not use behavior: 'create', behavior must be merge or replace のエラー
merge base の値に足す・上書きする(prod の例)
replace base の値を全部入れ替える

secretGenerator の値は base64 にされるだけで、暗号化はされません。Git に平文のパスワードを置かないよう、本番の値は外部のシークレット管理(External Secrets、Sealed Secrets など)と組み合わせる方法がよく使われます。

images:タグ・レジストリ・ダイジェスト

images:
  - name: nginx                              # 元の YAML のイメージ名
    newName: registry.example.com/web/nginx  # レジストリやリポジトリを変える
    newTag: 1.27.3                           # タグを変える
images:
  - name: nginx
    digest: sha256:0000…0000   # ダイジェストで固定(タグの代わり)

digest を指定すると、出力は image: nginx@sha256:0000…0000 の形になりました。CI では kustomize edit set image でタグを書き換える使い方がよくあります。

cd overlays/prod
kustomize edit set image nginx=registry.example.com/web/nginx:1.27.4

labels と commonLabels(非推奨)

commonLabels は非推奨で、labels に置き換わりました。 v5.8.3 で commonLabels を使うと、build の先頭に次の警告が出ました。

# Warning: 'commonLabels' is deprecated. Please use 'labels' instead. Run 'kustomize edit fix' to update your Kustomization automatically.
書き方 metadata.labels Pod テンプレート selector(matchLabels・Service)
commonLabels 付く 付く 付く
labels(既定) 付く 付かない 付かない
labels + includeTemplates: true 付く 付く 付かない
labels + includeSelectors: true 付く 付く 付く

Deployment の spec.selector は、作ったあとに変えられません。 commonLabels や includeSelectors: true で、すでに動いている Deployment にラベルを足すと、selector が変わって適用に失敗します。あとから足すラベルは includeSelectors: false にします。

kustomize edit add label も、何も付けないと commonLabels に書き込みました。--without-selector を付けると labels に書き込まれました。

kustomize edit add label env:staging --without-selector
# labels:
# - pairs:
#     env: staging

非推奨フィールドを一括で直す:kustomize edit fix

commonLabels・patchesStrategicMerge を使った kustomization.yaml で kustomize edit fix を実行すると、次のように書き換わりました。

Fixed fields:
  patchesJson6902 -> patches
  patchesStrategicMerge -> patches
  commonLabels -> labels

To convert vars -> replacements, run the command `kustomize edit fix --vars`
# 変換後
labels:
- includeSelectors: true
  pairs:
    team: shop
patches:
- path: p.yaml

commonLabels は includeSelectors: true の labels に変換され、selector に付く動きはそのままでした。bases も非推奨で、使うと # Warning: 'bases' is deprecated. Please use 'resources' instead. が出ました。

components:機能を部品にして複数の overlay で使う

Component は、複数の overlay で「付けたり外したり」したい設定をまとめる仕組みです。 監視用のアノテーションを部品にした例です。

# components/monitoring/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1alpha1
kind: Component
patches:
  - target:
      kind: Deployment
    patch: |-
      - op: add
        path: /spec/template/metadata/annotations
        value:
          prometheus.io/scrape: "true"
          prometheus.io/port: "9113"
# overlays/staging/kustomization.yaml
resources:
  - ../../base
components:
  - ../../components/monitoring

build すると、Deployment の Pod テンプレートに prometheus.io/scrape: "true" と prometheus.io/port: "9113" が付きました。kind: Component の apiVersion は v1alpha1 です。overlay(kind: Kustomization)との違いは、読み込んだ overlay の中で、overlay 自身の resources を対象に patch などを適用する点です。overlay ごとに components に書くかどうかで、機能を付け外しできます。

kustomize edit:kustomization.yaml をコマンドで書き換える

コマンド 書き込まれる内容
kustomize create --resources ../base resources: [../base] の kustomization.yaml を作る
kustomize edit set image nginx=nginx:1.27.4 images: に newName: nginx・newTag: 1.27.4
kustomize edit set namespace staging namespace: staging
kustomize edit set replicas web=2 replicas: に name: web・count: 2
kustomize edit add component ../comp/monitoring components: に追加
kustomize edit add label env:staging --without-selector labels: に追加
kustomize edit fix 非推奨フィールドを新しい書き方に変換

よく出るエラーと原因

エラー文(先頭) 原因 直し方
no resource matches strategic merge patch "Deployment.v1.apps/webb.[noNs]" patch の metadata.name が base と違う base の名前(prefix を付ける前)にそろえる
must specify a target for JSON patch JSON6902 に target が無い target: を書く
replace operation does not apply: doc is missing path replace の対象の項目が無い 無い項目は add を使う
Unable to remove nonexistent key remove の対象が無い、番号がずれた build の出力で位置を確かめる
security; file '…/base/deployment.yaml' is not in or below '…/overlays/prod' resources で、別のディレクトリのファイルを直接指定 ディレクトリ(../../base)を指定する
may not add resource with an already registered id: Deployment.v1.apps/web.[noNs] 同じリソースを2回読み込んだ resources の重複をなくす
invalid Kustomization: json: unknown field "resource" フィールド名の打ち間違い resources
unable to find one of 'kustomization.yaml', 'kustomization.yml' or 'Kustomization' 指定したディレクトリに kustomization.yaml が無い ディレクトリを確かめる

どれも kustomize build の段階で出るエラーです。 クラスタに適用する前に、CI で全 overlay を build しておくと、マージ前に見つけられます。

# CI の例:全 overlay を build して、失敗したら止める
for d in overlays/*/; do
  echo "== $d"
  kustomize build "$d" > /dev/null || exit 1
done

Helm との違い

Kustomize Helm
書き方 ふつうの YAML + 上書き テンプレート({{ .Values.xxx }})
環境ごとの差分 overlay のディレクトリ values ファイル
配布 ディレクトリや Git の URL チャート(リポジトリ・OCI)
リリース管理 しない(kubectl apply -k) helm upgrade・rollback で履歴を持つ
組み合わせ helmCharts(--enable-helm が必要)で Helm チャートを読み込める

Helm の書き方は、関連記事「Helm入門|チャートの作り方とコマンド一覧」で扱っています。

よくある質問

Q. kubectl apply -k と kustomize build | kubectl apply -f - は同じですか。 A. どちらも組み立てた YAML を適用します。違いは使われる Kustomize の版で、-k は kubectl に組み込まれた版、パイプは単体のバイナリの版です。新しいフィールドを使うときは、単体のバイナリの版で build してから渡すと、版の違いを避けられます。

Q. overlay から base 以外のディレクトリのファイルを読みたいです。 A. 既定では、kustomization.yaml のあるディレクトリより外のファイルを直接は読めません(--load-restrictor の既定は LoadRestrictionsRootOnly)。外のディレクトリは、そのディレクトリに kustomization.yaml を置いて resources で読み込みます。--load-restrictor LoadRestrictionsNone で外せますが、ヘルプには「kustomization の移動のしやすさが失われる」とあります。

Q. 値を別のリソースにコピーしたい(Service 名を環境変数に入れるなど)。 A. replacements を使います。以前の vars は非推奨で、kustomize edit fix --vars で変換できます。

Q. Git に置いた overlay を自動でクラスタに反映したいです。 A. Argo CD は、Application の source.path に kustomization.yaml があるディレクトリを指定すると、Kustomize として組み立てて同期します。関連記事「Argo CD入門|Applicationの書き方と同期の仕組み」で扱っています。

Q. Kubernetes の YAML の書き方から確認したいです。 A. Deployment・Service などの基本は、関連記事「Kubernetes入門|主要リソースとYAMLの書き方、kubectl早見表」で扱っています。

まとめ

  • base に共通の YAML、overlay に差分。kustomize build で出力を確かめてから適用する
  • images・replicas・namespace・namePrefix は専用のフィールドで書ける
  • patches の name は base の名前で書く。JSON6902 は target が必須、番号の指定はずれに注意
  • configMapGenerator は中身のハッシュを名前に付け、変えると Pod が入れ替わる
  • commonLabels は非推奨で selector まで変える。labels を使い、kustomize edit fix で一括変換できる

参考・出典

確認日はいずれも 2026年10月13日です。

  • Kustomize リファレンス「kustomization」(フィールド一覧):https://kubectl.docs.kubernetes.io/references/kustomize/kustomization/
  • Kustomize リファレンス「patches」:https://kubectl.docs.kubernetes.io/references/kustomize/kustomization/patches/
  • Kustomize リファレンス「labels」:https://kubectl.docs.kubernetes.io/references/kustomize/kustomization/labels/
  • Kustomize リファレンス「configMapGenerator」:https://kubectl.docs.kubernetes.io/references/kustomize/kustomization/configmapgenerator/
  • Kustomize リファレンス「components」:https://kubectl.docs.kubernetes.io/references/kustomize/kustomization/components/
  • kubernetes-sigs/kustomize(README・リリース v5.8.3):https://github.com/kubernetes-sigs/kustomize
  • Kubernetes ドキュメント「Declarative Management of Kubernetes Objects Using Kustomize」:https://kubernetes.io/docs/tasks/manage-kubernetes-objects/kustomization/

本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。

(記載例)出典:株式会社RJC「Kustomizeの使い方|base・overlay・patchの書き方」

株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。

WE ARE HIRING

RJCで一緒に開発しながら、
成長を楽しみませんか?

RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!

  • 127日年間休日
  • 12時間平均残業時間
  • 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!

ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。

ABOUT RJC RJCがどんな会社か知る 考え方、研修、働き方、福利厚生、社員の前職まで。RJCのことが丸わかり! RJC丸わかりページへ JOB DESCRIPTION 仕事内容・待遇を見てみる 仕事内容、給与・待遇、選考の流れ。経験者も未経験も!応募前に知りたいこと、まとめました! 募集要項を見る ENTRY エントリーする エントリーは1〜2分・履歴書不要です。まずは話を聞いてみたい、という方でも歓迎です! エントリーフォームへ

RJCで一緒に開発しながら、 成長を楽しみませんか?