GitOps Pipelines Example
2026-05-29
Edited: 2026-06-06
Pipeline Config
This is an example in GitLab, though I think it can be trivially ported elsewhere (me thinks)
stages:
# Put the fast jobs first, so they fail fast
- lint
- format
# Build and push your containers, test them to before deploying
- build
- test
# Make a git commit against the manifest repository updating the image tags
- deploy
# Example of building containers
.buildah:
image: quay.io/buildah/stable
variables:
STORAGE_DRIVER: vfs
BUILDAH_ISOLATION: chroot
before_script:
- buildah login -u "$CI_REGISTRY_USER" --password $CI_REGISTRY_PASSWORD $CI_REGISTRY
container:
extends: .buildah
stage: build
script:
- buildah build -t "$CI_REGISTRY_IMAGE/container:${CI_COMMIT_SHA}" ./container
- buildah push "$CI_REGISTRY_IMAGE/container:${CI_COMMIT_SHA}"
# Note: I no longer use this method, see below
make_commit:
image: alpine/git # Might be better to just an alpine container and add git manually
stage: deploy
script:
- git config --global user.name "Worker Bot"
- git config --global user.email "WorkerBot@evilplace.com"
- git clone "https://your.repo.com"
# Edit your manifests using whatever: yq, sed, awk
# This one changes the image version, but you can do whatever your heart desires
- yq -i '.images[].newTag = strenv(CI_COMMIT_SHA)' kustomization.yml
# Commit and push!
- git add kustomization.yaml
- git commit -m "Deploy ${CI_COMMIT_SHA} to dev!!!"
- git push origin mainAs an aside, when using sed, things get a bit trickly in regards to quoting, since you need quotes to expand the ENV variables, so you quote the whole thing and then use backslashes to escape the double quotes you want.
EDIT: I don't think making commits against the manifests repository is a good idea. If you store code in the same repository as your manifests, than it is probably fine. But as is the case with GitOps, the manifest repository is stored elsewhere from the code.
Having access tokens with write access to another repository is rather dangerous, and if you have multiple projects, then it isn't scalable to do this setup multiple times. Instead, even if you have to use polling, something like Renovate (running in manifests repository) or ArgoCD ImageUpdater (or Kargo) is better.
Kustomization.yaml
Generally you would have Kustomization.yaml file in base/ and then one in each of your overlays, for example
/base/Kustomization.yaml
/overlays/dev/Kustomization.yaml
/overlays/prod/Kustomization.yamlAnd in the file for dev, you might have something like this
namePrefix: dev-
resources:
- ../../base
- extra.yaml
- dev-specific-stuff.yaml
# Modify yaml files in base using patches, which are just JSON patch documents. You can pass in a file or just inline it
patches:
- target:
version: v1
group: route.openshift.io
kind: Route
name: shrunk
patch: |-
- op: replace
path: /spec/host
value: bleh.com
# k8 has builtin configuration for images without using patches
# Whenever you use frontend for an image, like the following
# image: frontend
# k8 will replace frontend with the give name and tag
images:
- name: frontend
newName: your.registry.com
newTag: "latest"