Skip to main content
In this guide, you’ll learn how to prevent overlapping deployments by using the concurrency key in GitHub Actions. We’ll start with a simple Docker build-and-publish workflow, simulate a long-running deployment, and then introduce concurrency controls to ensure only one deployment runs at a time.

1. Base Workflow

This workflow builds, logs in, and publishes a Docker image when manually triggered:

2. Adding a Long-Running Deploy Job

To demonstrate overlapping runs, let’s add a deploy job that runs the container and then sleeps for 10 minutes:
If you trigger this workflow while the previous deploy is still sleeping, you’ll end up with two simultaneous deployments—often a recipe for configuration drift or resource conflicts.
Once pushed, manually trigger the workflow:
The image shows a GitHub Actions workflow interface with a job titled "Exploring Variables and Secrets," displaying a sequence of steps involving "docker" and "deploy" processes.

3. Understanding Concurrency in GitHub Actions

GitHub Actions offers a concurrency key to group runs and control whether new runs cancel or queue behind in-progress ones:
Use a descriptive group name (for example, production-deployment) so unrelated workflows do not interfere with each other.
The image shows a GitHub documentation page about using concurrency in GitHub Actions, explaining how to run a single job at a time within a concurrency group.

4. Enabling Concurrency on the Deploy Job

Add concurrency to the deploy job so that any new deployment run cancels the one in progress:
After committing the change, triggering another run will immediately cancel the previous deploy:

5. Demonstration: Cancel in Progress = true

  1. Trigger Workflow Adeploy starts and sleeps.
  2. Trigger Workflow B → cancels A’s deploy job and starts B’s.
The image shows a GitHub Actions workflow interface with a "variable-secrets.yml" workflow that was manually triggered and then canceled. It includes a visual representation of the workflow steps and annotations explaining the cancellation.
You’ll see:
“The deploy job was canceled because a higher priority waiting request for the production deployment exists.”

6. Demonstration: Cancel in Progress = false

If you prefer to queue new runs behind in-progress ones, set cancel-in-progress: false:
  1. Trigger Workflow A → sleeps in deploy.
  2. Trigger Workflow B → its docker job runs immediately, but its deploy job waits.
The image shows a GitHub Actions workflow interface with a job titled "Exploring Variables and Secrets" that is waiting for the "deploy" step to complete. The workflow includes steps for "docker" and "deploy" with a status indicator.
Hovering over the pending job reveals it’s waiting for the prior deployment to finish.

Conclusion

By defining concurrency.group and choosing whether to cancel-in-progress, you can enforce single-instance deployments or queue them, protecting your production environment from conflicts and ensuring predictable rollouts.

Watch Video