Argo CD Integration Guide
Map each LynqNode to an Argo CD Application. Lynq renders the Application manifests from database rows; Argo CD handles GitOps sync and progressive delivery.
Overview
Lynq can render Argo CD Application manifests for every active node row. Each LynqNode becomes the canonical source of truth for a corresponding Argo CD Application, enabling GitOps workflows, progressive delivery, and automated cleanup.
Core Benefits
- 1:1 Mapping – Every LynqNode owns exactly one Argo CD Application (
LynqNode↔️Application). - Automatic Sync – Application source paths follow node metadata (UID, plan, region).
- Declarative Cleanup – When a LynqNode is deleted (or deactivated), the Argo CD Application and downstream workloads are removed.
- GitOps Alignment – Teams keep delivery pipelines in Git, while Lynq handles orchestration and lifecycle.
Prerequisites
- Argo CD installed (v2.8+ recommended) and accessible from the node namespace.
- ServiceAccount and RBAC granting Lynq permission to create Argo CD
Applicationobjects in the Argo CD namespace (oftenargocd). - Lynq chart deployed with namespace permissions covering the Argo CD API group.
- Git repository that hosts node application configuration.
Baseline Template (1 LynqNode ➝ 1 Application)
The following template renders an Argo CD Application per LynqNode. Each Application points to a unique Git path derived from node metadata.
apiVersion: operator.lynq.sh/v1
kind: LynqForm
metadata:
name: argocd-app-template
spec:
hubId: saas-registry
manifests:
- id: argocd-app
nameTemplate: "{{ printf \"%s-app\" (.uid | trunc63) }}"
spec:
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
namespace: argocd
labels:
lynq.sh/uid: "{{ .uid }}"
lynq.sh/region: "{{ .region | default \"global\" }}"
spec:
project: nodes
source:
repoURL: https://github.com/your-org/node-configs.git
targetRevision: main
path: "nodes/{{ .uid }}"
destination:
server: https://kubernetes.default.svc
namespace: "{{ .uid }}-workspace"
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true
- ApplyOutOfSyncOnly=trueFlow
Advanced Sync Patterns
| Pattern | Description | LynqForm Template Hints |
|---|---|---|
| Environment Branching | Target different Git branches per region or plan (targetRevision: "{{ ternary \"main\" \"staging\" (eq .planId \"enterprise\") }}"). | Use extra value mappings for planId, region. |
| Dynamic Paths | Compose repo paths from UID segments (path: "nodes/{{ .region }}/{{ .uid }}"). | Use Sprig splitList, join, default. |
| App-of-Apps | Point each node to an Application that references node-specific sub-apps. | Render Application with path: nodes/{{ .uid }}/apps. |
| Multi-Cluster Delivery | Route nodes to dedicated clusters using Argo CD credentials (destination.server). | Map datasource columns to clusterServer, clusterName. |
| Progressive Rollouts | Annotate Applications for Argo Rollouts or Progressive Sync plugins. | Add metadata.annotations via templates. |
Additional Use Cases
1. AppSet Fan-Out per Node Plan
- Combine Lynq with Argo CD ApplicationSet.
- Lynq renders a control-plane Application that references an ApplicationSet generator.
- Generator reads node metadata (via ConfigMap/Secret) to produce feature-specific Applications per plan tier.
2. Multi-Cluster Nodes with Cluster Secrets
- Add
extraValueMappingsfor cluster credentials. - LynqForm template creates:
- An Argo CD
ClusterSecret(with kubeconfig) in the Argo CD namespace. - An
Applicationtargeting that cluster secret.
- An Argo CD
- Enables dedicated clusters per enterprise node.
3. Canary and Blue/Green Releases
- Render two Applications per node (
node-app-canary,node-app-stable) with differenttargetRevision. - Use
creationPolicy: Onceon the stable Application andWhenNeededon the canary for rapid rollback. - Combine with Argo Rollouts by templating
analysisand promotion hooks.
Verification Commands
After deploying, verify the integration works correctly:
# 1. Check LynqNodes created Argo CD Applications
kubectl get applications -n argocd -l lynq.sh/uid
# Example output:
# NAME SYNC STATUS HEALTH STATUS AGE
# acme-corp-app Synced Healthy 5m
# beta-inc-app Synced Healthy 5m
# 2. Verify Application points to correct Git path
kubectl get application acme-corp-app -n argocd -o jsonpath='{.spec.source.path}'
# Expected: nodes/acme-corp
# 3. Check sync status
kubectl get application acme-corp-app -n argocd -o jsonpath='{.status.sync.status}'
# Expected: Synced
# 4. Check health status
kubectl get application acme-corp-app -n argocd -o jsonpath='{.status.health.status}'
# Expected: Healthy
# 5. View sync history
argocd app history acme-corp-app
# 6. Force sync (if needed)
argocd app sync acme-corp-app
# 7. Check downstream workloads
kubectl get all -n acme-corp-workspace
# 8. View Application events
kubectl describe application acme-corp-app -n argocd | tail -20Monitor Both Systems:
# Combined health check
echo "=== LynqNode Status ===" && \
kubectl get lynqnode acme-corp-web-app -o jsonpath='{.status.conditions[?(@.type=="Ready")].status}' && \
echo "" && \
echo "=== Argo CD Application Status ===" && \
kubectl get application acme-corp-app -n argocd -o jsonpath='{.status.sync.status}/{.status.health.status}'
# Expected: True (LynqNode Ready) + Synced/Healthy (Argo CD)Troubleshooting
Application not created after LynqNode is Ready
The operator may lack permission to create Application objects in the argocd namespace.
# Check operator RBAC
kubectl auth can-i create applications.argoproj.io -n argocd \
--as=system:serviceaccount:lynq-system:lynq-controller-manager
# Should return: yes
# Check for apply errors in operator logs
kubectl logs -n lynq-system deployment/lynq-controller-manager | grep "argocd"Application created but not syncing
# Check Argo CD project allows the destination namespace
kubectl get appproject nodes -n argocd -o jsonpath='{.spec.destinations}'
# Check source repo is accessible
argocd repo listApplication deleted but workloads remain
Argo CD's prune: true only removes resources it manages in Git. Resources created outside the Git path persist. Use argocd app sync --prune to force cleanup.
LynqNode Ready but Application Degraded
Lynq only manages the Application object lifecycle. Argo CD sync failures (wrong Git path, missing manifests) are outside Lynq's scope — check argocd app get <name> for details.
Caveats
- RBAC is cross-namespace: The Argo CD
ApplicationCR lives in theargocdnamespace, not the LynqNode namespace. The operator ClusterRole must includeargoproj.ioAPI group with verbscreate,update,patch,delete,get,list,watch. - Deletion cascade: When a LynqNode is deleted, Lynq deletes the
ApplicationCR. Argo CD then prunes the downstream workloads (only ifprune: true). UsedeletionPolicy: Retainon theargocd-appmanifest entry to keep the Application for post-mortem inspection. - Application name length: Argo CD Application names must be ≤ 63 characters. Always use
trunc63innameTemplate. - Cross-namespace tracking: Because the
Applicationlives inargocd(not the LynqNode namespace), Lynq uses label-based tracking (lynq.sh/node,lynq.sh/node-namespace) rather than ownerReferences.
Co-Management Rules (Lynq + Argo CD on the same resource)
When Lynq manages the Application CR and Argo CD writes back to it (e.g., status.sync, status.health, operationState), follow these rules:
- Use
patchStrategy: apply(SSA) on Lynq's template for theApplicationresource. Lynq'sfieldManagerislynq-operator; Argo CD writes status as its own manager. SSA preserves each manager's owned fields automatically. - Argo CD's status updates are status-subresource writes and do not bump
metadata.generation. Lynq's watch predicate ignores status-only andlynq.sh/*annotation-only changes, so Argo CD's normal activity does not trigger spurious Lynq reconciles. - Never strip Lynq's tracking annotations from the
Applicationresource —lynq.sh/applied-hash,lynq.sh/apply-start-time,lynq.sh/deletion-policy,lynq.sh/node,lynq.sh/node-namespace. These are written atomically with the spec and removing them forces Lynq to re-apply on every reconcile. - Do NOT use
patchStrategy: replacefor theApplicationresource — it wipes every field not in Lynq's template, including Argo CD'soperationandstatusblocks, on every apply.
If Lynq and Argo CD appear to fight over the same field, run kubectl get application <name> -n argocd -o yaml --show-managed-fields to identify the owner and update Lynq's template to either declare the field (Lynq owns it) or omit it and list it in ignoreFields (Argo CD owns it).
See Also
- Templates – Advanced templating and function usage.
- Policies – Control resource lifecycle (Retain vs. Delete).
- Monitoring – Capture Argo CD and Lynq metrics together.
