Migrating to FluxCD Operator with UI
FluxCD Operator brings a simplified management experience and a native web UI for monitoring your GitOps workflows. This guide walks you through migrating an existing FluxCD installation to the Operator pattern with UI enabled, while maintaining GitLab as your Git source.
What is FluxCD Operator?
FluxCD Operator is a new way to manage Flux instances on Kubernetes. Instead of installing Flux components directly, you deploy a FluxInstance custom resource that manages the entire Flux stack.
┌─────────────────────────────────────────────────────────┐
│ FluxCD Operator │
│ ┌─────────────────────────────────────────────────┐ │
│ │ FluxInstance CR │ │
│ │ - Manages Flux components │ │
│ │ - Handles upgrades automatically │ │
│ │ - Provides web UI (Flux Web UI) │ │
│ └─────────────────────────────────────────────────┘ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ Source │ │ Kustomize│ │ Helm │ │
│ │Controller│ │Controller│ │Controller│ │
│ └──────────┘ └──────────┘ └──────────┘ │
└─────────────────────────────────────────────────────────┘
│
▼
┌─────────────────┐
│ GitLab │
│ (Git Source) │
└─────────────────┘
Benefits of Migrating
| Feature | Traditional Flux | Flux Operator |
|---|---|---|
| Installation | Flux CLI (flux install) |
Kubernetes manifests / Helm |
| Upgrades | Manual (flux upgrade) |
Automatic via CR updates |
| Configuration | CLI flags / bootstrap | Declarative (FluxInstance CR) |
| Web UI | Third-party (Weave GitOps) | Native Flux Web UI |
| Multi-tenancy | Complex | Built-in support |
| Observability | Manual setup | Integrated dashboards |
Prerequisites
| Component | Version | Notes |
|---|---|---|
| Kubernetes | 1.25+ | Tested on 1.28, 1.29 |
| Existing Flux | v2.0+ | v1.x requires additional migration steps |
| GitLab | 15.0+ | Self-managed or GitLab.com |
| kubectl | Latest | Configured with cluster access |
| flux CLI | v2.2+ | For verification and troubleshooting |
Verify Current Flux Installation
# Check Flux components
flux check --pre
# List existing Flux resources
kubectl get GitRepository -A
kubectl get Kustomization -A
kubectl get HelmRelease -A
kubectl get HelmRepository -A
# Check Flux version
flux --version
Step 1: Backup Existing Flux Configuration
Before making any changes, export all existing Flux resources:
# Create backup directory
mkdir -p ~/flux-backup/$(date +%Y%m%d-%H%M%S)
BACKUP_DIR=~/flux-backup/$(date +%Y%m%d-%H%M%S)
# Export all Flux resources
kubectl get GitRepository -A -o yaml > $BACKUP_DIR/gitrepositories.yaml
kubectl get Kustomization -A -o yaml > $BACKUP_DIR/kustomizations.yaml
kubectl get HelmRelease -A -o yaml > $BACKUP_DIR/helmreleases.yaml
kubectl get HelmRepository -A -o yaml > $BACKUP_DIR/helmrepositories.yaml
kubectl get ImageRepository -A -o yaml > $BACKUP_DIR/imagerepositories.yaml
kubectl get ImagePolicy -A -o yaml > $BACKUP_DIR/imagepolicies.yaml
kubectl get ImageUpdateAutomation -A -o yaml > $BACKUP_DIR/imageupdateautomations.yaml
# Export Flux secrets (GitLab credentials)
kubectl get secret -n flux-system -o yaml > $BACKUP_DIR/flux-secrets.yaml
# Export existing flux-system namespace
kubectl get namespace flux-system -o yaml > $BACKUP_DIR/namespace.yaml
echo "Backup completed: $BACKUP_DIR"
Export GitLab Webhook Configuration
# Get existing webhook secrets
kubectl get secret -n flux-system webhook-secret -o yaml > $BACKUP_DIR/webhook-secret.yaml
# Document existing webhook URLs
kubectl get GitRepository -A -o jsonpath='{range .items[*]}{.metadata.name}: {.spec.url}{"\n"}{end}' > $BACKUP_DIR/git-urls.txt
Step 2: Install FluxCD Operator
Add Flux Operator Helm Repository
helm repo add flux-operator https://controlplaneio.github.io/flux-operator/
helm repo update
Create Operator Namespace
kubectl create namespace flux-operator --dry-run=client -o yaml | kubectl apply -f -
Install Flux Operator
helm install flux-operator flux-operator/flux-operator \
--namespace flux-operator \
--create-namespace \
--wait
Verify Operator Installation
# Check operator pods
kubectl get pods -n flux-operator
# Expected output:
# NAME READY STATUS RESTARTS AGE
# flux-operator-xxxxxxxxxx-xxxxx 1/1 Running 0 2m
# Check operator deployment
kubectl get deployment -n flux-operator
Step 3: Create FluxInstance with UI Enabled
Generate FluxInstance Manifest
Create flux-instance.yaml:
apiVersion: fluxcd.controlplane.io/v1
kind: FluxInstance
metadata:
name: flux
namespace: flux-system
spec:
# Distribution settings
distribution:
version: "2.2.x" # Specify Flux version
registry: "ghcr.io/fluxcd"
artifact: "flux-manifests"
# Enable components
components:
- source-controller
- kustomize-controller
- helm-controller
- notification-controller
# Enable web UI
ui:
enabled: true
# Cluster configuration
cluster:
type: "kubernetes" # or "openshift"
domain: "cluster.local"
# Sync configuration (will be configured after migration)
sync:
enabled: false # We'll enable after migration
# Resource policies
policies:
allowCrossNamespaceRef: true
allowIngress: true
# Observability
observability:
metrics:
enabled: true
tracing:
enabled: false
Apply FluxInstance
kubectl apply -f flux-instance.yaml
Monitor Installation Progress
# Watch FluxInstance status
kubectl get fluxinstance flux -n flux-system -w
# Check when components are ready
kubectl wait --for=condition=Ready fluxinstance/flux -n flux-system --timeout=5m
Step 4: Migrate GitLab Credentials
Option A: Reuse Existing Secrets
If your existing secrets are compatible:
# Label existing secrets for Flux Operator
kubectl label secret -n flux-system --all \
app.kubernetes.io/part-of=flux \
app.kubernetes.io/managed-by=flux-operator
Option B: Create New GitLab Secret
# Create GitLab deploy token secret
kubectl create secret generic flux-gitlab-deploy-token \
--from-literal=username=flux-deploy \
--from-literal=password=<YOUR_GITLAB_DEPLOY_TOKEN> \
--namespace flux-system \
--dry-run=client -o yaml | kubectl apply -f -
Generate GitLab Deploy Token
- Navigate to GitLab Project → Settings → Deploy Tokens
- Click Add deploy token
- Configure:
- Name:
flux-deploy - Username:
flux-deploy(auto-generated) - Expiration date: Optional (set for security)
- Scopes:
- ✅
read_repository - ✅
write_repository(for image updates)
- ✅
- Name:
Step 5: Migrate GitRepository Resources
Update GitRepository Specifications
The Flux Operator uses the same GitRepository CRD, but you may need to update secret references:
apiVersion: source.toolkit.fluxcd.io/v1
kind: GitRepository
metadata:
name: gitlab-repo
namespace: flux-system
spec:
interval: 1m
url: https://gitlab.com/your-group/your-repo.git
ref:
branch: main
secretRef:
name: flux-gitlab-deploy-token # Updated secret name
timeout: 60s
Apply Updated GitRepository
# Remove existing GitRepository (don't delete the CRD)
kubectl delete GitRepository -A --all --wait
# Apply updated GitRepository
kubectl apply -f $BACKUP_DIR/gitrepositories.yaml
# Or create new one
kubectl apply -f gitrepository.yaml
Verify Git Source Connection
# Check GitRepository status
kubectl get GitRepository -A
# Should show READY status
# NAME URL READY STATUS
# gitlab-repo https://gitlab.com/.../repo.git True Fetched revision: abc123
Step 6: Migrate Kustomization and HelmRelease
Apply Existing Resources
Most Kustomization and HelmRelease resources should work without modification:
# Apply Kustomizations
kubectl apply -f $BACKUP_DIR/kustomizations.yaml
# Apply HelmReleases
kubectl apply -f $BACKUP_DIR/helmreleases.yaml
# Apply HelmRepositories
kubectl apply -f $BACKUP_DIR/helmrepositories.yaml
Update Dependencies if Needed
If you have cross-namespace references, ensure they’re allowed:
# In FluxInstance spec
spec:
policies:
allowCrossNamespaceRef: true
Verify Reconciliation
# Check Kustomization status
kubectl get Kustomization -A
# Check HelmRelease status
kubectl get HelmRelease -A
# Watch for reconciliation
flux get kustomizations
flux get helmreleases
Step 7: Configure GitLab Webhooks
Create Webhook Secret
kubectl create secret generic webhook-secret \
--from-literal=token=$(openssl rand -hex 16) \
--namespace flux-system \
--dry-run=client -o yaml | kubectl apply -f -
Get Webhook Secret Value
WEBHOOK_TOKEN=$(kubectl get secret webhook-secret -n flux-system \
-o jsonpath='{.data.token}' | base64 -d)
echo "Webhook Token: $WEBHOOK_TOKEN"
Configure GitLab Webhook
- Navigate to GitLab Project → Settings → Webhooks
- Add new webhook:
- URL:
https://<YOUR-FLUX-UI-DOMAIN>/hook/receiver - Secret Token:
$WEBHOOK_TOKEN - Trigger:
- ✅ Push events
- ✅ Tag push events
- Enable SSL verification: Based on your setup
- URL:
Alternative: Use Flux Receiver
apiVersion: notification.toolkit.fluxcd.io/v1
kind: Receiver
metadata:
name: gitlab-receiver
namespace: flux-system
spec:
type: generic
secretRef:
name: webhook-secret
events:
- "push"
resources:
- apiVersion: source.toolkit.fluxcd.io/v1
kind: GitRepository
name: gitlab-repo
Step 8: Access Flux Web UI
Expose the UI
Option A: Ingress
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: flux-ui
namespace: flux-system
annotations:
nginx.ingress.kubernetes.io/auth-type: basic
nginx.ingress.kubernetes.io/auth-secret: flux-ui-auth
nginx.ingress.kubernetes.io/auth-realm: "Authentication Required"
spec:
rules:
- host: flux.yourdomain.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: flux-ui
port:
number: 80
Option B: Port Forward (Testing)
kubectl port-forward svc/flux-ui -n flux-system 8080:80
Then access: http://localhost:8080
Create Basic Auth (for Ingress)
# Create htpasswd file
htpasswd -c auth admin
# Enter password when prompted
# Create secret
kubectl create secret generic flux-ui-auth \
--from-file=auth \
--namespace flux-system
UI Features
Once logged in, you can:
- View cluster state - All Flux resources and their status
- Monitor reconciliation - Real-time sync status
- View logs - Controller logs for debugging
- Resource details - Click into any resource for details
- Health status - Overall cluster GitOps health
Step 9: Verify Migration
Run Flux Checks
# Check overall Flux health
flux check
# Check source controller
flux get sources git
# Check kustomizations
flux get kustomizations
# Check helm releases
flux get helmreleases
Verify UI Access
# Check UI service
kubectl get svc -n flux-system | grep flux-ui
# Check UI pods
kubectl get pods -n flux-system | grep ui
Test GitOps Flow
# Make a change in GitLab
# Commit a small change to your GitOps repo
# Wait for reconciliation
kubectl get GitRepository gitlab-repo -n flux-system -w
# Verify Kustomization picks up the change
kubectl get Kustomization -n flux-system -w
# Verify application updates
kubectl get pods -n your-app-namespace -w
Troubleshooting
Issue: FluxInstance Not Ready
# Check FluxInstance events
kubectl describe fluxinstance flux -n flux-system
# Check operator logs
kubectl logs -n flux-operator -l app=flux-operator
Issue: GitRepository Not Fetching
# Check GitRepository status
kubectl describe GitRepository gitlab-repo -n flux-system
# Common issues:
# - Invalid credentials: Check secret
# - Wrong URL: Verify GitLab URL
# - Network issues: Check egress rules
# Test Git connection manually
kubectl run git-test --image=alpine/git -it --rm -- \
git ls-remote https://gitlab.com/your-group/your-repo.git
Issue: UI Not Accessible
# Check UI service
kubectl get svc flux-ui -n flux-system
# Check UI deployment
kubectl get deployment flux-ui -n flux-system
# Check UI logs
kubectl logs -l app=flux-ui -n flux-system
Issue: HelmReleases Not Reconciling
# Check HelmRelease status
kubectl describe HelmRelease <name> -n <namespace>
# Check helm-controller logs
kubectl logs -n flux-system -l app=helm-controller
# Common issues:
# - Chart repository unreachable
# - Values validation errors
# - Dependency resolution failures
Post-Migration Tasks
1. Remove Old Flux Installation
Once verified, clean up the old Flux installation:
# DO NOT run this until you've verified everything works!
# flux uninstall --silent
2. Set Up Monitoring
# Create ServiceMonitor for Prometheus
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: flux
namespace: flux-system
spec:
selector:
matchLabels:
app: flux
endpoints:
- port: http-prom
interval: 30s
3. Configure Alerts
apiVersion: notification.toolkit.fluxcd.io/v1beta3
kind: Alert
metadata:
name: on-call-webapp
namespace: flux-system
spec:
providerRef:
name: slack
eventSeverity: error
eventSources:
- kind: GitRepository
name: '*'
- kind: Kustomization
name: '*'
- kind: HelmRelease
name: '*'
4. Document the New Setup
Update your runbooks with:
- Flux Operator management commands
- UI access instructions
- Backup procedures for FluxInstance CR
- Upgrade process (update FluxInstance version)
Rollback Procedure
If you need to rollback to traditional Flux:
# 1. Delete FluxInstance
kubectl delete fluxinstance flux -n flux-system
# 2. Wait for components to terminate
kubectl wait --for=delete fluxinstance/flux -n flux-system --timeout=5m
# 3. Reinstall Flux using CLI
flux install --components=source-controller,kustomize-controller,helm-controller,notification-controller
# 4. Restore resources from backup
kubectl apply -f $BACKUP_DIR/gitrepositories.yaml
kubectl apply -f $BACKUP_DIR/kustomizations.yaml
kubectl apply -f $BACKUP_DIR/helmreleases.yaml
Conclusion
Migrating to FluxCD Operator with UI provides:
Benefits:
- ✅ Declarative Flux management via FluxInstance CR
- ✅ Native web UI for monitoring and troubleshooting
- ✅ Automatic upgrades and version management
- ✅ Better observability with integrated metrics
- ✅ Simplified multi-tenancy support
Considerations:
- 📋 Requires additional namespace (flux-operator)
- 📋 UI needs separate exposure configuration
- 📋 Learning curve for FluxInstance CR
The migration process preserves your existing GitLab integration while adding the benefits of the Operator pattern and web UI. Start with a non-production cluster to validate the process before migrating production environments.
For more information, see the official Flux Operator documentation and Flux Web UI documentation.