GitOps on Azure Kubernetes Service: Building a Production-Ready Platform with Flux

Kubernetes platforms seldom fail due to the technology itself; instead, failures are usually caused by operational issues.

Between the initial successful ‘kubectl apply’ and the deployment of the twentieth production cluster, platform teams realize a harsh reality: Kubernetes isn’t hard to run, but maintaining consistent operations is very challenging. Configuration drifts occur. Hotfixes are often applied directly, skipping pipelines. Infrastructure ends up as a patchwork of Git repositories, YAML files, CI/CD pipelines, and manual tweaks performed from someone’s laptop at midnight.

This is precisely the issue that GitOps was created to address.

In the Kubernetes ecosystem, multiple tools support GitOps workflows, with Flux being one of the most popular. Flux is an open-source continuous delivery toolkit maintained by the Cloud Native Computing Foundation. It constantly reconciles the cluster’s actual state with the desired state specified in Git, ensuring the cluster always stays aligned with the configuration stored in version control.

In this blog, we will walk through a complete implementation of Flux with Azure Kubernetes Service (AKS). The aim is to go beyond a simple installation and develop a deployment model that meets platform-grade standards, designed for platform engineers and cloud architects.

We will set up an AKS cluster with Azure Linux, bootstrap Flux using a GitHub repository, and show how Git commits automatically trigger Kubernetes deployments.

By the end, the cluster itself will become almost secondary. Git becomes the platform.

Understanding Flux and the GitOps Model

GitOps is a straightforward yet powerful concept. The desired state of infrastructure and applications is specified in Git, and automated controllers ensure this state is maintained within the cluster.

Flux is a controller that implements this concept. It constantly watches a Git repository and ensures Kubernetes resources are aligned with what is specified in that repository.

Flux uses a pull-based reconciliation model instead of pushing deployments via pipelines. The cluster retrieves its desired configuration from Git and applies it internally. This setup eliminates the need for external credentials for CI systems to access the cluster.

The Flux architecture includes multiple controllers operating within Kubernetes. These controllers observe sources like Git repositories, OCI artifacts, or Helm charts, and then reconcile Kubernetes resources according to the definitions stored within them.

When a change is committed to Git, Flux detects it and updates the cluster automatically. If someone manually alters resources within the cluster, Flux detects the drift and reverts the configuration to match the Git state.

This reconciliation loop is the key to GitOps reliability. It ensures deployments are predictable, infrastructure changes are auditable, and rollbacks are simply Git reverts.

Why Platform Teams Choose Flux

Flux has gained popularity among platform engineering teams because it functions similarly to Kubernetes: it is controller-based, declarative, and composable.

A key strength of Flux is its Kubernetes-native design. Instead of functioning as a separate control plane, Flux uses controllers that integrate with the Kubernetes API. This enables teams to manage Git repositories, Helm releases, or image automation directly via Kubernetes resources.

This architecture seamlessly fits platform engineering models, positioning Kubernetes as the primary API for infrastructure delivery.

Flux also supports a wide ecosystem, integrating with GitHub, GitLab, Bitbucket, and other Git providers. It also supports OCI artifacts, Helm charts, and managing multiple repositories simultaneously.

Security is also a key benefit. Flux uses a pull-based approach, where the cluster fetches configuration from Git rather than having external systems push deployments into it. This minimizes the number of credentials needed for cluster access.

For organizations managing multiple clusters, Flux also enables multi-tenancy and fleet-level management across multiple repositories or environments.

However, Flux has its limitations.

A common criticism is its user experience when compared to tools like Argo CD. Traditionally, Flux emphasized Git workflows and command-line use over providing rich graphical dashboards.

Another challenge is the complexity of concepts. Since Flux offers multiple specialized controllers, such as GitRepository, Kustomization, HelmRelease, and ImageRepository, the initial learning curve may seem quite steep.

The positive news is that the ecosystem has progressed greatly, and adding graphical interfaces has made Flux much easier to access.

The Emergence of the Flux GUI

Flux traditionally focused on Git workflows over graphical management tools, with platform engineers primarily interacting via Git and CLI commands.

This has changed recently.

Projects like Weave GitOps UI and Kubernetes dashboard integrations have created graphical interfaces to visualize GitOps states, detect reconciliation loops, and display deployment statuses.

The Flux UI enables engineers to see connected Git repositories, deployed kustomizations, and the health status of reconciliation.

For platform teams overseeing numerous services, this visibility is highly beneficial. Engineers can swiftly determine if a Git commit caused a deployment, confirm whether resources were properly reconciled, and locate any failures.

The GUI does not substitute GitOps workflows; instead, it enhances them by providing operational visibility into the controllers’ activities within the cluster.

Architecture: AKS + Flux GitOps Platform

Below is the conceptual architecture of a typical AKS GitOps platform.

The Git repository serves as the definitive source of truth. Flux constantly syncs the cluster with this repository. Any modifications committed to Git are automatically reflected in the cluster.

Deploying a Secure AKS Cluster for GitOps

Before installing Flux, we need a Kubernetes cluster configured for platform-level operations.

In this example, we will deploy AKS with the following characteristics:

• Azure Linux node OS

• Managed identity

• Azure CNI networking

• SSH access disabled on nodes

• RBAC enabled

Disabling SSH is a crucial security measure. AKS enables you to disable SSH access during cluster creation with the — ssh-access disabled argument, which prevents direct login to cluster nodes. First, configure environment variables.

#!/usr/bin/env bash
set -euo pipefail

LOCATION="swedencentral"
RESOURCE_GROUP="rg-aks-flux-blog"
CLUSTER_NAME="aks-flux-platform"
NODE_COUNT=3
NODE_SIZE="Standard_D4s_v5"

Create the resoure group

az group create \
  --name $RESOURCE_GROUP \
  --location $LOCATION

Now create the Azure Kubernetes Cluster

az aks create \
  --resource-group $RESOURCE_GROUP \
  --name $CLUSTER_NAME \
  --node-count $NODE_COUNT \
  --node-vm-size $NODE_SIZE \
  --network-plugin azure \
  --enable-managed-identity \
  --generate-ssh-keys \
  --node-osdisk-type Managed \
  --os-sku AzureLinux \
  --ssh-access disabled \
  --enable-oidc-issuer \
  --enable-workload-identity

Retrieve cluster credentials

az aks get-credentials \
  --resource-group $RESOURCE_GROUP \
  --name $CLUSTER_NAME

Verify the cluster

kubectl get nodes -o wide

Currently, we have a production-ready AKS cluster featuring secured node access and Azure Linux as its foundation.

Installing Flux CLI

Flux provides a CLI used for bootstrapping GitOps repositories.

Install the CLI.

brew install fluxcd/tap/flux

Verify the installation

flux --version

Check cluster compatibility

flux check --pre

Output will look like

► checking prerequisites
✔ Kubernetes 1.33.6 >=1.33.0-0
✔ prerequisites checks passed

This command checks the Kubernetes version compatibility and necessary permissions prior to installation.

Bootstrapping Flux with GitHub

Flux employs a bootstrap process to install controllers and link the cluster with a Git repository. The bootstrap command sets up the controllers and commits the Flux configuration into the repository. Begin by creating a GitHub Personal Access Token with repository permissions, then export the token.

export GITHUB_TOKEN=<your-token>
export GITHUB_USER=<github-username>

Bootstrap Flux

flux bootstrap github \
  --token-auth \
  --owner=$GITHUB_USER \
  --repository=aks-flux-platform \
  --branch=main \
  --path=clusters/production \
  --personal

During bootstrap, Flux executes multiple actions at once. It installs Flux controllers into the Kubernetes cluster, creates a Git repository if one doesn’t already exist, commits the initial GitOps manifests to it, and configures the controllers to manage the cluster state based on the repository. After completing these steps, verify that the controllers are functioning correctly.

kubectl get pods -n flux-system

You should see controllers such as

NAME                                       READY   STATUS    RESTARTS   AGE
helm-controller-58b456999d-ghtgb           1/1     Running   0          100s
kustomize-controller-76c5d4c759-jhb99      1/1     Running   0          100s
notification-controller-6b5b7c8647-npwrd   1/1     Running   0          100s
source-controller-584b6f58bd-hmlmv         1/1     Running   0          100s

The cluster is now managed through Git.

A GitOps repository should follow a structure that separates environments from workloads.

Example:

aks-flux-platform
│
├── clusters
│   └── production
│       └── flux-system
│
└── apps
    └── demo
        ├── deployment.yaml
        └── kustomization.yaml

The clusters directory holds Flux configuration files, while the apps directory contains Kubernetes workloads. This separation enables platform engineers to manage infrastructure separately from application deployments.

Deploying an Application with Flux

Let’s deploy a basic NGINX application through GitOps.

Create the deployment manifest.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: demo-nginx
  namespace: default
spec:
  replicas: 2
  selector:
    matchLabels:
      app: demo-nginx
  template:
    metadata:
      labels:
        app: demo-nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.25
        ports:
        - containerPort: 80

Create a service

apiVersion: v1
kind: Service
metadata:
  name: demo-nginx
spec:
  selector:
    app: demo-nginx
  ports:
  - port: 80
    targetPort: 80
  type: ClusterIP

Create a kustomization.yaml

resources:
  - deployment.yaml
  - service.yaml

Now instruct Flux to deploy the application by creating a Flux Kustomization.

apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
  name: demo-app
  namespace: flux-system
spec:
  interval: 5m
  path: ./apps/demo
  prune: true
  sourceRef:
    kind: GitRepository
    name: flux-system

Commit the files to Git.

git add .
git commit -m "deploy demo nginx app"
git push

Flux quickly identifies the commit and proceeds with the deployment.

Verify the deployment.

GitOps Reconciliation in Action

Flux functions via reconciliation loops. It regularly checks the repository every few minutes to confirm that the cluster state aligns with the desired configuration.

Consider the following scenario.

An engineer manually scales the deployment.

kubectl scale deployment demo-nginx --replicas=5

The cluster now diverges from the Git configuration. During the next reconciliation interval, Flux detects the drift and resets the replica count to match the Git declaration.

This behaviour is what makes GitOps powerful. Git becomes the only source of truth.

Visualizing Flux Operations

Below is a simplified reconciliation flow.

   Git Commit
       |
       v
+---------------+
| Git Repository|
+-------+-------+
        |
        v
+---------------+
| Flux Source   |
| Controller    |
+-------+-------+
        |
        v
+---------------+
| Flux Kustomize|
| Controller    |
+-------+-------+
        |
        v
+---------------+
| Kubernetes API|
+---------------+

The controllers continuously observe Git and reconcile cluster state accordingly.

Observability and Operations

Flux integrates with Kubernetes observability tools like Prometheus and Grafana. Each controller provides metrics and events that describe the reconciliation status. These metrics help platform teams monitor GitOps health across multiple clusters. Operational dashboards usually visualize:

  • repository synchronization status
  • reconciliation success rate
  • deployment latency
  • controller health.
  • The Flux GUI offers similar insights through a graphical interface.

Security Considerations

A production GitOps platform demands meticulous security planning. In this example, we’ve adopted several best practices:

  • SSH access to nodes is turned off to minimize attack points
  • GitHub tokens are limited to specific repository permissions
  • Flux controllers run using Kubernetes RBAC instead of external credentials.

Since Flux relies on a pull-based approach, the cluster doesn’t need inbound connections from CI pipelines. These design choices greatly lower the risk compared to conventional deployment methods.

Limitations and Trade-Offs

Flux performs well in platform engineering but might not suit all teams. Organizations relying on visual deployment processes may lean toward Argo CD because of its established UI. Teams new to GitOps could find Flux’s concepts initially complex. Its modular controller structure also adds more Kubernetes objects that engineers need to grasp. Nonetheless, for platform teams building large-scale infrastructure, Flux provides a highly powerful and adaptable option.

When Flux Becomes a Platform Primitive

As Flux scales, it shifts from being just a deployment tool to becoming an integral part of the platform itself. Clusters are no longer set up manually; instead, everything is managed through Git. New clusters are automatically created and configured via GitOps repositories. Platform engineers can specify baseline infrastructure, networking policies, observability stacks, and security measures using declarative configurations. When a new environment is launched, Flux ensures the cluster aligns with the desired state set by the platform team. This approach is increasingly popular among modern platform engineering groups. In this model, Git acts as the API, Flux as the reconciling agent, and Kubernetes as the execution platform.

Final Thoughts

Integrating Azure Kubernetes Service with Flux forms a robust GitOps-based platform architecture. AKS offers a managed Kubernetes control plane and scalable infrastructure, while Flux acts as the continuous reconciliation engine that aligns clusters with Git. This configuration ensures infrastructure and applications are versioned, auditable, and reproducible. For platform engineers and cloud architects, this method addresses many operational issues that arise as Kubernetes environments expand beyond a single cluster. Infrastructure stability improves, deployments become traceable, clusters can self-heal, and most significantly, the platform can be completely rebuilt from Git.