[{"data":1,"prerenderedAt":839},["ShallowReactive",2],{"doc-\u002Fdocs\u002Foperacoes\u002Fprimeiro-cluster":3,"docs-all":770},{"id":4,"title":5,"body":6,"category":752,"description":753,"draft":754,"extension":755,"icon":756,"lastReviewed":757,"meta":758,"navigation":385,"order":227,"path":759,"prerequisites":760,"readingTime":762,"seo":763,"stem":764,"tags":765,"__hash__":769},"docs_pt\u002Fdocs\u002Foperacoes\u002Fprimeiro-cluster.md","Subir cluster de 3 nós",{"type":7,"value":8,"toc":737},"minimark",[9,13,18,21,87,90,100,104,107,187,190,199,203,206,249,252,260,263,267,270,302,310,314,325,432,435,439,442,456,459,465,468,490,501,505,508,564,567,602,613,616,621,628,635,639,645,648,686,690,696,711,715,726,733],[10,11,12],"p",{},"Um nó isolado funciona, mas qualquer reboot derruba a aplicação. Para tolerar falhas e fazer deploy sem janela, você precisa de 3 nós formando um plano de controle distribuído.",[14,15,17],"h2",{"id":16},"por-que-3-nos-e-nao-2-ou-4","Por que 3 nós, e não 2 ou 4?",[10,19,20],{},"A regra é simples: o consenso entre servidores precisa de maioria. Com 3 nós, perder 1 ainda mantém 2 vivos, ou seja, maioria. Com 2, perder 1 deixa 1 vivo, sem maioria, e o cluster trava por segurança.",[22,23,24,40],"table",{},[25,26,27],"thead",{},[28,29,30,34,37],"tr",{},[31,32,33],"th",{},"Nós",[31,35,36],{},"Tolera falha de",[31,38,39],{},"Indicado para",[41,42,43,55,65,76],"tbody",{},[28,44,45,49,52],{},[46,47,48],"td",{},"1",[46,50,51],{},"0",[46,53,54],{},"desenvolvimento, lab",[28,56,57,60,62],{},[46,58,59],{},"3",[46,61,48],{},[46,63,64],{},"produção pequena\u002Fmédia",[28,66,67,70,73],{},[46,68,69],{},"5",[46,71,72],{},"2",[46,74,75],{},"produção crítica",[28,77,78,81,84],{},[46,79,80],{},"4 ou 6",[46,82,83],{},"mesmo que 3 ou 5",[46,85,86],{},"nunca, é desperdício",[10,88,89],{},"Números pares somam custo sem somar resiliência. Sempre ímpar.",[91,92,93],"blockquote",{},[10,94,95,99],{},[96,97,98],"strong",{},"Nota:"," workers (modo agente puro) não contam para essa matemática. Você pode ter 3 servidores + 50 workers e a regra continua sendo \"1 servidor pode cair\".",[14,101,103],{"id":102},"provisionar-os-3-servidores","Provisionar os 3 servidores",[10,105,106],{},"O HeroCtl não exige máquinas iguais, mas servidores devem ter mesmo perfil de CPU e disco para evitar lentidão em quem fica para trás. Um exemplo barato e funcional:",[22,108,109,131],{},[25,110,111],{},[28,112,113,116,119,122,125,128],{},[31,114,115],{},"Provedor",[31,117,118],{},"Plano",[31,120,121],{},"Custo\u002Fmês",[31,123,124],{},"CPU",[31,126,127],{},"RAM",[31,129,130],{},"Disco",[41,132,133,153,171],{},[28,134,135,138,141,144,147,150],{},[46,136,137],{},"Hetzner",[46,139,140],{},"CPX21",[46,142,143],{},"€ 7,99",[46,145,146],{},"3 vCPU",[46,148,149],{},"4 GB",[46,151,152],{},"80 GB",[28,154,155,158,161,164,167,169],{},[46,156,157],{},"DigitalOcean",[46,159,160],{},"s-2vcpu-4gb",[46,162,163],{},"US$ 24",[46,165,166],{},"2 vCPU",[46,168,149],{},[46,170,152],{},[28,172,173,176,179,181,183,185],{},[46,174,175],{},"Vultr",[46,177,178],{},"vc2-2c-4gb",[46,180,163],{},[46,182,166],{},[46,184,149],{},[46,186,152],{},[10,188,189],{},"Provisione 3 máquinas no mesmo datacenter, em rede privada se o provedor oferece. Latência entre nós deve ficar abaixo de 10 ms.",[10,191,192,193,198],{},"Depois siga o passo de ",[194,195,197],"a",{"href":196},"\u002Fdocs\u002Foperacoes\u002Finstalacao","instalação"," em cada um. Quando terminar, você terá 3 servidores com binário pronto e nada mais.",[14,200,202],{"id":201},"inicializar-o-primeiro-no","Inicializar o primeiro nó",[10,204,205],{},"O primeiro nó \"abre\" o cluster. Os outros vão se juntar a ele. Esse comando só roda em uma máquina e nunca mais.",[207,208,213],"pre",{"className":209,"code":210,"language":211,"meta":212,"style":212},"language-bash shiki shiki-themes github-dark-default","# No nó 1, IP privado 10.0.0.1\nsudo heroctl cluster init --advertise 10.0.0.1\n","bash","",[214,215,216,225],"code",{"__ignoreMap":212},[217,218,221],"span",{"class":219,"line":220},"line",1,[217,222,224],{"class":223},"sH3jZ","# No nó 1, IP privado 10.0.0.1\n",[217,226,228,232,236,239,242,246],{"class":219,"line":227},2,[217,229,231],{"class":230},"sQhOw","sudo",[217,233,235],{"class":234},"s9uIt"," heroctl",[217,237,238],{"class":234}," cluster",[217,240,241],{"class":234}," init",[217,243,245],{"class":244},"sFSAA"," --advertise",[217,247,248],{"class":244}," 10.0.0.1\n",[10,250,251],{},"Saída esperada:",[207,253,258],{"className":254,"code":256,"language":257},[255],"language-text","cluster initialized\nnode-id:  node-1\nstate:    healthy\nnodes:    1\u002F1\n","text",[214,259,256],{"__ignoreMap":212},[10,261,262],{},"A partir daqui, o nó 1 já aceita jobs. Mas sem os outros dois, não há tolerância a falha.",[14,264,266],{"id":265},"gerar-token-de-join","Gerar token de join",[10,268,269],{},"Para que outros nós se juntem, você precisa de um token. Ele é assinado pelo cluster e tem validade configurável.",[207,271,273],{"className":209,"code":272,"language":211,"meta":212,"style":212},"# No nó 1\nheroctl cluster join-token --ttl 1h\n# eyJhbGciOi...truncado...8X7Z\n",[214,274,275,280,296],{"__ignoreMap":212},[217,276,277],{"class":219,"line":220},[217,278,279],{"class":223},"# No nó 1\n",[217,281,282,285,287,290,293],{"class":219,"line":227},[217,283,284],{"class":230},"heroctl",[217,286,238],{"class":234},[217,288,289],{"class":234}," join-token",[217,291,292],{"class":244}," --ttl",[217,294,295],{"class":234}," 1h\n",[217,297,299],{"class":219,"line":298},3,[217,300,301],{"class":223},"# eyJhbGciOi...truncado...8X7Z\n",[91,303,304],{},[10,305,306,309],{},[96,307,308],{},"Atenção:"," o token concede entrada no plano de controle. Trate como senha. Use TTL curto (1h é suficiente para juntar 3 nós) e nunca cole em logs ou Slack público.",[14,311,313],{"id":312},"conectar-nos-2-e-3","Conectar nós 2 e 3",[10,315,316,317,320,321,324],{},"Em cada um dos outros dois nós, rode o comando de join. Substitua ",[214,318,319],{},"\u003CTOKEN>"," pelo token gerado e ",[214,322,323],{},"\u003CIP>"," pelo IP privado daquela máquina.",[207,326,328],{"className":209,"code":327,"language":211,"meta":212,"style":212},"# No nó 2, IP privado 10.0.0.2\nsudo heroctl cluster join \\\n  --token eyJhbGciOi...8X7Z \\\n  --advertise 10.0.0.2 \\\n  --servers 10.0.0.1:8080\n\n# No nó 3, IP privado 10.0.0.3\nsudo heroctl cluster join \\\n  --token eyJhbGciOi...8X7Z \\\n  --advertise 10.0.0.3 \\\n  --servers 10.0.0.1:8080\n",[214,329,330,335,350,360,371,380,387,393,406,415,425],{"__ignoreMap":212},[217,331,332],{"class":219,"line":220},[217,333,334],{"class":223},"# No nó 2, IP privado 10.0.0.2\n",[217,336,337,339,341,343,346],{"class":219,"line":227},[217,338,231],{"class":230},[217,340,235],{"class":234},[217,342,238],{"class":234},[217,344,345],{"class":234}," join",[217,347,349],{"class":348},"suJrU"," \\\n",[217,351,352,355,358],{"class":219,"line":298},[217,353,354],{"class":244},"  --token",[217,356,357],{"class":234}," eyJhbGciOi...8X7Z",[217,359,349],{"class":348},[217,361,363,366,369],{"class":219,"line":362},4,[217,364,365],{"class":244},"  --advertise",[217,367,368],{"class":244}," 10.0.0.2",[217,370,349],{"class":348},[217,372,374,377],{"class":219,"line":373},5,[217,375,376],{"class":244},"  --servers",[217,378,379],{"class":234}," 10.0.0.1:8080\n",[217,381,383],{"class":219,"line":382},6,[217,384,386],{"emptyLinePlaceholder":385},true,"\n",[217,388,390],{"class":219,"line":389},7,[217,391,392],{"class":223},"# No nó 3, IP privado 10.0.0.3\n",[217,394,396,398,400,402,404],{"class":219,"line":395},8,[217,397,231],{"class":230},[217,399,235],{"class":234},[217,401,238],{"class":234},[217,403,345],{"class":234},[217,405,349],{"class":348},[217,407,409,411,413],{"class":219,"line":408},9,[217,410,354],{"class":244},[217,412,357],{"class":234},[217,414,349],{"class":348},[217,416,418,420,423],{"class":219,"line":417},10,[217,419,365],{"class":244},[217,421,422],{"class":244}," 10.0.0.3",[217,424,349],{"class":348},[217,426,428,430],{"class":219,"line":427},11,[217,429,376],{"class":244},[217,431,379],{"class":234},[10,433,434],{},"Cada comando demora de 5 a 15 segundos. O nó baixa o estado atual, sincroniza e passa a receber atualizações em tempo real.",[14,436,438],{"id":437},"verificar-saude","Verificar saúde",[10,440,441],{},"Após os 3 estarem conectados, qualquer um responde com a visão do cluster:",[207,443,445],{"className":209,"code":444,"language":211,"meta":212,"style":212},"heroctl cluster status\n",[214,446,447],{"__ignoreMap":212},[217,448,449,451,453],{"class":219,"line":220},[217,450,284],{"class":230},[217,452,238],{"class":234},[217,454,455],{"class":234}," status\n",[10,457,458],{},"Saída saudável:",[207,460,463],{"className":461,"code":462,"language":257},[255],"cluster:  3 nodes\nquorum:   ok (2\u002F3 required)\nleader:   node-1 (10.0.0.1)\npeers:\n  - node-1  10.0.0.1  server  ready  applied=1247\n  - node-2  10.0.0.2  server  ready  applied=1247\n  - node-3  10.0.0.3  server  ready  applied=1247\nlast_update: 0.4s ago\n",[214,464,462],{"__ignoreMap":212},[10,466,467],{},"O que olhar:",[469,470,471,478,484],"ul",{},[472,473,474,477],"li",{},[96,475,476],{},"quorum: ok"," — maioria viva, cluster aceita escritas.",[472,479,480,483],{},[96,481,482],{},"applied"," igual nos 3 nós — todos viram as mesmas mudanças. Diferença pequena (1–2) é normal entre o pulso atual.",[472,485,486,489],{},[96,487,488],{},"last_update"," abaixo de 5s — replicação fluindo.",[10,491,492,493,495,496,500],{},"Se ",[214,494,482],{}," diverge muito (centenas), há problema de rede ou disco em um nó. Veja ",[194,497,499],{"href":498},"#problemas-comuns","Problemas comuns"," abaixo.",[14,502,504],{"id":503},"adicionar-workers","Adicionar workers",[10,506,507],{},"Workers rodam apenas contêineres. Não votam, não decidem, não guardam estado. Adicione quantos quiser sem afetar o consenso.",[207,509,511],{"className":209,"code":510,"language":211,"meta":212,"style":212},"# Em cada worker\nsudo heroctl agent \\\n  --token \u003CTOKEN> \\\n  --advertise 10.0.0.10 \\\n  --servers 10.0.0.1:8080,10.0.0.2:8080,10.0.0.3:8080\n",[214,512,513,518,529,548,557],{"__ignoreMap":212},[217,514,515],{"class":219,"line":220},[217,516,517],{"class":223},"# Em cada worker\n",[217,519,520,522,524,527],{"class":219,"line":227},[217,521,231],{"class":230},[217,523,235],{"class":234},[217,525,526],{"class":234}," agent",[217,528,349],{"class":348},[217,530,531,533,536,539,543,546],{"class":219,"line":298},[217,532,354],{"class":244},[217,534,535],{"class":348}," \u003C",[217,537,538],{"class":234},"TOKE",[217,540,542],{"class":541},"sZEs4","N",[217,544,545],{"class":348},">",[217,547,349],{"class":348},[217,549,550,552,555],{"class":219,"line":362},[217,551,365],{"class":244},[217,553,554],{"class":244}," 10.0.0.10",[217,556,349],{"class":348},[217,558,559,561],{"class":219,"line":373},[217,560,376],{"class":244},[217,562,563],{"class":234}," 10.0.0.1:8080,10.0.0.2:8080,10.0.0.3:8080\n",[10,565,566],{},"Confirme com:",[207,568,570],{"className":209,"code":569,"language":211,"meta":212,"style":212},"heroctl node list\n# node-1   server  ready   3.2 GB free   2 jobs\n# node-2   server  ready   3.4 GB free   1 job\n# node-3   server  ready   3.0 GB free   2 jobs\n# node-10  worker  ready   7.8 GB free   0 jobs\n",[214,571,572,582,587,592,597],{"__ignoreMap":212},[217,573,574,576,579],{"class":219,"line":220},[217,575,284],{"class":230},[217,577,578],{"class":234}," node",[217,580,581],{"class":234}," list\n",[217,583,584],{"class":219,"line":227},[217,585,586],{"class":223},"# node-1   server  ready   3.2 GB free   2 jobs\n",[217,588,589],{"class":219,"line":298},[217,590,591],{"class":223},"# node-2   server  ready   3.4 GB free   1 job\n",[217,593,594],{"class":219,"line":362},[217,595,596],{"class":223},"# node-3   server  ready   3.0 GB free   2 jobs\n",[217,598,599],{"class":219,"line":373},[217,600,601],{"class":223},"# node-10  worker  ready   7.8 GB free   0 jobs\n",[91,603,604],{},[10,605,606,608,609,612],{},[96,607,98],{}," sempre passe os 3 IPs em ",[214,610,611],{},"--servers",". Se você apontar só para o nó 1 e ele cair durante o join, o agente fica em retry.",[14,614,499],{"id":615},"problemas-comuns",[617,618,620],"h3",{"id":619},"no-nao-conecta","Nó não conecta",[10,622,623,624,627],{},"Sintoma: o ",[214,625,626],{},"cluster join"," fica em \"connecting...\" por mais de 30s.",[10,629,630,631,634],{},"Causa típica: firewall bloqueando porta 8082 entre os nós. Confirme com ",[214,632,633],{},"nc -zv \u003Cip-do-nó-1> 8082",". Se falhar, libere a porta no firewall externo do provedor.",[617,636,638],{"id":637},"estado-divergente-entre-nos","Estado divergente entre nós",[10,640,641,642,644],{},"Sintoma: ",[214,643,482],{}," muito diferente entre nós (>100).",[10,646,647],{},"Possível causa: um nó ficou offline e está reaplicando histórico. Espere alguns minutos. Se não converge, force resync no nó atrasado:",[207,649,651],{"className":209,"code":650,"language":211,"meta":212,"style":212},"sudo systemctl restart heroctl\nheroctl node info \u003Cnode-id>\n",[214,652,653,666],{"__ignoreMap":212},[217,654,655,657,660,663],{"class":219,"line":220},[217,656,231],{"class":230},[217,658,659],{"class":234}," systemctl",[217,661,662],{"class":234}," restart",[217,664,665],{"class":234}," heroctl\n",[217,667,668,670,672,675,677,680,683],{"class":219,"line":227},[217,669,284],{"class":230},[217,671,578],{"class":234},[217,673,674],{"class":234}," info",[217,676,535],{"class":348},[217,678,679],{"class":234},"node-i",[217,681,682],{"class":541},"d",[217,684,685],{"class":348},">\n",[617,687,689],{"id":688},"cluster-sem-maioria","Cluster sem maioria",[10,691,641,692,695],{},[214,693,694],{},"quorum: degraded"," ou comandos de escrita travam.",[10,697,698,699,703,704,707,708,710],{},"Significa que 2 de 3 nós estão fora. O cluster recusa escritas para evitar inconsistência. Recupere os nós antes de tentar mudar qualquer coisa. Se um dos servidores está perdido em definitivo, ",[194,700,702],{"href":701},"\u002Fdocs\u002Foperacoes\u002Fcomandos-cli","substitua-o"," com ",[214,705,706],{},"cluster leave"," + novo ",[214,709,626],{},".",[617,712,714],{"id":713},"dois-lideres-aparecem","Dois \"líderes\" aparecem",[10,716,717,718,721,722,725],{},"Não acontece. Se o status de um nó diz que ele é líder e outro também, há problema de relógio. Sincronize com ",[214,719,720],{},"chrony"," ou ",[214,723,724],{},"ntpd"," em todos os nós e reinicie.",[10,727,728,729,710],{},"Próximo passo: ",[194,730,732],{"href":731},"\u002Fdocs\u002Fdeploy\u002Fprimeiro-deploy","fazer deploy do primeiro app",[734,735,736],"style",{},"html pre.shiki code .sH3jZ, html code.shiki .sH3jZ{--shiki-default:#8B949E}html pre.shiki code .sQhOw, html code.shiki .sQhOw{--shiki-default:#FFA657}html pre.shiki code .s9uIt, html code.shiki .s9uIt{--shiki-default:#A5D6FF}html pre.shiki code .sFSAA, html code.shiki .sFSAA{--shiki-default:#79C0FF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .suJrU, html code.shiki .suJrU{--shiki-default:#FF7B72}html pre.shiki code .sZEs4, html code.shiki .sZEs4{--shiki-default:#E6EDF3}",{"title":212,"searchDepth":227,"depth":227,"links":738},[739,740,741,742,743,744,745,746],{"id":16,"depth":227,"text":17},{"id":102,"depth":227,"text":103},{"id":201,"depth":227,"text":202},{"id":265,"depth":227,"text":266},{"id":312,"depth":227,"text":313},{"id":437,"depth":227,"text":438},{"id":503,"depth":227,"text":504},{"id":615,"depth":227,"text":499,"children":747},[748,749,750,751],{"id":619,"depth":298,"text":620},{"id":637,"depth":298,"text":638},{"id":688,"depth":298,"text":689},{"id":713,"depth":298,"text":714},"operacoes","Forme um cluster com 3 servidores em menos de 10 minutos. Tolera falha de 1 nó sem indisponibilidade.",false,"md","i-lucide-network","2026-04-26",{},"\u002Fdocs\u002Foperacoes\u002Fprimeiro-cluster",[761],"instalacao","8 min",{"title":5,"description":753},"docs\u002Foperacoes\u002Fprimeiro-cluster",[766,767,768],"cluster","alta-disponibilidade","primeiros-passos","M6fNP7j4FoFvphLYvUnyRXi6R0t8_QlKA8TSY6_Awjo",[771,777,782,787,793,798,802,806,811,812,818,822,828,833],{"path":772,"title":773,"description":774,"category":775,"order":220,"icon":776},"\u002Fdocs\u002Fapi\u002Freferencia-api","Referência da API REST","Endpoints, autenticação JWT, exemplos com curl e padrões de erro da API do HeroCtl.","api","i-lucide-code",{"path":731,"title":778,"description":779,"category":780,"order":220,"icon":781},"Deploy do primeiro app","Suba uma aplicação Node.js com banco Postgres em 50 linhas de YAML. Inclui health check, rolling deploy e rollback.","deploy","i-lucide-rocket",{"path":783,"title":784,"description":785,"category":780,"order":227,"icon":786},"\u002Fdocs\u002Fdeploy\u002Frolling-canary-bluegreen","Rolling, canary, blue-green e rainbow","Quatro estratégias de deploy. Quando usar cada uma, com exemplos completos e trade-offs honestos.","i-lucide-git-branch",{"path":788,"title":789,"description":790,"category":791,"order":227,"icon":792},"\u002Fdocs\u002Fobservabilidade\u002Fbackup-restore","Backup e restore do estado do cluster","Como salvar, agendar e restaurar snapshots do plano de controle do HeroCtl. Estratégia de disaster recovery.","observabilidade","i-lucide-archive",{"path":794,"title":795,"description":796,"category":791,"order":220,"icon":797},"\u002Fdocs\u002Fobservabilidade\u002Fmetricas-logs","Métricas e logs","Coleta de métricas, logs e traces sem montar uma pilha de observabilidade externa. Quando vale, e quando integrar com ferramenta de fora.","i-lucide-activity",{"path":701,"title":799,"description":800,"category":752,"order":298,"icon":801},"Referência completa do CLI","Todos os comandos heroctl com sinopse, flags e exemplo. Use como cola de mesa.","i-lucide-terminal",{"path":196,"title":803,"description":804,"category":752,"order":220,"icon":805},"Instalação","Instale o HeroCtl em qualquer servidor Linux com Docker em um único comando. Cobre pré-requisitos, bootstrap e verificação.","i-lucide-download",{"path":807,"title":808,"description":809,"category":752,"order":362,"icon":810},"\u002Fdocs\u002Foperacoes\u002Fmulti-region","Multi-region (em planejamento Q4 2026)","O que esperar de multi-region no HeroCtl, como rodar em várias regiões hoje e o roadmap até 2027.","i-lucide-globe",{"path":759,"title":5,"description":753,"category":752,"order":227,"icon":756},{"path":813,"title":814,"description":815,"category":816,"order":227,"icon":817},"\u002Fdocs\u002Frede\u002Ffirewall","Configuração de firewall","Quais portas o HeroCtl usa, quais precisam ficar abertas, e quais nunca deveriam ser expostas à internet.","rede","i-lucide-shield",{"path":819,"title":820,"description":821,"category":816,"order":220,"icon":810},"\u002Fdocs\u002Frede\u002Fingress-tls","Ingress e TLS automático","Como expor aplicações pela porta 443 com certificados emitidos e renovados automaticamente, sem operar um roteador externo.",{"path":823,"title":824,"description":825,"category":826,"order":227,"icon":827},"\u002Fdocs\u002Fseguranca\u002Frbac","RBAC e controle de acesso (Business+)","Modelo de papéis, políticas e tokens para limitar quem pode submeter, ler e operar o cluster.","seguranca","i-lucide-users",{"path":829,"title":830,"description":831,"category":826,"order":220,"icon":832},"\u002Fdocs\u002Fseguranca\u002Fsecrets","Gerenciamento de segredos","Como guardar senhas, tokens e chaves fora do spec do job, com criptografia em repouso e rotação versionada.","i-lucide-key",{"path":834,"title":835,"description":836,"category":837,"order":220,"icon":838},"\u002Fdocs\u002Ftroubleshooting\u002Fproblemas-comuns","Troubleshooting de problemas comuns","Os 12 problemas mais frequentes em clusters HeroCtl, com sintoma, diagnóstico e correção passo a passo.","troubleshooting","i-lucide-alert-triangle",1777362178314]