This repository implements a GitOps-based fleet management system for Kubernetes clusters using Flux CD with Git-based delivery. It is inspired by the d2-fleet architecture but uses Git repositories instead of OCI artifacts for component delivery.
This fleet repository follows a three-repository architecture:
- fleet (this repo): Cluster fleet management and configuration
- fleet-infra: Infrastructure components (operators, controllers, security policies)
- fleet-apps: Application deployments and services
fleet/
├── clusters/ # Per-cluster configurations
│ ├── staging/ # Staging environment
│ │ └── flux-system/
│ └── production/ # Production environment
│ └── flux-system/
├── tenants/ # Multi-tenant ResourceSet definitions
│ ├── platform/ # Platform team resources
│ └── apps/ # Application team resources
├── infra/ # Local infrastructure overlays (optional)
├── apps/ # Local application overlays (optional)
└── Makefile # Common automation tasks
- d2-fleet: Uses
OCIRepositoryresources pointing to OCI artifacts in container registries - This fleet: Uses
GitRepositoryresources pointing to Git repositories with branches/tags
Staging Environment:
- Syncs from
mainbranch for continuous updates - Fast reconciliation intervals (5-10 minutes)
- Tracks latest changes automatically
Production Environment:
- Uses semver tags (
>=1.0.0 <2.0.0) for stability - Slower reconciliation intervals (10-30 minutes)
- Controlled releases via Git tags
Each cluster has a FluxInstance resource that:
- Configures Flux CD components (source, kustomize, helm, notification controllers)
- Enables multi-tenancy with network policies
- Syncs cluster state from this Git repository
infra: References the fleet-infra repository for infrastructure componentsapps: References the fleet-apps repository for application deployments
kustomization-infra.yaml: Applies infrastructure configurationskustomization-apps.yaml: Applies application configurations (depends on infra)
Multi-tenant organization using ResourceSet resources:
platform: Platform team infrastructure and toolingapps: Application team deployments
Each cluster includes a runtime-info ConfigMap that stores environment-specific variables:
- Environment metadata:
ENVIRONMENT,CLUSTER_NAME,CLUSTER_DOMAIN - Git references:
INFRA_REF,APPS_REF(branches for staging, semver for production) - Reconciliation settings:
RECONCILE_INTERVAL,RECONCILE_TIMEOUT
These variables are automatically substituted into GitRepository and Kustomization resources using Flux's postBuild.substituteFrom feature, enabling:
- Single source of truth for environment configuration
- Easy environment-specific customization
- Simplified cluster configuration management
The Flux Operator itself is managed declaratively using a HelmRelease resource (flux-operator.yaml):
- Pulls the operator from OCI Helm registry
- Configures multitenancy and reporting settings
- Enables automatic operator updates
- Provides GitOps-based operator lifecycle management
- Kubernetes cluster(s) with cluster-admin access
- Flux Operator installed
- Git repository access (GitHub, GitLab, etc.)
- GitHub personal access token or deploy key
Option A: Quick Install (recommended for initial bootstrap)
kubectl apply -f https://github.com/controlplaneio/flux-operator/releases/latest/download/install.yamlOption B: Declarative Install (GitOps-managed operator)
For GitOps-managed Flux Operator lifecycle:
kubectl apply -f clusters/staging/flux-system/flux-operator.yamlThis installs the operator using a HelmRelease, enabling automatic updates and declarative management. Once bootstrapped, the operator will be managed by Flux itself.
Create a secret with your Git credentials:
export GITHUB_TOKEN=<your-token>
kubectl create namespace flux-system
kubectl create secret generic flux-system \
--namespace flux-system \
--from-literal=username=git \
--from-literal=password=${GITHUB_TOKEN}Replace ${GITHUB_ORG} placeholders in cluster configs:
# For staging
export GITHUB_ORG=your-github-org
find clusters/staging -type f -name "*.yaml" -exec sed -i "s/\${GITHUB_ORG}/${GITHUB_ORG}/g" {} +
# For production
find clusters/production -type f -name "*.yaml" -exec sed -i "s/\${GITHUB_ORG}/${GITHUB_ORG}/g" {} +kubectl apply -k clusters/staging/flux-systemkubectl apply -k clusters/production/flux-system# Check FluxInstance status
kubectl get fluxinstance -n flux-system
# Check GitRepository resources
kubectl get gitrepository -n flux-system
# Check Kustomization resources
kubectl get kustomization -n flux-system
# Watch reconciliation
flux logs --all-namespaces --follow- Make changes to fleet-infra or fleet-apps repositories
- Commit and push to
mainbranch - Flux automatically reconciles changes to staging clusters within 5-10 minutes
- Monitor with
flux get sources gitandflux get kustomizations
- Test changes thoroughly in staging
- Tag the fleet-infra or fleet-apps repository with a semver tag:
git tag -a v1.0.1 -m "Release v1.0.1" git push origin v1.0.1 - Flux automatically reconciles to production clusters using the latest matching semver tag
- Monitor with
flux get sources gitandflux get kustomizations
- Make changes to this fleet repository (cluster configs, tenants, etc.)
- Commit and push to
mainbranch - Flux reconciles cluster configurations automatically
To modify environment-specific settings, edit the runtime-info.yaml ConfigMap for each cluster:
Example: Change reconciliation interval for staging
# clusters/staging/flux-system/runtime-info.yaml
data:
RECONCILE_INTERVAL: 3m # Changed from 5m to 3mExample: Update cluster domain
# clusters/production/flux-system/runtime-info.yaml
data:
CLUSTER_DOMAIN: prod.example.comAfter committing changes, Flux automatically applies the new configuration and reconciles affected resources. The postBuild.substituteFrom feature ensures all GitRepository and Kustomization resources use the updated values.
This setup supports multi-tenancy through:
- Namespace isolation: Separate namespaces per tenant
- ResourceSets: Group and manage tenant resources
- Network policies: Enabled in FluxInstance for network isolation
- Service accounts: Dedicated service accounts per tenant
fleet-infra/
├── clusters/
│ ├── staging/
│ │ ├── infrastructure.yaml # Infrastructure Kustomizations
│ │ └── ...
│ └── production/
│ ├── infrastructure.yaml
│ └── ...
└── components/ # Shared infrastructure components
├── cert-manager/
├── ingress-nginx/
└── ...
fleet-apps/
├── clusters/
│ ├── staging/
│ │ ├── applications.yaml # Application Kustomizations
│ │ └── ...
│ └── production/
│ ├── applications.yaml
│ └── ...
└── components/ # Shared application components
├── frontend/
├── backend/
└── ...
# Validate cluster configurations
make validate-staging
make validate-production
# Show diff before applying
make diff-staging
make diff-production
# Force reconciliation
make reconcile-staging
make reconcile-production
# Check status
make status-staging
make status-productionkubectl get gitrepository -n flux-system
kubectl describe gitrepository infra -n flux-systemkubectl get kustomization -n flux-system
kubectl describe kustomization infra -n flux-systemflux reconcile source git infra
flux reconcile kustomization infraflux logs --all-namespaces --follow
kubectl logs -n flux-system -l app=source-controller
kubectl logs -n flux-system -l app=kustomize-controller- Git Credentials: Store as Kubernetes secrets, rotate regularly
- RBAC: Limit cluster-admin access to platform team
- Network Policies: Enabled by default in FluxInstance
- Multi-tenancy: Isolate tenant workloads using namespaces and service accounts
- Git Signing: Consider GPG-signed commits for production
If migrating from d2-fleet's OCI approach:
- Package your OCI artifacts as Git repositories (fleet-infra, fleet-apps)
- Replace
OCIRepositoryresources withGitRepositoryresources - Update
sourceRef.kindin Kustomizations fromOCIRepositorytoGitRepository - Use branches for staging, semver tags for production
- Remove Cosign verification patches (unless using Git commit signing)