惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Forbes - Security
Forbes - Security
H
Hackread – Cybersecurity News, Data Breaches, AI and More
N
Netflix TechBlog - Medium
Engineering at Meta
Engineering at Meta
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
F
Fortinet All Blogs
P
Privacy & Cybersecurity Law Blog
The Hacker News
The Hacker News
博客园 - 司徒正美
博客园 - 聂微东
T
The Blog of Author Tim Ferriss
I
Intezer
WordPress大学
WordPress大学
Security Archives - TechRepublic
Security Archives - TechRepublic
Scott Helme
Scott Helme
T
Threat Research - Cisco Blogs
T
Tailwind CSS Blog
月光博客
月光博客
Recent Announcements
Recent Announcements
博客园 - 叶小钗
J
Java Code Geeks
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
L
LINUX DO - 热门话题
Google DeepMind News
Google DeepMind News
C
Cyber Attacks, Cyber Crime and Cyber Security
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
N
News and Events Feed by Topic
A
Arctic Wolf
B
Blog RSS Feed
Recorded Future
Recorded Future
D
DataBreaches.Net
有赞技术团队
有赞技术团队
Project Zero
Project Zero
U
Unit 42
T
Tor Project blog
The GitHub Blog
The GitHub Blog
Hacker News - Newest:
Hacker News - Newest: "LLM"
C
CERT Recently Published Vulnerability Notes
博客园 - Franky
博客园 - 【当耐特】
Microsoft Security Blog
Microsoft Security Blog
C
Cybersecurity and Infrastructure Security Agency CISA
Security Latest
Security Latest
C
Cisco Blogs
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
Blog — PlanetScale
Blog — PlanetScale
H
Heimdal Security Blog
Vercel News
Vercel News
Stack Overflow Blog
Stack Overflow Blog
S
Securelist

Devoriales - DevOps and Python Tutorials

Cloud & DevOps & AI Digest: The Week of Jun 28, 2026 Cloud & DevOps & AI Digest: The Week of Jun 20, 2026 Ansible for DevOps Engineers: Architecture, Core Concepts, and Hands-On Lab Login Must-Have Kubernetes CLI Tools Every Platform Engineer Should Know Login Login Login Why Your Best Engineers Are Quitting (And How to Stop It) Login ArgoCD Vulnerability: How the ServerSideDiff Feature Exposes Kubernetes Secrets Login How Kubernetes Controls What Your Containers Can Do Login Multi-AZ Is Not Disaster Recovery: What the AWS Bahrain Outage Finally Proved Trivy Supply Chain Attack: When Your Security Scanner Becomes the Threat Is Claude Opus 4.6 Fast Mode Really Worth 6× the Price? Login Unlocking Higher Pod Density in EKS with Prefix Delegation AWS Regional NAT Gateway: What It Is and Why You Should Care Kubernetes 1.35 Timbernetes Release AWS re:Invent 2025: The Future of Kubernetes on EKS Debate Series: Should We Eliminate Kubernetes Secrets Entirely? Kubernetes CRDs Explained: A Beginner-Friendly Guide to Extending the Kubernetes API Reduce Cloud Cross-Zone Data Transfer Costs with Kubernetes 1.33 trafficDistribution Building Custom Bitnami Images: A Guide for Self-Hosted Container Images New Features in Kubernetes 1.34: An Overview From Free to Fee: How Broadcom's Bitnami Monetization Disrupts DevOps Infrastructure Claude Code Cheat Sheet: The Reference Guide Kubernetes Loses Enterprise Slack Status: Discord Among Platforms Being Considered Understanding Container Security: A Guide to Docker and Pod Security Container Patterns in Kubernetes: Init Containers, Sidecars, and Co-located Containers Explained AWS Launches Serverless MCP Server: AI-Powered Development Gets a Serverless Boost Valve Responds to Alleged Steam Data Breach Reports: What Users Need to Know ArgoCD 3.0: The Evolution Toward Secure GitOps Redis Returns to Open Source: The AGPLv3 Licensing Decision New Features in Kubernetes 1.33: An Overview Prometheus: How We Slashed Memory Usage IngressNightmare: Critical Ingress-NGINX Vulnerabilities and How to Check Your Exposure New Features in Kubernetes 1.32: An Overview What to Consider If You're Not Signing Up for Bitnami Premium Certified Kubernetes Administrator (CKA) Exam Updates for 2025 DeepSeek AI and the Question of the AI Bubble Python Tops the Tiobe Index: The Most Popular Programming Languages - January 2025 2024 in Review: IT Trends, Startups, and What’s Next Inside Argo: The Open-Source Journey Captured in a CNCF Documentary Running Docker on macOS Without Docker Desktop - updated with Kubernetes installation HashiCorp Rolls Out Terraform 2.0 at HashiConf, Keeps IBM Acquisition in the Shadows Is the EU Falling Behind in the Global AI Race? Prometheus Essentials: Node Exporter And System Monitoring Prometheus Essentials: Install and Start Monitoring Your App Prometheus Essentials: Introduction To Metric Types Kubernetes Pod Scheduling Explained: Taints, Tolerations, and Node Affinity Retrieval Augmented Generation (RAG) Explained for Beginners Like Me Using Sealed Secrets with Your Kubernetes Applications
Debate Series: How Do We Control Deployment Order in Kubernetes?
Aleksandro Matejic · 2025-12-09 · via Devoriales - DevOps and Python Tutorials

Control Deployment Orders With ArgoCD

A discussion on Reddit recently highlighted a challenge that many engineers and teams face: how to handle automated deployments when each release requires different dynamic steps.

The question was like: "How do you automate deployments in Kubernetes when each release is unique and needs its own workflow?" The scenarios are: pre-deployment schema changes, controlled rollouts across services, and post-deployment verification checks.

The tricky part is, not every deployment follows the same pattern, and some steps are one-time tasks that don't fit into reusable templates.

In this article, we'll look into how the ordering can be configured and controlled in ArgoCD.

"ArgoCD gives us Sync Waves, Hooks, and Progressive Syncs. But when do we use which one?"

Now the question is when I should use which approach. Sync Waves order resources within a single application (except of App of Apps pattern). Hooks run tasks at specific lifecycle points. Progressive Syncs control the order of applications across environments when using ApplicationSet.  Each mechanism serves a purpose so let's look into it.

The Scenario: A Three-Tier Application Stack


Consider a common deployment: a PostgreSQL database, a backend API service, and a frontend application. In the simplest case, you might deploy all three simultaneously and let Kubernetes sort out the startup order. The frontend will retry connections until the backend responds, and the backend will wait for the database to accept connections. This works, but it creates noise in logs and delays in readiness checks.

staggered deployments

The reality becomes more complex once you add requirements: the database needs a schema migration before the backend starts, the backend needs health verification before the frontend deploys, and certain configuration changes require a specific sequence.

Sync Waves - Most Basic Ordering


Sync Waves provide the most basic ordering mechanism.

You annotate resources with argocd.argoproj.io/sync-wave, and ArgoCD processes them in numerical order, waiting for each wave to become healthy before proceeding to the next.

apiVersion: v1
kind: ConfigMap
metadata:
  name: database-config
  annotations:
    argocd.argoproj.io/sync-wave: "-1"
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: postgres
  annotations:
    argocd.argoproj.io/sync-wave: "0"
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: backend-api
  annotations:
    argocd.argoproj.io/sync-wave: "1"
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: frontend
  annotations:
    argocd.argoproj.io/sync-wave: "2"

Please be aware that hooks and resources are assigned to wave 0 by default. If you want something to have higher priority, you should use negative numbers.

This approach establishes a clear dependency chain. The ConfigMap appears first (wave -1), then the database (wave 0), followed by the backend (wave 1), and finally the frontend (wave 2).

ArgoCD Sync Waves

The delay between sync waves is 2 seconds by default. This can be configured by setting the ARGOCD_SYNC_WAVE_DELAY environment variable.

You can verify the order by watching the sync operation:

# Watch the sync progress
kubectl get applications -n argocd -w

# Check which resources are in which wave
kubectl get all -n production -o json | \
  jq -r '.items[] | select(.metadata.annotations."argocd.argoproj.io/sync-wave" != null) | 
  "\(.metadata.annotations."argocd.argoproj.io/sync-wave"): \(.kind)/\(.metadata.name)"'

Sync Waves only order resources within a single Application. To use sync waves between Applications, you need the App of Apps pattern along with custom health assessment configuration (required since ArgoCD v1.8). ref here

Example of the health assessment configuration that needs to be added (as from the official doc):

---
apiVersion: v1
kind: ConfigMap
metadata:
  name: argocd-cm
  namespace: argocd
  labels:
    app.kubernetes.io/name: argocd-cm
    app.kubernetes.io/part-of: argocd
data:
  resource.customizations.health.argoproj.io_Application: |
    hs = {}
    hs.status = "Progressing"
    hs.message = ""
    if obj.status ~= nil then
      if obj.status.health ~= nil then
        hs.status = obj.status.health.status
        if obj.status.health.message ~= nil then
          hs.message = obj.status.health.message
        end
      end
    end
    return hs

They establish ordering but don't address the scenario from the Reddit discussion: what about one-time migration scripts or verification checks that shouldn't remain after deployment?

ArgoCD SyncWaves

"Sync Waves order your long-lived resources, but they don't handle the ephemeral tasks that appear once and then disappear."

Hooks: Ephemeral Tasks at Lifecycle Points


This is where Hooks enter the picture. A Hook runs a Kubernetes resource (typically a Job) at a specific point in the sync lifecycle and then removes it according to a deletion policy. The most common hooks are PreSync and PostSync.

Also, Kubernetes Job objects are fundamentally immutable after creation, meaning they generally cannot be "overwritten" in place. So if you want to re-run a job via ArgoCD, a great way is to have a hook-delete-policy in place. This will make sure that the Job will be deleted which then provides possibility to run the Job again on next Sync. Technically it's a new Job.

Consider the database migration requirement. The schema needs updating before the backend deploys, but the migration Job shouldn't remain in the cluster afterward:

apiVersion: batch/v1
kind: Job
metadata:
  name: database-migration
  annotations:
    argocd.argoproj.io/hook: PreSync
    argocd.argoproj.io/hook-delete-policy: HookSucceeded
    argocd.argoproj.io/sync-wave: "0"
spec:
  template:
    spec:
      containers:
      - name: migrate
        image: migrate/migrate:v4
        command:
        - migrate
        - -path=/migrations
        - -database=postgres://postgres:5432/appdb
        - up
      restartPolicy: Never

The PreSync hook ensures this Job completes before ArgoCD applies the main application resources. The deletion policy removes it once successful.

ArgoCD Hooks

For verification tasks after deployment, PostSync hooks serve the same purpose:

ArgoCD Post Hook

We need to specify the argocd.argoproj.io/hook: PostSync annotation:

apiVersion: batch/v1
kind: Job
metadata:
  name: smoke-test
  annotations:
    argocd.argoproj.io/hook: PostSync
    argocd.argoproj.io/hook-delete-policy: HookSucceeded
spec:
  template:
    spec:
      containers:
      - name: test
        image: curlimages/curl:latest
        command:
        - sh
        - -c
        - |
          curl -f http://backend-api:8080/health || exit 1
          curl -f http://frontend:80/health || exit 1
      restartPolicy: Never

The combination of Hooks and Sync Waves addresses the Reddit scenario. Migrations run before deployment, verification runs after, and each step can be unique to that specific release. Your CI pipeline generates the appropriate Job manifest with specific parameters, commits it to Git, and ArgoCD syncs it once.

"Hooks let you inject imperative steps into a declarative workflow, but they only work within a single ArgoCD Application."

Ordered Deployments

Pruning Order

During resource creation and updates, ArgoCD processes waves from lowest to highest. However, during pruning (deletion), the order is reversed—resources are deleted from highest wave to lowest. This ensures dependencies are cleaned up in the correct order.

Progressive Syncs: Orchestrating Multiple Applications


Note: Progressive Syncs is an Beta feature that must be explicitly enabled and may change in future releases.

The mechanisms discussed so far operate within a single ArgoCD Application. But what happens when your architecture spans multiple Applications? A microservices platform split across five Applications. A multi-environment pipeline where dev, staging, and production are separate Application instances. A multi-region deployment where each region has its own Application.

When you have multiple Applications with dependencies between them, simultaneous syncing breaks ordering guarantees. This is where Progressive Syncs provides control.

The Multiple Application Problem

You might structure deployments as multiple Applications for various reasons:

Separation of concerns:

  • Database infrastructure in one Application
  • Application services in another
  • Monitoring stack in a third

Environment progression:

  • Dev environment as an Application
  • Staging environment as an Application
  • Production environment as an Application

Scale and complexity:

  • A monolithic Application with 500 resources becomes unwieldy
  • Splitting into focused Applications improves clarity
  • But dependencies still exist between them

Geographic distribution:

  • US-East cluster Application
  • EU-West cluster Application
  • APAC cluster Application

Whatever the reason, once you have multiple Applications with dependencies, you need a way to control their sync order. Without Progressive Syncs, all Applications sync simultaneously when changes hit Git. The backend Application syncs before the database migration completes. The production Application deploys while staging is still failing.

What Progressive Syncs Does

Progressive Syncs extends sync control across multiple Applications managed by an ApplicationSet. It defines stages—groups of Applications that sync together—and ensures each stage reaches "Healthy" before the next stage begins.

The ApplicationSet generates multiple Application resources and controls when each syncs based on matching criteria. Applications are grouped by labels, and sync operations follow the defined sequence.

Note: Progressive Syncs is a beta feature that must be explicitly enabled.

Enabling the Feature

Progressive Syncs is not enabled by default. Enable it using one of these methods:

# Method 1: Controller argument
--enable-progressive-syncs

# Method 2: Environment variable
ARGOCD_APPLICATIONSET_CONTROLLER_ENABLE_PROGRESSIVE_SYNCS=true

# Method 3: ConfigMap
kubectl patch cm argocd-cmd-params-cm -n argocd \
  --patch '{"data":{"applicationsetcontroller.enable.progressive.syncs":"true"}}'

Implementation

If we go back to our original story, with the  ApplicationSet we want to generate the three Applications: one for the database layer, one for the backend services, and one for the frontend. Progressive Syncs ensures they deploy in sequence, as shown in the following diagram:

argocd progressive sync

The ApplicationSet acts as the orchestrator, defining which Applications exist and in what order they sync. It uses a list generator to create three separate Applications and a RollingSync strategy to control their sync sequence.

Generators define what Applications to create. In the config below, we're creating three Applications from a list, each with a different component name and priority label. The priority label (1, 2, 3) determines sync order.

Strategy defines how Applications sync. The RollingSync type enables Progressive Syncs. Each step in the rollingSync.steps array defines a group of Applications that sync together. Applications matching step 1 sync first, then step 2, then step 3. The maxUpdate: 100% means all Applications in a step sync simultaneously (you could use 50% to sync half at a time, or 1 to sync one at a time).

Template defines the Application spec for each generated Application. The {{.component}} and {{.priority}} variables get replaced with values from the generator. So we end up with three Applications: platform-database, platform-backend, and platform-frontend, each labeled with its priority.

When this ApplicationSet is applied, ArgoCD creates three Application resources. When changes occur, the ApplicationSet controller syncs them in priority order: database first (must reach healthy), then backend (must reach healthy), then frontend.

apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
  name: platform-stack
spec:
  goTemplate: true
  goTemplateOptions: ["missingkey=error"]
  generators:
  - list:
      elements:
      - component: database
        priority: "1"
      - component: backend
        priority: "2"
      - component: frontend
        priority: "3"
  strategy:
    type: RollingSync
    rollingSync:
      steps:
      - matchExpressions:
        - key: priority
          operator: In
          values: ["1"]
        maxUpdate: 100%
      - matchExpressions:
        - key: priority
          operator: In
          values: ["2"]
        maxUpdate: 100%
      - matchExpressions:
        - key: priority
          operator: In
          values: ["3"]
        maxUpdate: 100%
  template:
    metadata:
      name: 'platform-{{.component}}'
      labels:
        priority: '{{.priority}}'
    spec:
      project: platform
      source:
        repoURL: https://github.com/org/manifests
        targetRevision: main
        path: '{{.component}}'
      destination:
        server: https://kubernetes.default.svc
        namespace: production

The maxUpdate parameter controls how many Applications in each step can update simultaneously. Setting it to `100%` means all Applications in a step update together. Setting it to `50%` updates half at a time. Setting it to `1` updates one Application at a time.

How RollingSync Behaves

RollingSync monitors the OutOfSync status of managed Applications. When Applications become out of sync (due to Git changes, ApplicationSet updates, or manual modifications), RollingSync triggers sync operations following the defined strategy.

You'll see warnings in the `applicationset-controller` logs if Application specs try to enable automated sync policies:

WARN Application has automated sync enabled, but RollingSync requires manual sync control

You can monitor the progressive rollout:

# Watch ApplicationSet status
kubectl get applicationset -n argocd production-stack -o yaml | \
  yq '.status'

# Check individual application sync status
argocd app list --selector priority=1
argocd app list --selector priority=2
argocd app list --selector priority=3

# View the rollout progression
argocd appset get production-stack

read more about the commands  here

As mentioned, RollingSync forces all generated Applications to have auto-sync disabled.

The ApplicationSet controller manages when syncs happen according to your defined steps. Applications won't automatically sync on every Git commit—they sync when their step is active and previous steps are healthy.

ArgoCD ApplicationSet

When you commit a change to Git with RollingSync enabled:

  1. Git change is detected - ArgoCD notices the Git state differs from cluster state
  2. Application marked OutOfSync - The Application status shows it's out of sync
  3. RollingSync sees the OutOfSync status - The ApplicationSet controller monitors this
  4. Sync happens according to strategy - The controller triggers the sync, BUT only when it's that Application's turn

The Choice Point

The question isn't "Should I use Progressive Syncs?" The question is "Do I have multiple Applications that need ordering?"

If you have one Application, use Sync Waves to order resources within it. If you have multiple Applications without dependencies, let them sync independently. If you have multiple Applications with dependencies, Progressive Syncs provides the coordination mechanism.

The ApplicationSet generates Applications and controls their sync sequence. Each Application can have its own source repository, its own sync waves, its own hooks. Progressive Syncs coordinates at the Application level, not the resource level.

Consider the Reddit scenario about unique release steps. That requirement alone doesn't mandate multiple Applications or Progressive Syncs. Hooks handle unique steps within a single Application. Progressive Syncs only adds value when those steps span multiple Applications that need coordination.

Verification in Practice


Understanding these mechanisms conceptually is one thing; verifying their behavior in a running cluster is another. When troubleshooting ordering issues, specific kubectl commands reveal what's happening:

# Check resource order within an Application
kubectl get all -n production -o json | \
  jq -r '.items[] | 
  select(.metadata.annotations != null) | 
  {
    name: "\(.kind)/\(.metadata.name)",
    wave: (.metadata.annotations."argocd.argoproj.io/sync-wave" // "0"),
    hook: (.metadata.annotations."argocd.argoproj.io/hook" // "none")
  }' | jq -s 'sort_by(.wave | tonumber)'

# View current Application health and sync status
argocd app get production-app --refresh

# Check sync operation details including hook execution
argocd app get production-app --show-operation

# Watch live sync progress
argocd app sync production-app --async
kubectl get pods -n production -w

# Verify Progressive Sync step execution
kubectl get applications -n argocd -l priority=1 -o jsonpath='{.items[*].status.sync.status}'

These commands expose the current state and help diagnose when ordering isn't working as expected. The most common issue I've encountered is forgetting that Sync Waves only order resources within the same sync operation which also means within the same application. If you sync one resource outside of application, the wave ordering no longer applies.

Combining All Three Techniques To Order Your Deployments


Each mechanism operates at a different level. Understanding how they layer together provides the full control over deployment ordering:

The Hierarchy:

  1. Progressive Syncs - Controls which Applications sync and when (ApplicationSet level)
  2. Sync Phases - Defines lifecycle stages within each Application (PreSync → Sync → PostSync → SyncFail)
  3. Sync Waves - Orders resources within each phase (resource level)

ApplicationSet Level (Progressive Syncs):

  • Database Application (priority 1) syncs first
  • Backend Application (priority 2) waits until database is healthy
  • Frontend Application (priority 3) waits until backend is healthy

Within Each Application (Phases + Waves):

  • PreSync hooks run (backups, preparations)
  • Sync phase executes with wave ordering (wave -1, then 0, then 1, etc.)
  • PostSync hooks run (tests, notifications)
  • SyncFail hooks run only if sync fails

Within Each Wave (Resource Ordering):

  • ArgoCD applies resources by kind (Namespaces, then ConfigMaps, then Deployments, etc.)
  • Waits for resources to be healthy before proceeding to next wave

Using Them Effectively

Start simple, add complexity only when needed:

  • Single Application with simple ordering → Use Sync Waves
  • Single Application with migrations/tasks → Add Hooks
  • Multiple Applications with dependencies → Add Progressive Syncs

An ApplicationSet with Progressive Syncs can generate Applications that use Sync Waves, which can include Hooks. Each level addresses a different aspect of deployment control: which Applications deploy when (Progressive Syncs), what order resources deploy within Applications (Sync Waves), and what ephemeral tasks run at specific lifecycle points (Hooks).

Related Posts


You can find more posts and tutorials under the ArgoCD and Kubernetes categories.

This post is part of the Debate Series on devoriales.com, where I share my perspective on topics that often spark strong opinions in the tech community. My view is not the final answer, and it shouldn't be treated as such. The goal is to exchange ideas, question assumptions, and learn from each other. If you see something differently or have experience that adds another angle, I welcome your thoughts. These discussions shape how we grow, and my own opinions may shift as the conversations continue.