This article presents an architectural approach for automatically bootstrapping core services in Kubernetes clusters using GitOps principles, with Flux CD as the reconciliation engine. The primary goal is not maximum abstraction or infinite scalability, but a clear, maintainable, and operationally practical foundation that enables a cluster to become usable quickly and remain easy to reason about over time.
The scope of this article is intentionally architecture‑focused. It describes how responsibilities, dependencies, and isolation boundaries are modeled declaratively - across repositories, directories, namespaces, and Flux resources - rather than how to provision infrastructure or tune workloads. Kubernetes itself is assumed to be operational up to the networking layer.
The article is structured as follows:
- An explanation of why GitOps is a suitable operating model for core Kubernetes services and how Flux CD fits this role
- An overview of Flux CD concepts, focusing on architectural responsibilities rather than internal mechanics
- A detailed discussion of the architectural building blocks used to structure and isolate core services
- A reflection on common GitOps challenges and improvement areas, independent of Flux CD as a specific tool
Throughout the article, the emphasis remains on explicit dependency modeling, clear separation of concerns, and day‑to‑day operability.
Defining Core Services
In this context, core services refer to foundational components required to operate, scale, and secure a Kubernetes cluster. The exact selection depends on the environment and organizational requirements, but commonly includes:
- Storage enablement: CSI drivers for persistent storage
- Scaling: Node and pod autoscaling (
cluster-autoscaler,keda) - Ingress and networking: Ingress controllers, load balancing (e.g.
Traefik,MetalLB), and service meshes (e.g.Linkerd,Istio) - Secret and certificate handling: Certificate management (
cert-manager) and secret distribution (external-secrets-operator,reflector) - Identity and access management: Human and machine identity systems (e.g.
Keycloak,Zitadel) - Monitoring and operations: Metrics, observability, and operational tooling (e.g. the
Prometheusstack) - Application delivery tooling: GitOps‑based deployment tools such as
Argo CD
Application workloads and environment‑specific business logic are deliberately out of scope.
Why GitOps and Which Tool to Choose
Managing core Kubernetes services requires a deployment model that is repeatable, transparent, and recoverable. GitOps provides this by using Git as the single source of truth and continuously reconciling the running cluster against the desired state defined in code, rather than relying on imperative actions applied directly to the cluster.
This approach enables automation with minimal manual effort, makes configuration changes auditable by default, and allows failed or broken changes to be reverted simply by restoring a known‑good Git state. When combined with pull request workflows, GitOps also supports clear ownership boundaries and controlled change processes for critical platform components.
Several tools can implement GitOps for Kubernetes, notably Flux CD and Argo CD, with infrastructure tooling such as Terraform or OpenTofu as possible complements. The choice between them is less about feature parity and more about how well the tool aligns with the intended architecture, dependency model, and long‑term maintainability goals of the platform.
Why Flux CD Fits This Architecture
Flux CD is well suited for bootstrapping and operating core Kubernetes services because it is lightweight, has minimal prerequisites, and encourages a clear separation of responsibilities from the start.
It can be bootstrapped early with little effort, integrates natively with GitOps workflows, and supports essential technologies such as Kustomize, Helm, encrypted secrets via SOPS/age, and Terraform/OpenTofu through the tofu-controller out of the box. Its modular controller design allows platform teams to enable only what is needed, keeping operational overhead and resource usage low.
Most importantly, Flux CD fits an architecture‑first approach. It makes dependency modeling, ownership boundaries, and isolation explicit design concerns, enabling platform teams to manage critical services independently from application workloads while retaining a consistent GitOps operating model.
Flux CD Concepts from an Architectural Perspective
Flux CD is built around a small set of composable control‑loop primitives that together implement the GitOps reconciliation model. In the context of this article, these concepts are not presented as API references, but as architectural responsibilities that define how intent flows from Git into a running cluster.
Understanding Flux CD at this level is less about memorizing resource fields and more about recognizing which concern each concept owns - such as sourcing configuration, applying state, enforcing order, or integrating external tooling. This perspective is essential when using Flux as a foundation for bootstrapping and operating Kubernetes core services.
The following sections introduce the key Flux concepts with a focus on what role they play in the overall architecture, rather than on their internal implementation details.
Flux Custom Resource Definitions (CRDs)
Flux models GitOps workflows using Kubernetes Custom Resource Definitions (CRDs). Each CRD represents a clearly defined responsibility in the reconciliation process and is handled by a corresponding dedicated controller.
-
GitRepository
Defines a Git source for Flux. It tells Flux where configuration lives and which revision should be considered the desired state. Multiple sources can be used simultaneously. -
Kustomization
Describes what should be applied to the cluster from a source. It ties a Git repository to a directory path, builds manifests, and keeps the cluster in sync with the declared state. -
Flux Helm (
HelmRepositoryandHelmRelease)
Enables Helm charts to be managed declaratively. These resources define which chart should be deployed and ensure it remains aligned with Git over time. -
Flux Terraform
Optional extensions that allowTerraformorOpenTofuto follow the same GitOps reconciliation model as Kubernetes resources.
Tooling Used by Flux CD
Flux intentionally relies on existing, proven tools instead of introducing proprietary abstractions.
-
Kustomize
Used to compose and customize Kubernetes manifests in a fully declarative way. -
Helm
Used for packaging and deploying complex applications, managed declaratively through Flux controllers. -
Terraform/OpenTofu
Normally known in the Infrastructure-as-Code field, it can also be utilized to configure the core services (e.g.Zitadel) and external resources under GitOps control. -
SOPS/age
Used to store secrets encrypted in Git and decrypt them safely during reconciliation.
Reconciliation
Reconciliation is the core principle of Flux CD. Flux continuously (depending on your configuration) compares the desired state stored in Git with the actual cluster state and works to align the two. This mechanism applies consistently to Kubernetes manifests, Helm releases, and integrated infrastructure‑as‑code workflows.
Flux Kustomization Dependencies
Flux allows explicit dependencies between Kustomization resources. This makes deployment order predictable and helps avoid race conditions, which is especially important during cluster bootstrap phases and when working with CRDs or operators.
GitOps Architecture for Core Services
In the context of GitOps with Flux CD, architecture is not about infrastructure sizing or cluster topology. Instead, it describes how responsibilities, dependencies, and ownership are expressed declaratively, so that reconciliation becomes predictable rather than accidental.
This architecture defines how core services are structured, how they depend on one another, and where boundaries exist - across directories, Flux Kustomizations, repositories, and Kubernetes namespaces. These layers work together to control reconciliation order, limit blast radius, and make failures understandable during both bootstrap and ongoing operation.
A well‑designed architecture ensures that:
- dependencies are explicit rather than implicit,
- changes are isolated to the smallest possible scope,
- reconciliation failures stop early instead of cascading,
- and operational intent is visible directly in Git.
The following sections describe the architectural building blocks used to achieve this, starting with service isolation and continuing through Kustomizations, directory structures, repositories, and namespaces.
Isolating Services into Meaningful Parts
Core services should be isolated into cohesive, well‑defined parts rather than being deployed as a single, flat set of manifests. Isolation can be applied at multiple levels, including native Kustomize overlays, Flux Kustomizations, directories, repositories, and Kubernetes namespaces.
Logical boundaries between these parts should primarily be driven by the following criteria:
-
Change intervals:
Components that change infrequently - such as CRDs or foundational controllers - should be separated from rapidly evolving components. This reduces unnecessary reconciliation and minimizes the blast radius of changes. -
Cohesive responsibilities:
Closely related components, such asprometheusand dashboard tooling likegrafana, often benefit from being grouped together because they are typically upgraded, tuned, and reasoned about as a single unit. -
Access and ownership requirements:
When access management becomes relevant, isolation at the directory and repository level becomes an important security and governance control. Components maintained by platform teams should be clearly separated from those managed by application teams.
Possible high‑level isolation layers include directories such as bootstrap, network-and-security, core-services, or similar groupings. These layers map naturally to Flux dependency chains and distinct reconciliation stages. 1
Native Kustomizations
Native Kustomize configurations (kustomization.yaml) define which manifests or Helm chart outputs belong together. They operate purely at the manifest composition level and are unaware of reconciliation order.
While native Kustomizations are useful for grouping related resources, care must be taken to evaluate potential ordering and race‑condition risks. Flux does not guarantee ordering within a single native Kustomization, which can lead to failures when resources depend on one another.
A typical example is deploying cert-manager together with its dependent resources. If CRDs or controllers are not fully available before dependent objects such as ClusterIssuer are applied, reconciliation will fail and stop at this level. For this reason, native Kustomizations should only include resources that have no implicit ordering dependencies.
Flux Kustomizations
Flux Kustomizations API operate at a higher abstraction level and provide explicit dependency management. Each Flux Kustomization references a directory - which may contain native Kustomizations - and defines how and when its contents should be reconciled.
Dependencies are always expressed at this layer. Flux does not support dependencies between native Kustomizations, and attempting to enforce ordering at that level inevitably leads to race conditions. By splitting logically ordered components into multiple Flux Kustomizations, explicit dependsOn relationships can be declared.
This approach has two important benefits. First, it prevents cascading reconciliation failures: if an earlier layer fails, dependent Kustomizations will not attempt reconciliation at all. Second, it makes the intended ordering visible and declarative, rather than hidden in scripts or implied by convention.
Each Flux Kustomization typically encapsulates a single isolation layer and points to a directory containing the corresponding native Kustomizations.
Directories
Once a setup grows beyond a handful of manifests, directories become essential for maintainability. They are not merely passive containers, but an architectural tool for expressing structure and intent.
Directories should clearly differentiate between technologies, such as:
- Flux
Kustomizationdefinitions - Native
Kustomizebases and overlays OpenTofu/Terraformmodules
Kustomize itself relies on a well‑defined directory layout to cleanly separate base configurations from overlays and environment‑ or layer‑specific variations. A key architectural objective should be to keep manifests DRY (Do-not-Repeat-Yourself) and avoid unnecessary duplication.
This is achieved by pushing shared configuration into common base directories and using overlays sparingly and intentionally. Repetition of values or resources across multiple directories is often an indication that the Kustomization structure requires refinement.
Following an illustration of an appropriate blueprint directory structure:
[flux main repository directory structure]
.
├── kustomize
│ ├── base
│ │ └── <service-group>
│ └── <service-base>
│ └── overlay
│ └── <env>
│ ├── flux
│ │ └── flux-system
│ └── <service-group>
│ └── <service-overlay>
└── tofu
└── <tofu-module>
Repositories
Splitting components into multiple repositories can significantly reduce reconciliation impact. Flux only reconciles the Kustomizations that reference a changed repository; without this separation, a (mono) repository change can trigger large reconciliation cascades, making development and debugging increasingly difficult as scale grows.
To maintain consistency, child repositories should follow the same directory layout as the root GitOps repository. This allows patterns and automation to be reused and helps reduce cognitive overhead for operators and contributors.
The following example shows a possible directory structure for a sub‑repository:
[flux sub repository directory structure]
.
├── kustomize
│ ├── base
│ │ └── <service-base>
│ └── overlay
│ └── <env>
│ └── <service-overlay>
└── tofu
└── <tofu-module>
Repository design also introduces operational requirements around authentication. For example, platforms such as GitHub restrict deploy keys to a single repository. These keys must be created, distributed, and managed correctly to allow Flux to access each repository as intended.
Namespaces
Namespaces provide another important isolation layer, especially as Kustomizations grow in size and complexity. Splitting Flux‑managed components into separate namespaces improves readability, operational clarity, and fault isolation.
A common pattern is to align namespaces with architectural isolation layers, for example flux-network-and-security. Flux‑related namespaces should be clearly distinguished from service namespaces, typically by using a flux-* prefix. This helps avoid mixing secrets, configuration, and operational concerns with application workload resources.
There are several critical operational constraints to consider:
- Namespaces must exist before Flux attempts reconciliation. If namespaces are created as part of GitOps reconciliation itself, race conditions will lead to reconciliation failures.
- Secrets, such as
SOPS/ageencryption keys, must be explicitly and correctly distributed across all namespaces where they are required.
Failing to account for these constraints often results in hard‑to‑diagnose errors during bootstrap phases and should therefore be addressed early in the architectural design.
By combining namespace separation with repository boundaries, the following setup illustrates how these isolation layers work together:
Where GitOps Still Requires Architectural Discipline
While Flux CD provides a solid and composable foundation for GitOps workflows, certain limitations and edge cases emerge in real‑world environments - especially when managing complex core services. Recognizing these early allows teams to design architectures and operational processes that compensate for current gaps, rather than working against them.
Services with Limited GitOps Compatibility
Not all services are designed to integrate naturally with a Kubernetes‑native, declarative GitOps operating model. This is neither a limitation of Flux CD nor of GitOps itself, but rather a mismatch between certain service designs and the assumptions of a declarative, reconciliation‑driven approach.
Some systems generate critical state dynamically at runtime or manage credentials entirely internally. As a result, their outputs cannot be consumed directly by downstream components in a purely declarative manner, regardless of the GitOps tooling used.
A common example is identity and access management tooling that automatically generates personal access tokens or client secrets internally. In such cases, the generated credentials cannot be referenced directly by dependent services through static manifests. Instead, additional mechanisms - such as Kubernetes Jobs, side‑effect controllers, or external automation - are required to extract, post‑process, and redistribute this information in a GitOps‑compatible form.
These compensating patterns introduce additional operational complexity and should be considered carefully when evaluating or selecting core services. In practice, GitOps compatibility becomes an architectural selection criterion: services that externalize configuration, expose declarative APIs, and integrate cleanly with Kubernetes primitives align naturally with GitOps workflows, while others require deliberate adaptation - independent of whether Flux CD or another GitOps reconciler is used.
Managing and Improving Dependencies
Dependencies are one of the most challenging aspects of GitOps, particularly during bootstrap phases where multiple core services depend on one another. Real‑world dependency graphs often span databases, operators, identity systems, and networking components.
For example, an identity provider may depend on a database cluster, which in turn requires storage primitives and networking foundations. Expressing such dependency chains within a single native Kustomization is unreliable, as execution order is not guaranteed and race conditions are common. Dependencies modeled at that level tend to work by coincidence and frequently break as systems evolve.
Flux addresses this challenge partially through explicit dependency management between Flux Kustomizations. By structuring directories, namespaces, and repositories deliberately, dependencies can be expressed declaratively and enforced consistently. This architectural discipline prevents failed components from triggering repeated reconciliation attempts in dependent layers.
Nevertheless, dependency handling has room for improvement. Concepts such as sync groups, known from other GitOps tools, could make deployment stages easier to express. Similarly, distinguishing between hard and soft dependencies could allow finer‑grained control - for example, preventing Flux from even evaluating downstream Kustomizations when a critical prerequisite is missing.
Another recurring pain point is the lack of dependency modeling within a single Flux Kustomization. Support for ordered execution inside a Kustomization would reduce the need for many narrowly scoped Flux Kustomizations, which are currently required to avoid race conditions - particularly when deploying CRDs before their corresponding custom resources.
Diff Visibility and Change Control
Flux CD deliberately does not provide a native, interactive diff or approval workflow. Reconciliation is automatic, continuous, and non‑negotiable: once a change is merged into Git, Flux will apply it. This is not a missing feature, but a deliberate design decision that enforces Git as the single control plane.
The implication is clear: diffs must exist outside of Flux. There is no “pause and inspect” phase during reconciliation. Operators who want to understand the impact of a change must do so before it is merged, not after Flux has already acted.
This shifts responsibility upstream and makes disciplined Git workflows mandatory. Direct commits to main branches undermine the model. Pull or merge requests are not optional ceremony - they are the only place where review, validation, and human‑readable diffs can occur.
Because Flux spans multiple abstraction layers, a single, unified diff mechanism would be misleading. Diffs must be generated at the layer where they naturally belong:
- rendered manifests for
Kustomize, - rendered charts for
Helm, - execution plans for
TerraformorOpenTofu.
These tools provide the necessary visibility without weakening Flux’s reconciliation guarantees. The operator’s responsibility is to generate and review these diffs explicitly, not to expect Flux to surface them.
Architecturally, this separation is intentional. Flux is a reconciler, not a deployment UI or change approval system. Teams that accept this boundary - and design their workflows around pre‑merge diffing - benefit from predictable reconciliation, cleaner audits, and fewer unexpected changes in production.
Secret Management Pitfalls
Although Flux integrates well with SOPS and age for encrypted secret management, operational mistakes remain possible. The most common risk is accidentally committing unencrypted secrets due to misconfiguration or human error.
To mitigate this risk, additional safeguards should be implemented outside the cluster. Pre‑commit hooks, CI checks, and repository scanning tools can enforce encryption policies and prevent plaintext secrets from being pushed. These measures are not a replacement for Flux’s security features, but an essential complement to them.
Secret management in GitOps is as much a process concern as a technical one. Clear conventions, automated enforcement, and shared responsibility across teams are required to maintain a secure and sustainable setup.
Conclusion
GitOps with Flux CD is not a plug‑and‑play solution - it is an architectural discipline. Flux provides powerful, flexible building blocks, but whether GitOps works reliably in practice depends largely on how those blocks are composed.
A well‑designed architecture is the key success factor: clear isolation layers, explicit dependency modeling, consistent directory and repository structures, and deliberate namespace separation. Without these foundations, GitOps quickly degrades into fragile reconciliation chains, race conditions, and difficult debugging. With them, Git becomes a true system of record, and the cluster becomes predictable, auditable, and self‑healing.
Flux CD rewards thoughtful design. When structure, ownership, and change boundaries are defined intentionally, GitOps evolves from a deployment mechanism into a sustainable operating model for Kubernetes core services.
In the end, GitOps succeeds not because of the tool chosen, but because of the architecture intentionally designed around it.