Matrix strategy no GitHub Actions

Matrix strategy executa o mesmo job em múltiplas configurações em paralelo, testar em Node.js 20 e 22 simultaneamente, em Ubuntu e macOS, ou com diferentes variáveis de configuração. O que levaria 10 minutos em sequência leva 2 minutos em paralelo, com visibilidade clara de quais combinações passaram ou falharam.

Matrix básico: múltiplas versões Node.js

Testar em Node.js 20 e 22 em paralelo:

yaml
# .github/workflows/ci.yml
jobs:
  test:
    runs-on: ubuntu-latest

    strategy:
      matrix:
        node-version: ['20', '22']
      fail-fast: false    # continuar outras combinações se uma falhar

    name: Node.js ${{ matrix.node-version }}

    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: ${{ matrix.node-version }}
          cache: 'npm'
      - run: npm ci
      - run: npm test

# Resultado: 2 jobs rodando em paralelo
# ✅ Node.js 20 — test
# ✅ Node.js 22 — test

Matrix multidimensional: OS + versão

Testar em múltiplos sistemas operacionais e versões simultaneamente:

yaml
strategy:
  matrix:
    os: [ubuntu-latest, windows-latest, macos-latest]
    node-version: ['20', '22']
    # Gera 6 combinações (3 OS × 2 versões)

runs-on: ${{ matrix.os }}

# Excluir combinações específicas:
strategy:
  matrix:
    os: [ubuntu-latest, windows-latest, macos-latest]
    node-version: ['20', '22']
    exclude:
      # macOS é caro em minutos — excluir Node.js 20
      - os: macos-latest
        node-version: '20'

# Incluir configurações extras:
    include:
      # Adicionar combinação Node.js 22 + ubuntu com variável extra
      - os: ubuntu-latest
        node-version: '22'
        run-e2e: true

Matrix com variáveis de configuração

Use matrix para testar diferentes configurações da aplicação:

yaml
strategy:
  matrix:
    database:
      - { type: postgres, version: '15', port: 5432 }
      - { type: postgres, version: '16', port: 5432 }
      - { type: mysql, version: '8.0', port: 3306 }

services:
  db:
    image: ${{ matrix.database.type }}:${{ matrix.database.version }}
    ports:
      - ${{ matrix.database.port }}:${{ matrix.database.port }}
    env:
      POSTGRES_PASSWORD: test    # ou MYSQL_ROOT_PASSWORD
      POSTGRES_DB: testdb

steps:
  - name: Rodar testes
    env:
      DB_TYPE: ${{ matrix.database.type }}
      DB_PORT: ${{ matrix.database.port }}
    run: npm run test:db

Matrix com include dinâmico via JSON

Gere a matrix dinamicamente a partir de um script:

yaml
jobs:
  # Job 1: gerar a matrix
  setup-matrix:
    runs-on: ubuntu-latest
    outputs:
      matrix: ${{ steps.generate.outputs.matrix }}

    steps:
      - uses: actions/checkout@v4
      - name: Gerar matrix de módulos afetados
        id: generate
        run: |
          # Detectar quais módulos do monorepo mudaram
          MODULES=$(git diff --name-only origin/main | grep "^apps/" | cut -d/ -f2 | sort -u | python3 -c "
          import sys, json
          modules = [line.strip() for line in sys.stdin if line.strip()]
          print(json.dumps({'module': modules}))
          ")
          echo "matrix=$MODULES" >> $GITHUB_OUTPUT

  # Job 2: usar a matrix gerada
  test:
    needs: setup-matrix
    runs-on: ubuntu-latest
    strategy:
      matrix: ${{ fromJson(needs.setup-matrix.outputs.matrix) }}

    steps:
      - uses: actions/checkout@v4
      - run: cd apps/${{ matrix.module }} && npm test

Controle de falhas na matrix

Configure o comportamento quando uma combinação falha:

yaml
strategy:
  matrix:
    node-version: ['18', '20', '22']

  # fail-fast: true (padrão) — cancela outras combinações se uma falhar
  # fail-fast: false — continua todas as combinações mesmo com falha
  fail-fast: false

  # max-parallel: limitar número de jobs simultâneos
  max-parallel: 2    # rodar no máximo 2 de uma vez (economiza minutos)

# Tornar uma combinação "allowed to fail":
    include:
      - node-version: '18'
        experimental: true

    # No job:
    continue-on-error: ${{ matrix.experimental == true }}

# Identificar qual combinação falhou:
# Na interface GitHub Actions:
# Jobs → test (Node.js 20) → falhou
# Jobs → test (Node.js 22) → passou

Não quer configurar manualmente?

Implante o VPS em menos de 5 minutos com a Runstack. Infraestrutura da OPEN Datacenter, com servidores em São Paulo.

Perguntas frequentes

256 jobs por workflow run (limite do GitHub). Uma matrix de 4 OS × 4 versões = 16 combinações, bem dentro do limite. Para matrizes muito grandes (monorepos com centenas de módulos): use matrix dinâmica gerada por script e limite com max-parallel para não consumir todos os runners simultâneos.

Estratégias: (1) exclude combinações desnecessárias (macOS é 10x mais caro em minutos); (2) rodar matrix completo apenas em PRs para main, só a versão principal em outros branches; (3) max-parallel para limitar jobs simultâneos; (4) usar self-hosted runners para evitar cobranças de minutos.

Cada job da matrix tem seu próprio ambiente isolado. Para compartilhar: use actions/upload-artifact com nome único por combinação (ex: test-results-${{ matrix.node-version }}) e actions/download-artifact em um job posterior que usa needs: [test]. O job de agregação pode então combinar todos os resultados.

Sim. Exemplo: deploy em múltiplas regiões em paralelo: matrix: { region: [sa-east-1, us-east-1, eu-west-1] }. Cada job faz deploy na sua região. Com fail-fast: false, se uma região falhar as outras continuam. Útil para distribuições globais.

fail-fast: true (padrão): economiza minutos cancelando jobs desnecessários quando um falha, bom para pipelines de produção onde você quer feedback rápido. fail-fast: false: útil quando quer saber TODAS as combinações que falham antes de corrigir, bom para testar compatibilidade de versões ou detectar falhas intermitentes.