Streamlining CI/CD with GitHub Actions Matrix Builds for Sequential Deployments
GitHub Actions matrix builds are usually about running things in parallel — test across multiple OS versions, Node versions, whatever. But you can also use them to run things sequentially, which turns out to be a clean way to handle multi-stage deployments.
The trick: max-parallel 1
Say you’re deploying to development, integration, and production, and the steps are identical across stages. Instead of copy-pasting the same job three times, use a matrix with max-parallel: 1:
jobs:
deploy:
runs-on: ubuntu-latest
strategy:
matrix:
stage: ['development', 'integration', 'production']
fail-fast: true
max-parallel: 1
steps:
- name: Execute deployment tasks
uses: ...
with: ...
max-parallel: 1 forces the stages to run one at a time, in order. fail-fast: true stops the whole pipeline if any stage fails — so a broken development deploy won’t roll forward into production.
Per-stage secrets with GitHub Environments
This pairs well with GitHub Environments. Set environment: name: ${{ matrix.stage }} and each matrix run picks up the secrets configured for that environment:
jobs:
deploy:
runs-on: ubuntu-latest
strategy:
matrix:
stage: ['development', 'integration', 'production']
fail-fast: true
max-parallel: 1
environment:
name: ${{ matrix.stage }}
steps:
- name: Configure AWS Credentials
uses: aws-actions/configure-aws-credentials@v1
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: wonderland-central-1
- name: Execute deployment tasks
uses: ...
with: ...
Same secrets.AWS_ACCESS_KEY_ID reference in the YAML, different actual values per stage. No conditionals, no duplicated jobs.
That’s it. One job definition, sequential execution, per-environment secrets, and the pipeline stops on first failure.