Build do Tauri no GitHub Actions: quanto demora

Vitor Ayres (abre em nova aba)

Desenho de plantas gerado a partir do primeiro commit do repositório tauri-build.
Plantas geradas pelo primeiro commit de tauri-build (f7d36a5).

Um app Tauri leva minutos para compilar no GitHub Actions, e a internet está cheia de conselhos genéricos sobre Rust em CI: cache, sccache, trocar o linker, cortar dependências. Quisemos saber quais valem para o Tauri, e quanto.

Este texto registra o que cerca de 380 builds válidos, em 24 lotes, mostraram entre 10 e 12 de setembro de 2026. Cada número abaixo é a mediana de pelo menos 2 amostras, e cada afirmação traz o grau de confiança que o registro de evidências dá a ela. O app é pequeno, com um frontend trivial, e isso limita o que os números dizem.

Resumo

  • Runner de 4 núcleos, sem cache, app pequeno: 3 a 5,5 minutos. O cargo é quase tudo.
  • Um cache do target/ corta 55 a 65% de um commit comum. rust-cache e actions/cache empatam.
  • No Linux, não gerar AppImage e rpm economiza 1m14s, mais que qualquer flag.
  • codegen-units = 1 é a opção cara do perfil de release. LTO fat custa bem menos.
  • panic = "abort" corta o binário do Windows pela metade.
  • sccache, troca de linker e corte de dependências não ajudaram.
  • A variação entre runners é de 5 a 30%, até 40% no macOS. Diferença abaixo de 10% é ruído.

Onde o tempo vai

Um build frio, sem cache, num runner de 4 núcleos, estágio por estágio:

Estágio Linux Windows macOS
Dependências do sistema (apt) 33 s 0 0
pnpm install 2 s 10 s 3 s
Build do frontend 2 s 3 s 2 s
cargo 3m43s 5m05s 2m50s
Empacotamento 1m18s 17 s 12 s

Confiança alta. O apt, que muita gente aponta como o vilão do Linux, custa 33 segundos. O cargo custa de 3 a 5 minutos, e dentro dele o piso é a crate do app, que carrega o runtime do Tauri monomorfizado: de 40 a 55 segundos no Linux e de 70 a 110 no Windows, mesmo com tudo o mais em cache.

O tamanho do runner decide os números absolutos. Repositórios públicos ganham 4 núcleos no Linux e no Windows, e privados, 2. O mesmo build frio levou 8m57s contra 5m03s no Linux e 9m41s contra 5m25s no Windows. Confiança média, porque o lado de 2 núcleos é uma amostra só. As proporções entre opções transferem melhor que os tempos.

A variação entre runners idênticos é de 5 a 30%. Builds frios iguais no Linux repetiram com 4 segundos de diferença, mas o Windows espalhou de 4m30s a 5m54s, e dois jobs idênticos no macOS no mesmo run levaram 3m33s e 5m02s. Uma diferença de uma amostra só, abaixo de 20% no Windows ou 40% no macOS, não quer dizer nada.

Cache: o que mais corta

Build quente, depois de uma mudança de uma função na crate do app, mediana de 2:

Linux Windows macOS
Sem cache 5m03s 5m25s 3m04s
rust-cache 2m17s 2m06s 1m17s
actions/cache simples 2m07s 2m25s 1m14s
rust-cache + sccache 2m05s 2m29s 1m30s
sccache sozinho 3m55s 4m22s 1m38s

Confiança média. O rust-cache e o actions/cache simples diferem menos que a variação em todos os runners. A escolha entre eles é de tamanho e higiene: o rust-cache poda o que não foi usado e sempre recompila a crate do workspace; o actions/cache guarda tudo e chega antes ao limite de 10 GB por repositório.

Encher o cache não custa nada: o build frio com rust-cache levou 4m31s contra 5m03s sem cache. Uma dependência nova custa o mesmo que uma mudança no app: adicionar chrono custou 3 segundos no Linux e 15 no Windows.

Num monorepo de 16 plugins e 434 crates, a opção cache-workspace-crates do rust-cache economizou de 15 a 30 segundos, e o cargo quente ficou em 57 segundos no Linux contra 5m35s sem cache.

Não gere instaladores no pull request

Build frio no Linux, mediana de 2, contra a linha de base com todos os bundlers:

Linux tauri build empacotamento diretório de bundles
deb, rpm e AppImage 5m03s 1m18s 339 MB
só deb 3m25s 4 s 17,7 MB
sem bundle 3m24s 2 s nada

Confiança alta para o estágio de empacotamento, que ficou entre 1m07s e 1m20s em todo job Linux de quatro lotes, e média para o tempo total. É a maior economia individual no Linux, maior que qualquer flag de compilador.

No Windows, o empacotamento vai de 17 segundos com NSIS e MSI para 11 só com NSIS e 6 sem bundle. No macOS, de 12 segundos com .app e DMG para 2 só com .app. Nenhum vale tirar pelo tempo. O DMG vale tirar dos builds de pull request porque é o passo que falha: 1 de 24 jobs macOS morreu no hdiutil.

O perfil de release

Tempo do cargo no app maior, mediana das amostras agrupadas de dois lotes, com o binário em Linux, Windows e macOS:

Linux Windows macOS Binário
Referência 3m51s 6m08s 3m25s 10,3 / 10,4 / 7,2 MB
codegen-units = 1 +26% +33% +54% 8,6 / 9,4 / 5,9 MB
lto = "fat" +14% -2% +12% 8,6 / 9,8 / 6,1 MB
Perfil fast -18% -23% -19% 14,3 / 10,3 / 11,4 MB

Confiança alta para codegen-units = 1 e para o perfil fast, média para o LTO fat.

Uma unidade de codegen serializa a crate do app, a que carrega o runtime do Tauri, num núcleo só. O binário fica de 15 a 20% menor. Subir para 256 unidades foi na direção contrária do esperado, mais lento no Linux e no Windows, porque com 4 núcleos não há paralelismo para comprar além das 16 unidades padrão.

O LTO fat teve um dia ruim de +44% no macOS que o rerun não repetiu. O LTO thin não muda nada, nem em tempo nem em tamanho. O opt-level sozinho quase não move o tempo: um build Tauri gasta nas dependências, na monomorfização e no link, não no otimizador.

O perfil fast, com opt-level = 1, 256 unidades e LTO desligado, vale cerca de 20% do tempo do cargo, por um binário de 40 a 60% maior no Linux e no macOS. Cache e bundlers valem de 50 a 70%. Ele vem depois deles, e só em pull request.

panic = "abort" é a economia de tamanho mais barata da lista: o binário foi de 10,4 para 4,9 MB no Windows, de 10,3 para 7,6 no Linux e de 7,2 para 5,0 no macOS, com o tempo dentro da variação. Confiança alta para o tamanho, baixa para o tempo. Só serve se o app não captura panics.

O que não ajudou

sccache. Sozinho é a pior opção com cache, porque sem o target/ restaurado toda crate ainda precisa ser linkada e a crate final compilada. Em cima do rust-cache, não ajuda num commit comum: a crate do app recompila de qualquer jeito, e os uploads por objeto às vezes deixam a combinação atrás do rust-cache sozinho. O piloto mostrou um ganho de 3 vezes, mas só porque nada tinha mudado entre os runs.

Pior: o backend do GitHub Actions do sccache grava um item de cache por objeto compilado, e a API de cache limita a taxa por repositório. Matrizes de 24, 30 e 42 jobs perderam de 34 a 100% das escritas, e 4 jobs com sccache entre 10 ainda perderam a metade e derrubaram um save do rust-cache junto. O sccache só avisa como “Cache write errors” nas estatísticas, e o job termina verde com o cache pela metade. Confiança alta, com os contadores nos logs de 24 jobs.

Trocar o linker. O Rust 1.90 em diante já usa rust-lld no Linux, então lld e mold caem no mesmo tempo. No Windows, rust-lld.exe não foi mais rápido.

Cortar dependências. Tirar reqwest e tokio, 29 crates, não teve efeito mensurável, nem frio nem quente, em 4 núcleos.

Compilação incremental. Custa de 7 a 8% num build frio e só paga quando o target/ sobrevive entre builds, cortando um rebuild para 3 a 8 segundos no melhor caso.

Configuração recomendada

Para o pull request: rust-cache, sem bundlers, perfil rápido. Medido junto, o tauri build levou 3m05s, 4m22s e 2m23s frio e 39 segundos, 1m42s e 39 segundos quente, no Linux, no Windows e no macOS. O job inteiro quente, com checkout, toolchain e apt, ficou entre 1m16s e 1m38s no Linux. Confiança média.

- uses: Swatinem/rust-cache@v2
  with:
    workspaces: src-tauri -> target
- run: pnpm tauri build --no-bundle -- --profile fast

Para a release: o mesmo cache e o perfil como ele é entregue, com todos os bundlers. Um preset de release com sccache, lld e LTO thin por cima foi medido e saiu de 15 a 40 segundos mais lento quente que o cache sozinho. Não há o que acrescentar.

Como verificamos

Foram 24 lotes de resultados entre 10 e 12 de setembro de 2026, em ubuntu-24.04 e windows-2022 com 4 núcleos, num repositório público, e macos-14 com 3. Os três primeiros lotes rodaram com 2 núcleos, num repositório privado, e valem só como verificação do pipeline. Rust 1.98.1. Cada lote tem o seu relatório e um registro de tempo por job.

Cada célula é a mediana de 2 a 6 amostras, com chaves de cache separadas por experimento e por lote, depois que um lote piloto contaminou o seguinte com caches ainda válidos. O tempo de parede é o do tauri build, o do cargo vem do --timings, e o empacotamento é o resto, incluindo a sobrecarga da CLI do Tauri.

O que não medimos: toolchain nightly e frontend paralelo, runners maiores, contêineres prontos, compilação cruzada, e onde começa o limite de taxa do sccache com 1 ou 2 jobs. O app de teste é pequeno e o frontend é trivial; um SPA grande põe minutos na linha do frontend.

O repositório com os workflows, os registros e o livro de evidências é o tauri-build. Uma versão em inglês desse material está em rascunho para a documentação do Tauri, à qual já contribuímos com tutoriais executáveis.

O mesmo cuidado em medir antes de decidir aparece no post sobre as capas do blog, geradas no build, e o GitHub Actions sem servidor nenhum é também o que publica as filas da ANVISA como feeds. Se a sua empresa mantém um app desktop em Tauri, é esse o trabalho que fazemos em código aberto.

Perguntas

Quanto demora um build do Tauri no GitHub Actions?
Um app pequeno, num runner de 4 núcleos e sem cache: 5m03s no Linux, 5m25s no Windows e 3m04s no macOS. Num repositório privado, com 2 núcleos, cerca de 9 minutos no Linux e no Windows.
Quanto o cache economiza num build do Tauri?
Com o rust-cache, um commit comum caiu de 55 a 65%: 2m17s no Linux, 2m06s no Windows e 1m17s no macOS. O actions/cache simples empata em tempo.
O sccache ajuda num build do Tauri no GitHub Actions?
Com o backend do GitHub Actions, não. Sozinho foi a pior opção com cache, 3m55s no Linux, e em cima do rust-cache ficou dentro da variação. Numa matriz de jobs ele perde mais da metade das escritas por limite de taxa.
Qual opção do perfil de release mais pesa no build?
codegen-units = 1: mais 26% no Linux, 33% no Windows e 54% no macOS no tempo do cargo. O LTO fat custa bem menos e encolhe o binário quase igual.
O que mais economiza num build Linux do Tauri?
Não gerar AppImage e rpm. O empacotamento cai de 1m18s para 4 segundos e o diretório de bundles de 339 MB para 18 MB. Mais que qualquer flag de compilador.

Como podemos ajudar?

Oi (abre em nova aba)