Migrating to FluxCD Operator with UI

Migrating to FluxCD Operator with UI
Page content

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

  1. Navigate to GitLab ProjectSettingsDeploy Tokens
  2. Click Add deploy token
  3. Configure:
    • Name: flux-deploy
    • Username: flux-deploy (auto-generated)
    • Expiration date: Optional (set for security)
    • Scopes:
      • read_repository
      • write_repository (for image updates)

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

  1. Navigate to GitLab ProjectSettingsWebhooks
  2. 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

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
graph TB A[User] --> B[Flux UI Dashboard] B --> C[GitRepositories] B --> D[Kustomizations] B --> E[HelmReleases] B --> F[Events & Logs] C --> G[GitLab Status] D --> H[Sync Status] E --> I[Deploy Status] F --> J[Error Details] style B fill:#4a90d9 style G fill:#28a745 style H fill:#28a745 style I fill:#28a745

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.