Distribuir uma aplicação Node.js normalmente exige instalar o runtime, copiar arquivos, instalar dependências e manter uma estrutura de diretórios. O recurso de Single Executable no Node.js permite empacotar o código da aplicação dentro de um executável baseado no binário do Node.js.
Essa abordagem é útil para CLIs, ferramentas internas, agentes, utilitários de suporte e aplicações que precisam ser entregues a usuários sem configurar um ambiente Node.js completo. Entretanto, gerar um único arquivo não elimina dependências nativas, arquivos externos, diferenças entre sistemas operacionais ou responsabilidades de atualização.
Neste guia, você aprenderá o fluxo de geração de um executável, o arquivo de configuração SEA, o blob de preparação, injeção no binário, assets, CommonJS, ES Modules, dependências, assinatura, cross-compilation, segurança e atualização.
O que é uma Single Executable Application?
Single Executable Applications, ou SEA, é um recurso do Node.js para incorporar um script e recursos em um binário. A documentação oficial de Single Executable Applications descreve o processo e as limitações. O projeto postject no GitHub é uma ferramenta usada para injetar o blob em executáveis.
Para entender os formatos de módulos, veja CommonJS no Node.js e ES Modules no Node.js.
Casos de uso
- ferramentas de linha de comando;
- agentes instalados em servidores;
- utilitários de migração;
- diagnóstico offline;
- aplicações distribuídas para equipes não técnicas;
- executáveis usados em pipelines;
- provas de conceito com instalação simples.
O que não é resolvido automaticamente?
- dependências nativas por plataforma;
- arquivos de configuração externos;
- certificados e segredos;
- migrações de banco;
- atualizações do executável;
- compatibilidade entre sistemas;
- assinatura de código;
- antivírus e políticas corporativas.
Preparando o script principal
// cli.cjs
console.log('Aplicação iniciada');
console.log({
version: process.version,
platform: process.platform,
arch: process.arch
});O fluxo mais direto usa um arquivo CommonJS. Recursos mais recentes podem ampliar suporte, mas consulte a versão do Node.js escolhida.
Arquivo de configuração
{
"main": "cli.cjs",
"output": "sea-prep.blob",
"disableExperimentalSEAWarning": true,
"useSnapshot": false,
"useCodeCache": true
}Salve como sea-config.json. Os campos disponíveis dependem da versão.
Gerando o blob
node --experimental-sea-config sea-config.jsonO comando gera o arquivo de preparação definido em output. A flag e o aviso experimental podem mudar conforme o recurso amadurece.
Copiando o binário do Node.js
Em sistemas Unix:
cp "$(command -v node)" my-toolNo Windows, copie node.exe para um novo nome. Use o binário oficial e verifique sua origem.
Removendo assinatura antes da injeção
Em plataformas como macOS e Windows, um binário assinado pode precisar ter sua assinatura removida antes da alteração. Depois da injeção, assine novamente.
Os comandos variam conforme a plataforma e os certificados disponíveis.
Injetando o blob
npx postject my-tool \
NODE_SEA_BLOB sea-prep.blob \
--sentinel-fuse NODE_SEA_FUSE_...
Use exatamente o fuse documentado pela versão. Um valor incorreto gera executável inválido.
Executando
./my-toolO executável inicia o runtime incorporado e executa o script empacotado.
Automatizando o build
set -e
node --experimental-sea-config sea-config.json
cp "$(command -v node)" dist/my-tool
npx postject dist/my-tool NODE_SEA_BLOB sea-prep.blob \
--sentinel-fuse NODE_SEA_FUSE_...Use CI reprodutível, versão fixa do Node.js e checksum dos artefatos.
Code cache
useCodeCache pode reduzir trabalho de compilação no início. O cache é ligado à versão e à arquitetura do runtime. Teste o executável na mesma plataforma de destino.
Snapshot
useSnapshot pode inicializar estado antecipadamente, mas impõe limitações. Não inclua conexões, descritores ou dados específicos da máquina no snapshot.
Assets
A configuração pode permitir incorporar arquivos:
{
"main": "cli.cjs",
"output": "sea-prep.blob",
"assets": {
"help.txt": "./assets/help.txt",
"schema.json": "./assets/schema.json"
}
}Assets ficam disponíveis por APIs específicas do módulo SEA.
Lendo assets
const { getAsset } = require('node:sea');
const help = getAsset('help.txt', 'utf8');
console.log(help);A assinatura da API depende da versão. Consulte a documentação.
Assets não são segredos
Dados incorporados podem ser extraídos do executável. Não inclua chaves privadas, tokens ou senhas.
Arquivos externos
Configurações que mudam após o build devem permanecer externas:
const configPath = process.env.APP_CONFIG;
const config = JSON.parse(
fs.readFileSync(configPath, 'utf8')
);Use validação e caminhos controlados. Veja File System no Node.js e Variáveis de Ambiente no Node.js.
Dependências JavaScript
O script incorporado não transforma automaticamente todo o node_modules em um bundle. Muitas equipes usam um bundler para gerar um único arquivo CommonJS antes do SEA.
Bundling
Um bundler pode:
- resolver imports;
- eliminar código não usado;
- incluir dependências JavaScript;
- minificar;
- gerar sourcemap.
Teste módulos dinâmicos, leitura de arquivos e caminhos relativos.
require dinâmico
require(pluginName);Bundlers não conseguem resolver todos os valores dinâmicos. Mantenha uma allowlist ou distribua plugins externamente.
__dirname e caminhos
O script incorporado não está em um arquivo comum no disco. Não presuma que __dirname aponta para assets da aplicação.
Use APIs SEA para assets e process.execPath para localizar o executável.
Diretório do executável
const path = require('node:path');
const executableDirectory = path.dirname(process.execPath);Esse diretório pode ser somente leitura. Para dados graváveis, use diretório de dados do usuário ou configuração explícita.
process.argv
const args = process.argv.slice(1);O formato dos argumentos pode diferir de um script normal. Teste a versão e use um parser.
ES Modules
O suporte direto ao script principal pode ter restrições. Uma estratégia é gerar bundle CommonJS. Outra é usar import dinâmico conforme suportado.
Não assuma que import.meta.url representa um arquivo real.
Addons nativos
Arquivos .node são binários por plataforma e geralmente precisam existir fora do executável ou ser extraídos para o disco. Eles também dependem da ABI.
Prefira dependências puramente JavaScript quando a portabilidade é prioridade.
Child Process
Se a aplicação chama binários externos, eles continuam necessários:
spawn('git', ['--version']);O SEA não incorpora automaticamente o Git. Veja Child Process no Node.js.
Certificados
HTTPS pode depender do store de certificados do sistema. Teste em ambientes mínimos e containers.
Cross-compilation
Normalmente, gere um executável para cada combinação de sistema operacional e arquitetura:
- Windows x64;
- Linux x64;
- Linux arm64;
- macOS x64;
- macOS arm64.
O binário base precisa corresponder ao destino.
Build matrix
Use runners nativos ou pipelines separados. Teste o artefato em ambiente limpo, não apenas na máquina de build.
Assinatura no macOS
Após a injeção, assine o binário. Distribuição ampla pode exigir notarização.
Assinatura no Windows
Use certificado de code signing e timestamp. Binários não assinados podem gerar alertas de reputação.
Linux
Bibliotecas do sistema, glibc e outras dependências ainda afetam compatibilidade. Teste em distribuições suportadas.
Tamanho
O executável inclui o runtime e pode ter dezenas de megabytes. Compressão do instalador pode reduzir download, mas o arquivo final continua grande.
Inicialização
Code cache e snapshot podem melhorar startup, mas meça. Antivírus, disco e assinatura também influenciam.
Atualizações
Um executável incorpora uma versão do Node.js. Para receber correções de segurança, é preciso rebuildar e redistribuir.
Versão
console.log({
app: '1.4.0',
node: process.version
});Exponha versão da aplicação e runtime para suporte.
Atualização automática
Se implementar auto-update:
- use HTTPS;
- verifique assinatura;
- verifique hash;
- faça download atômico;
- mantenha rollback;
- não execute arquivo parcial.
Segurança
Empacotamento não protege o código contra análise. Strings, assets e lógica podem ser extraídos.
Permission Model
O executável pode ser iniciado com opções do runtime ou configuração compatível. Avalie o Permission Model no Node.js para limitar arquivos e processos.
Integridade
Publique checksums:
sha256sum my-toolChecksums sem canal confiável não provam autoria. Combine com assinatura.
Sourcemaps
Manter sourcemaps externos ajuda suporte, mas pode expor código. Armazene em sistema restrito e associe à versão.
Logs
Uma CLI deve separar stdout e stderr. Consulte Console no Node.js.
Testes
Teste:
- execução sem Node instalado;
- argumentos;
- assets;
- configuração externa;
- diretório somente leitura;
- atualização;
- assinatura;
- cada plataforma;
- antivírus corporativo;
- caminhos com Unicode.
CI
O pipeline deve:
- fixar Node.js;
- instalar dependências com lockfile;
- executar testes;
- gerar bundle;
- gerar blob;
- copiar binário;
- injetar;
- assinar;
- testar artefato;
- publicar checksum.
Erros comuns
- Assumir que node_modules foi incluído: require falha.
- Usar caminhos relativos: assets não são encontrados.
- Incluir segredos: dados podem ser extraídos.
- Gerar só para uma plataforma: usuários incompatíveis ficam sem artefato.
- Não rebuildar o runtime: vulnerabilidades permanecem.
- Ignorar assinatura: sistema e antivírus bloqueiam.
- Não testar em máquina limpa: dependências externas ficam escondidas.
Boas práticas
- Use versão fixa do Node.js.
- Gere bundle controlado.
- Mantenha segredos externos.
- Use assets apenas para conteúdo público.
- Crie build por plataforma.
- Assine artefatos.
- Publique checksums.
- Teste em ambiente limpo.
- Planeje atualização.
- Monitore versões incorporadas.
Conclusão
O recurso de Single Executable no Node.js simplifica a distribuição de CLIs e utilitários ao incorporar o código em um binário baseado no runtime.
O arquivo único não elimina as responsabilidades de build e segurança. Dependências, assets, addons nativos, assinatura, plataformas e atualizações precisam de planejamento. Com bundle reproduzível, matriz de builds, testes em máquinas limpas e artefatos assinados, SEA pode oferecer uma experiência de instalação simples sem perder controle operacional.




