Skip to main content
Version: Next

GitOps with Flux CD

This tutorial walks through setting up a complete GitOps workflow with OpenChoreo and Flux CD using the sample-gitops repository. You will configure Flux to sync platform resources, build and deploy a multi-component application using OpenChoreo Workflows, and promote it across environments.

What you will learn:

  • How to set up Flux CD to sync OpenChoreo resources from Git
  • The structure of a GitOps repository for OpenChoreo (platform vs. projects)
  • How to build and deploy components using OpenChoreo Workflows
  • How ComponentReleases and ReleaseBindings drive environment promotion
  • How to promote across the development β†’ staging pipeline

Prerequisites​

Before you begin, ensure you have:

  • kubectl configured to access your cluster
  • git CLI installed
  • A GitHub account (to fork the sample repository)

Install OpenChoreo​

Follow the Run Locally on k3d guide to install OpenChoreo in your Kubernetes cluster.

warning

Do not install the OpenChoreo default resources. Only create the default ClusterDataPlane and ClusterWorkflowPlane.

Install Flux CD​

Install Flux CD in your cluster. Only the source-controller and kustomize-controller are required.

Follow the official Flux CD installation docs, or run:

kubectl apply -f https://github.com/fluxcd/flux2/releases/latest/download/install.yaml
note

This tutorial assumes you are using the k3d local setup from the "Try It Out" guide. If you are using a different cluster, adjust hostnames and ports accordingly.

Step 1: Fork and Clone the Sample Repository​

  1. Navigate to https://github.com/openchoreo/sample-gitops
  2. Click the Fork button in the top-right corner
  3. Clone your fork locally:
git clone https://github.com/<your-github-username>/sample-gitops.git
cd sample-gitops

Repository Structure​

The repository is organized to separate platform-level resources from application resources:

.
β”œβ”€β”€ flux/ # Flux CD configuration
β”‚ β”œβ”€β”€ gitrepository.yaml # Points Flux to this repo
β”‚ β”œβ”€β”€ namespaces-kustomization.yaml # Syncs namespaces/
β”‚ β”œβ”€β”€ platform-shared-kustomization.yaml # Syncs platform-shared/
β”‚ β”œβ”€β”€ oc-demo-platform-kustomization.yaml # Syncs platform/ (depends on namespaces, platform-shared)
β”‚ └── oc-demo-projects-kustomization.yaml # Syncs projects/ (depends on platform)
β”‚
β”œβ”€β”€ platform-shared/ # cluster-scoped resources
β”‚ └── cluster-workflow-templates/
β”‚ └── argo/
β”‚ β”œβ”€β”€ docker-with-gitops-release.yaml
β”‚ β”œβ”€β”€ google-cloud-buildpacks-gitops-release-template.yaml
β”‚ β”œβ”€β”€ react-gitops-release-template.yaml
β”‚ └── bulk-gitops-release-template.yaml
β”‚
└── namespaces/ # namespace-scoped resources
└── <namespace>/
β”œβ”€β”€ namespace.yaml
β”‚
β”œβ”€β”€ platform/ # platform-level resources (managed by platform team)
β”‚ β”œβ”€β”€ infra/
β”‚ β”‚ β”œβ”€β”€ deployment-pipelines/
β”‚ β”‚ β”‚ └── standard.yaml
β”‚ β”‚ └── environments/
β”‚ β”‚ β”œβ”€β”€ development.yaml
β”‚ β”‚ β”œβ”€β”€ staging.yaml
β”‚ β”‚ └── production.yaml
β”‚ β”œβ”€β”€ component-types/
β”‚ β”‚ β”œβ”€β”€ service.yaml
β”‚ β”‚ β”œβ”€β”€ webapp.yaml
β”‚ β”‚ β”œβ”€β”€ database.yaml
β”‚ β”‚ └── message-broker.yaml
β”‚ β”œβ”€β”€ traits/
β”‚ β”‚ β”œβ”€β”€ persistent-volume.yaml
β”‚ β”‚ β”œβ”€β”€ api-management.yaml
β”‚ β”‚ └── observability-alert-rule.yaml
β”‚ └── workflows/
β”‚ β”œβ”€β”€ bulk-gitops-release.yaml
β”‚ β”œβ”€β”€ docker-with-gitops.yaml
β”‚ β”œβ”€β”€ google-cloud-buildpacks-gitops-release.yaml
β”‚ └── react-gitops-release.yaml
β”‚
└── projects/ # application resources (managed by development teams)
└── <project-name>/
β”œβ”€β”€ project.yaml
└── components/
└── <component-name>/
β”œβ”€β”€ component.yaml
β”œβ”€β”€ workload.yaml
β”œβ”€β”€ releases/
β”‚ └── <component>-<date>-<revision>.yaml
└── release-bindings/
β”œβ”€β”€ <component>-development.yaml
└── <component>-staging.yaml
tip

The platform/ directory is synced first, ensuring Environments, DataPlanes, and ComponentTypes exist before any Components are created. Flux enforces this ordering through the dependsOn field in oc-demo-projects-kustomization.yaml.

note

The sample repository's platform-shared/ directory only includes Argo ClusterWorkflowTemplates. In a production setup, this directory would also contain other cluster-scoped resources such as ClusterComponentTypes, ClusterTraits, ClusterDataPlanes, and ClusterAuthzRoles. See the GitOps Overview for the full directory structure.

Step 2: Update Repository URLs​

Flux and the build Workflows need to know your fork's URL. Update the following files to point to your forked repository, then commit and push:

  1. flux/gitrepository.yaml β€” update the spec.url field:
apiVersion: source.toolkit.fluxcd.io/v1
kind: GitRepository
metadata:
name: sample-gitops
namespace: flux-system
spec:
interval: 1m
url: https://github.com/<your-github-username>/sample-gitops
ref:
branch: main
  1. Workflow files β€” update the gitops-repo-url parameter in each:
    • namespaces/default/platform/workflows/docker-with-gitops.yaml
    • namespaces/default/platform/workflows/google-cloud-buildpacks-gitops-release.yaml
    • namespaces/default/platform/workflows/react-gitops-release.yaml

Commit and push the changes to your fork:

git add -A
git commit -m "Update repository URLs to point to my fork"
git push origin main
note

If your fork is a private repository, you will need to configure a Flux secret for Git authentication. See the Flux secret create guide for details.

Step 3: Create Git Secrets​

OpenChoreo Workflows need access to your repositories to clone source code and push GitOps manifests. Store your GitHub Personal Access Token (PAT) in the OpenBao secret store:

  1. Generate a GitHub PAT with read/write access to your forked repository

  2. Store the secrets in OpenBao:

# Secret for cloning source repositories
kubectl exec -n openbao openbao-0 -- bao kv put secret/git-token git-token=<your_github_pat>

# Secret for pushing to and creating PRs in the GitOps repository
kubectl exec -n openbao openbao-0 -- bao kv put secret/gitops-token git-token=<your_github_pat>

Replace <your_github_pat> with your actual token.

Step 4: Deploy Flux Resources​

Apply all Flux resources to start syncing the repository with your cluster:

kubectl apply -f flux/

This creates five resources:

  • GitRepository (sample-gitops): Tells Flux to monitor your fork
  • Kustomization (namespaces): Syncs the namespaces/ directory (namespace definitions)
  • Kustomization (platform-shared): Syncs the platform-shared/ directory (cluster-scoped resources)
  • Kustomization (oc-demo-platform): Syncs the namespaces/default/platform/ directory (depends on namespaces and platform-shared)
  • Kustomization (oc-demo-projects): Syncs the namespaces/default/projects/ directory (depends on platform being ready first)

Verify the Flux resources were created:

kubectl get gitrepository,kustomization -n flux-system
tip

To trigger an immediate sync instead of waiting for the interval:

kubectl annotate gitrepository -n flux-system sample-gitops \
reconcile.fluxcd.io/requestedAt="$(date +%s)" --overwrite

Step 5: Verify Platform Resources​

Within 1-2 minutes, Flux syncs the namespaces/default/platform/ directory. Verify the platform resources were created:

kubectl get environments              # β†’ development, staging, production
kubectl get dataplanes # β†’ default
kubectl get deploymentpipelines # β†’ standard
kubectl get componenttypes # β†’ deployment/service, deployment/web-application, deployment/scheduled-task

The standard DeploymentPipeline defines the promotion flow: development β†’ staging (auto-promote) β†’ production (requires approval).

Step 6: Build and Deploy the Doclet Application​

The sample uses the Doclet application β€” a multi-component app with two backend services and a frontend. You deploy it by triggering OpenChoreo Workflow runs that build container images and create pull requests in your GitOps repository.

Available Workflows​

WorkflowWhen to Use
docker-gitops-releaseSource repo has a Dockerfile β€” works with any language
google-cloud-buildpacks-gitops-releaseSource repo has no Dockerfile β€” auto-detects Go, Java, Node.js, Python, .NET, Ruby, PHP
react-gitops-releaseReact or SPA apps β€” builds with Node.js and packages into nginx
bulk-gitops-releasePromote existing releases to a target environment (no build)

Build the Document Service​

kubectl apply -f - <<EOF
apiVersion: openchoreo.dev/v1alpha1
kind: WorkflowRun
metadata:
name: document-svc-manual-01
namespace: default
labels:
openchoreo.dev/project: "doclet"
openchoreo.dev/component: "document-svc"
spec:
workflow:
name: docker-gitops-release
kind: Workflow
parameters:
componentName: document-svc
projectName: doclet
docker:
context: /project-doclet-app/service-go-document
filePath: /project-doclet-app/service-go-document/Dockerfile
repository:
appPath: /project-doclet-app/service-go-document
revision:
branch: main
commit: ""
url: https://github.com/openchoreo/sample-workloads.git
workloadDescriptorPath: workload.yaml
EOF

Build the Collaboration Service​

kubectl apply -f - <<EOF
apiVersion: openchoreo.dev/v1alpha1
kind: WorkflowRun
metadata:
name: collab-svc-manual-01
namespace: default
labels:
openchoreo.dev/project: "doclet"
openchoreo.dev/component: "collab-svc"
spec:
workflow:
kind: Workflow
name: docker-gitops-release
parameters:
componentName: collab-svc
projectName: doclet
docker:
context: /project-doclet-app/service-go-collab
filePath: /project-doclet-app/service-go-collab/Dockerfile
repository:
appPath: /project-doclet-app/service-go-collab
revision:
branch: main
commit: ""
url: https://github.com/openchoreo/sample-workloads.git
workloadDescriptorPath: workload.yaml
EOF

Build the Frontend​

kubectl apply -f - <<EOF
apiVersion: openchoreo.dev/v1alpha1
kind: WorkflowRun
metadata:
name: frontend-workflow-manual-01
namespace: default
labels:
openchoreo.dev/project: "doclet"
openchoreo.dev/component: "frontend"
spec:
workflow:
kind: Workflow
name: docker-gitops-release
parameters:
componentName: frontend
projectName: doclet
docker:
context: /project-doclet-app/webapp-react-frontend
filePath: /project-doclet-app/webapp-react-frontend/Dockerfile
repository:
appPath: /project-doclet-app/webapp-react-frontend
revision:
branch: main
commit: ""
url: https://github.com/openchoreo/sample-workloads.git
workloadDescriptorPath: workload.yaml
EOF
note

The source code for the Doclet application is available at openchoreo/sample-workloads.

Merge the Pull Requests​

Once all three Workflow runs complete, 3 pull requests will be created in your forked GitOps repository β€” one for each component. Each PR contains the generated Workload, ComponentRelease, and ReleaseBinding manifests targeting the development environment.

Review and merge them, then wait for Flux to sync and deploy the components to your cluster.

Verify the Deployment​

kubectl get releasebindings -o wide
kubectl get deployments -A
kubectl get pods -A

The READY column on your ReleaseBindings should show True.

Step 7: How a Component Gets Deployed​

In OpenChoreo, deploying a Component to an Environment requires three key resources:

  1. Component β€” defines the application, its ComponentType, and configuration parameters
  2. ComponentRelease β€” an immutable snapshot capturing the exact state of the Component, its ComponentType, and Workload at a point in time
  3. ReleaseBinding β€” binds a ComponentRelease to a specific Environment, triggering OpenChoreo to render and deploy the actual Kubernetes resources (Deployment, Service, etc.)

In a GitOps workflow, you commit all three as YAML manifests. The Workflow runs in the previous step generated these manifests and created pull requests β€” once merged, Flux synced them to your cluster automatically.

To deploy to additional Environments, you create new ReleaseBinding manifests that reference the same ComponentRelease but target a different Environment. The following step demonstrates this promotion workflow.

Step 8: Promote to Staging​

After validating in development, promote the entire Doclet project to staging using the bulk release Workflow:

kubectl apply -f - <<EOF
apiVersion: openchoreo.dev/v1alpha1
kind: WorkflowRun
metadata:
name: bulk-release-manual-01
namespace: default
spec:
workflow:
kind: Workflow
name: bulk-gitops-release
parameters:
scope:
all: false
projectName: "doclet"
gitops:
repositoryUrl: "https://github.com/<your-github-username>/sample-gitops"
branch: "main"
targetEnvironment: "staging"
deploymentPipeline: "standard"
EOF

Replace <your-github-username> with your GitHub username. Once the Workflow completes, a pull request will be created to promote all Doclet components from development to staging in a single operation.

Merge the PR and wait for Flux to sync. Then verify:

kubectl get releasebindings -o wide

You should see ReleaseBindings for both the development and staging Environments.

note

The same ComponentReleases are now deployed to both development and staging. This is the power of the ReleaseBinding model: one immutable release, multiple Environments.

Step 9: Environment-Specific Overrides​

Different Environments often need different configurations β€” for example, a staging Environment might use a different database endpoint or log level than development. In OpenChoreo, the ReleaseBinding is where you define these environment-specific overrides.

The ReleaseBinding supports three types of overrides:

  • componentTypeEnvironmentConfigs β€” override ComponentType parameters for a specific Environment
  • traitEnvironmentConfigs β€” override Trait configurations for a specific Environment
  • workloadOverrides β€” override workload-level settings such as environment variables and file mounts

This approach keeps the ComponentRelease immutable β€” the same release artifact is deployed everywhere, with only the configuration varying per Environment.

See the ReleaseBinding API reference for full details on available override fields.

Rollback

To roll back, revert the releaseName in the ReleaseBinding to the previous ComponentRelease name and push. OpenChoreo handles the rest.

Summary​

You've successfully:

  • Set up a GitOps repository with Flux CD for OpenChoreo
  • Configured platform infrastructure (Environments, ComponentTypes, Workflows) via Git
  • Built and deployed a multi-component application using OpenChoreo Workflows
  • Promoted all components from development to staging using the bulk release Workflow

What to explore next:

Advanced flows:

Clean Up​

To remove the Flux resources:

kubectl delete -f flux/