A Kubernetes operator that watches KubeVirt VirtualMachine resources and syncs them to git repositories for complete GitOps workflows with ArgoCD.
VirtGitSync enables GitOps for KubeVirt VirtualMachines by automatically pushing cleaned VM manifests to git and managing ArgoCD Applications with manual sync control. This creates a single source of truth in git while preventing race conditions between VM changes and ArgoCD reconciliation.
Key Features:
- Automatic git sync: VM changes pushed to git repository in real-time
- Zero drift: YAML cleaning eliminates runtime metadata for Argo CD compatibility
- ArgoCD integration: Automatically creates and manages Application CRs with manual sync control
- Manual sync control: Operator triggers ArgoCD syncs only after git push completes
- Comprehensive auth: SSH keys and HTTPS tokens via Kubernetes Secrets
- Change tracking: Descriptive commit messages with VM change details
- Status visibility: Git and ArgoCD status tracked in VirtGitSync CR
graph LR
VM[VirtualMachines] -->|watch| Operator[VirtGitSync Operator]
Operator -->|clean & push| Git[(Git Repository)]
Operator -->|create/manage| App[ArgoCD Application]
Git <-->|sync| ArgoCD[ArgoCD]
ArgoCD -->|deploy| VM
style Operator fill:#326ce5,stroke:#fff,color:#fff
style Git fill:#f05032,stroke:#fff,color:#fff
style ArgoCD fill:#ef7b4d,stroke:#fff,color:#fff
📊 Detailed Architecture Diagrams - See comprehensive architecture, data flow, and reconciliation workflows.
- Kubernetes cluster v1.26+ with KubeVirt installed
- ArgoCD installed (optional, but recommended)
- Git repository (GitHub, GitLab, Gitea, etc.)
- SSH key or token for git authentication
Install CRDs and deploy the operator:
make install
make deploy IMG=quay.io/mathianasj/virt-git-sync-operator:v2.0.0Or run locally for development:
make install
make runIf using ArgoCD integration, install the required RBAC for ArgoCD to manage VirtualMachines:
make install-argocd-rbacThis grants the ArgoCD application controller permission to manage VirtualMachine resources cluster-wide. Skip this step if you're only using git sync without ArgoCD.
Create a Secret with your SSH private key:
kubectl create secret generic git-ssh-key \
--from-file=ssh-private-key=$HOME/.ssh/id_rsa \
-n defaultCreate a VirtGitSync resource:
apiVersion: virt.mathianasj.github.com/v1alpha1
kind: VirtGitSync
metadata:
name: vm-sync
namespace: default
spec:
gitRepository:
url: git@github.com:myorg/vm-manifests.git
branch: main
secretRef:
name: git-ssh-keyApply the resource:
kubectl apply -f virtgitsync.yamlThe operator will sync all VirtualMachines in the namespace to vms/default/*.yaml in your git repository.
apiVersion: virt.mathianasj.github.com/v1alpha1
kind: VirtGitSync
metadata:
name: vm-gitops
namespace: production
spec:
vmSelector:
matchLabels:
managed-by: gitops
gitRepository:
url: git@github.com:myorg/vm-manifests.git
branch: main
secretRef:
name: git-ssh-key
syncPath: vms
argocd:
namespace: argocd
applicationName: production-vms
destinationNamespace: production
project: defaultThis creates an ArgoCD Application with automated sync disabled. The operator manually triggers syncs after git pushes.
The operator disables ArgoCD's automated sync and instead manually triggers syncs at the right time:
- VM changes → Operator detects and cleans YAML
- Git push → Changes pushed to repository
- Sync trigger → Operator manually triggers ArgoCD sync (only when git is clean)
- ArgoCD applies → Changes deployed back to cluster
This prevents race conditions where ArgoCD might sync while the operator is still pushing changes to git.
flowchart LR
A[VM Change] --> B[Watch Event]
B --> C[Clean YAML]
C --> D[Git Commit]
D --> E[Git Push]
E --> F[Update ArgoCD App]
F --> G[ArgoCD Syncs]
G --> A
style A fill:#FFE4B5
style C fill:#87CEEB
style E fill:#f05032,color:#fff
style F fill:#ef7b4d,color:#fff
- VM Watch: Operator watches VirtualMachine resources (optionally filtered by labels)
- YAML Cleaning: Strips runtime metadata to prevent drift (resourceVersion, uid, etc.)
- Git Commit: Cleaned YAML committed with descriptive message
- Git Push: Changes pushed to remote repository
- Trigger Sync: Operator manually triggers ArgoCD sync (only when git is clean)
- Argo Sync: ArgoCD syncs VMs from git back to cluster
📊 View detailed reconciliation flow
VMs are organized by namespace in your git repository:
repo-root/
vms/ # syncPath (configurable)
default/
vm1.yaml
vm2.yaml
production/
vm3.yaml
vm4.yaml
Create a Secret with your SSH private key:
kubectl create secret generic git-ssh-key \
--from-file=ssh-private-key=$HOME/.ssh/id_rsa \
-n <namespace>Reference in VirtGitSync:
spec:
gitRepository:
url: git@github.com:myorg/repo.git
secretRef:
name: git-ssh-keyCreate a Secret with username and token:
kubectl create secret generic git-https-auth \
--from-literal=username=myuser \
--from-literal=password=ghp_xxxxxxxxxxxx \
-n <namespace>Reference in VirtGitSync:
spec:
gitRepository:
url: https://github.com/myorg/repo.git
secretRef:
name: git-https-authCheck VirtGitSync status:
kubectl get virtgitsync vm-sync -o yamlKey status fields:
status.gitStatus.lastCommit: SHA of last successful commitstatus.gitStatus.lastPush: Timestamp of last pushstatus.gitStatus.lastError: Last git error (if any)status.argocdStatus.applicationCreated: Whether Application CR existsstatus.argocdStatus.lastUpdated: Last Application update time
| Field | Type | Required | Description |
|---|---|---|---|
gitRepository |
GitRepositorySpec | Yes | Git repository configuration |
argocd |
ArgoCDSpec | No | ArgoCD Application configuration |
syncPath |
string | No | Path within repo for VMs (default: "vms") |
vmSelector |
LabelSelector | No | Filter VMs by labels (default: all VMs) |
| Field | Type | Required | Description |
|---|---|---|---|
url |
string | Yes | Git repository URL (ssh or https) |
branch |
string | No | Branch name (default: "main") |
secretRef |
LocalObjectReference | No | Secret with git credentials |
| Field | Type | Required | Description |
|---|---|---|---|
namespace |
string | No | ArgoCD namespace (default: "argocd") |
applicationName |
string | No | Application name (default: VirtGitSync name) |
destinationNamespace |
string | No | Target namespace (default: VirtGitSync namespace) |
project |
string | No | ArgoCD project (default: "default") |
Note: The operator always disables automated sync and manually controls when ArgoCD syncs. This prevents race conditions between git pushes and ArgoCD syncs.
VirtGitSync uses fully automated releases. When you push a version tag, GitHub Actions:
- ✅ Builds multi-arch images (amd64 + arm64)
- ✅ Generates and publishes OLM bundle
- ✅ Creates GitHub release with artifacts
- ✅ Automatically creates PRs to OperatorHub.io and OpenShift catalogs
Use the automated release script:
./release.shOr manually:
git tag v0.2.0
git push origin v0.2.0Within minutes, PRs will be automatically created in:
k8s-operatorhub/community-operators(OperatorHub.io)redhat-openshift-ecosystem/community-operators-prod(OpenShift)
Setup required: See Automated Release Process for one-time GitHub token configuration.
make test # All tests
go test ./internal/git/... # Git manager tests
go test ./internal/argocd/... # ArgoCD manager tests
go test ./internal/controller/... # Controller testsmake build # Build manager binary
make docker-build IMG=<image> # Build container image
make docker-push IMG=<image> # Push container imageRun the operator locally against your kubeconfig cluster:
make install # Install CRDs
make run # Run locallyFor testing with different architectures (e.g., building on Apple Silicon for OpenShift x86_64):
make docker-build-amd64 IMG=quay.io/mathianasj/virt-git-sync:dev
./test-install-dev.sh # Automated OLM installationSee Development Workflow for complete guide including:
- Local vs cluster development
- Multi-architecture builds
- Kustomize overlays for dev/prod
- Testing workflows
Symptom: status.gitStatus.lastError shows auth error
Solutions:
- Verify Secret exists and has correct key name (
ssh-private-keyorusername/password) - For SSH: Ensure private key has no passphrase
- For HTTPS: Use token instead of password
- Check git URL format matches auth type
Symptom: status.argocdStatus.applicationCreated: false
Solutions:
- Verify ArgoCD is installed
- Check ArgoCD namespace in spec matches actual ArgoCD installation
- Review operator logs for RBAC errors
- Ensure operator has permissions for
applications.argoproj.io
Symptom: Argo shows differences between git and cluster
Solutions:
- Verify YAML cleaning:
kubectl diff -f <git-yaml> - Check if system fields leaked through
- Review cleanVMForGitOps() logic
- Check operator logs for git push errors
Breaking Change: Git repository is now REQUIRED in v2.0.
If you used v1.x with local /tmp/vm-sync/ only:
- Create a git repository
- Create authentication Secret
- Update VirtGitSync CR to add
spec.gitRepository - Operator will sync all VMs on next reconcile
No migration path exists for local-only mode.
Contributions welcome! Please:
- Fork the repository
- Create a feature branch
- Add tests for new functionality
- Ensure all tests pass:
make test - Submit a pull request
Licensed under the Apache License, Version 2.0. See LICENSE for details.