[{"data":1,"prerenderedAt":12757},["ShallowReactive",2],{"home-recent-posts-pt-BR":3},[4,3395,4411,5381,6399,7516,8779,11792],{"id":5,"title":6,"author":7,"body":8,"category":3379,"cover":3380,"date":3381,"description":3382,"draft":3383,"extension":3384,"lastReviewed":3380,"meta":3385,"navigation":411,"path":3386,"readingTime":3387,"seo":3388,"sitemap":3389,"stem":3390,"tags":3391,"__hash__":3394},"blog_pt\u002Fblog\u002Fdeploy-zero-downtime-sem-kubernetes-tutorial.md","Deploy zero-downtime sem Kubernetes: tutorial prático em 2026","Equipe HeroCtl",{"type":9,"value":10,"toc":3354},"minimark",[11,15,18,23,39,42,56,59,63,66,88,91,95,102,105,108,111,115,118,205,208,211,213,217,220,223,275,286,289,314,317,339,346,350,357,367,372,916,920,1000,1004,1080,1094,1097,1101,1104,1140,1143,1208,1211,1236,1239,1243,1246,1249,1252,1266,1273,1363,1369,1372,1434,1440,1457,1460,1464,1467,2346,2357,2383,2386,2444,2447,2451,2454,2457,2506,2509,2520,2525,2533,2536,2538,2542,2545,2607,2611,2618,2624,2638,2710,2713,2727,2731,2734,2755,2758,2762,2765,2783,2786,2790,2793,2941,2944,2947,2968,2971,2975,3168,3171,3175,3223,3227,3232,3239,3244,3247,3252,3255,3260,3266,3271,3274,3279,3285,3290,3293,3298,3305,3307,3311,3314,3317,3333,3347,3350],[12,13,14],"p",{},"Tem um mito persistente que zero-downtime deploy é exclusividade de quem rodou Kubernetes em produção. Não é. A técnica existe desde antes do colosso ter nome — qualquer time que rodou um par de servidores físicos atrás de um balanceador na década passada já fazia isso, com scripts de cinquenta linhas e nenhum CRD na vida. O que mudou foi o marketing em volta da prática, não a prática em si.",[12,16,17],{},"Esse post é um tutorial passo a passo pra montar deploy sem downtime do zero, em duas máquinas Linux, sem orquestrador pesado, sem painel mágico. No fim você vai ter um script de bash que troca uma instância por vez, espera a nova ficar saudável, e rola pra próxima — exatamente o algoritmo que orquestradores grandes implementam, só que sem o boilerplate.",[19,20,22],"h2",{"id":21},"tldr","TL;DR",[12,24,25,26,30,31,34,35,38],{},"Zero-downtime deploy depende de três ingredientes, não de uma ferramenta específica. Primeiro: ",[27,28,29],"strong",{},"duas ou mais instâncias da aplicação rodando em paralelo",", atrás de um proxy básico. Segundo: ",[27,32,33],{},"um endpoint de health check confiável"," que valida dependências reais (banco, cache, fila), não só responde 200 instantaneamente. Terceiro: ",[27,36,37],{},"um script ou orquestrador que substitua um contêiner por vez",", esperando o novo ficar saudável antes de prosseguir pro próximo.",[12,40,41],{},"Esse tutorial monta o setup completo em duas VPS Linux com Docker, Caddy na frente como proxy + balanceador, e um script bash de cinquenta linhas que faz rolling update com health check ativo, tempo mínimo saudável, e rollback automático se falhar. Resultado: deploy sem 5xx visível pro usuário, em menos de um minuto, sem janela de manutenção.",[12,43,44,47,48,51,52,55],{},[27,45,46],{},"Pré-requisitos:"," duas VPS Linux com Docker (Hetzner CPX11 a R$30 cada), domínio com DNS controlável, app com health check decente. ",[27,49,50],{},"Tempo de setup:"," duas a três horas. ",[27,53,54],{},"Custo mensal:"," R$60 (R$75 se você quiser uma terceira VPS dedicada ao proxy). No fim mostramos a versão \"robusta\" via HeroCtl pra quem quer parar de scriptar.",[57,58],"hr",{},[19,60,62],{"id":61},"os-tres-ingredientes-sem-isso-nao-e-zero-downtime","Os três ingredientes (sem isso, não é zero-downtime)",[12,64,65],{},"Antes de qualquer comando, vale fixar a teoria — porque toda configuração mais elaborada que você vai ver na internet é variação dessas três peças.",[67,68,69,76,82],"ol",{},[70,71,72,75],"li",{},[27,73,74],{},"Múltiplas instâncias da app rodando em paralelo."," Mínimo dois. Se você só tem uma, qualquer reinício é janela de erro. Não tem como contornar isso com truque de configuração.",[70,77,78,81],{},[27,79,80],{},"Um proxy\u002Fbalanceador na frente, fazendo health check."," O proxy decide pra qual instância mandar tráfego. Se uma cai (ou foi tirada deliberadamente pro deploy), o proxy só manda pras restantes.",[70,83,84,87],{},[27,85,86],{},"Um script que troca instâncias uma por vez."," Nunca todas juntas. Espera a nova ficar saudável antes de mexer na próxima. Se a nova falha, para o deploy e mantém as antigas servindo.",[12,89,90],{},"É só isso. O resto — Kubernetes, painéis modernos, orquestradores leves — é embalagem em volta desses três pontos.",[19,92,94],{"id":93},"por-que-single-server-nunca-e-zero-downtime-mesmo-se-for-rapido","Por que single-server NUNCA é zero-downtime (mesmo se for rápido)",[12,96,97,98,101],{},"Vejo essa pergunta toda semana no Discord da comunidade: \"consigo zero-downtime com um servidor só, se o deploy for rápido o suficiente?\". Resposta curta: ",[27,99,100],{},"não",".",[12,103,104],{},"Numa máquina única, o ciclo de deploy é: para o contêiner antigo, sobe o novo. Mesmo que tudo aconteça em três segundos, esses três segundos existem. Conexões TCP em curso são cortadas. Requisições que chegam nesse intervalo levam connection refused ou 502. Se você tem cinco requests por segundo, são quinze usuários vendo erro a cada deploy.",[12,106,107],{},"Tem variação esperta — subir o novo numa porta diferente, mudar o proxy local, derrubar o antigo. Isso melhora, mas não elimina. Se a app demora pra fechar conexões em curso, o cutover ainda gera erros. Se health check é fraco, o proxy aponta tráfego pra app que ainda não terminou de subir. Sempre tem uma janela.",[12,109,110],{},"A única forma confiável de eliminar a janela é ter pelo menos uma instância sempre disponível durante todo o deploy. Isso exige duas máquinas. Ponto.",[19,112,114],{"id":113},"o-setup-minimo-duas-vps-um-proxy","O setup mínimo (duas VPS + um proxy)",[12,116,117],{},"A topologia mais barata que entrega zero-downtime real:",[119,120,121,140],"table",{},[122,123,124],"thead",{},[125,126,127,131,134,137],"tr",{},[128,129,130],"th",{},"Componente",[128,132,133],{},"Tamanho",[128,135,136],{},"Custo",[128,138,139],{},"Função",[141,142,143,158,170,189],"tbody",{},[125,144,145,149,152,155],{},[146,147,148],"td",{},"VPS A",[146,150,151],{},"2 vCPU \u002F 2 GB RAM",[146,153,154],{},"R$30\u002Fmês",[146,156,157],{},"App instância 1",[125,159,160,163,165,167],{},[146,161,162],{},"VPS B",[146,164,151],{},[146,166,154],{},[146,168,169],{},"App instância 2",[125,171,172,175,183,186],{},[146,173,174],{},"Proxy",[146,176,177,178,182],{},"rodando em VPS A ",[179,180,181],"em",{},"ou"," terceira VPS",[146,184,185],{},"R$0 (compartilhado) ou R$15\u002Fmês",[146,187,188],{},"Caddy\u002Fnginx fazendo balance",[125,190,191,194,199,202],{},[146,192,193],{},"Banco",[146,195,196,197,182],{},"Postgres gerenciado ",[179,198,181],{},[146,200,201],{},"varia",[146,203,204],{},"Estado compartilhado entre A e B",[12,206,207],{},"Ter o proxy compartilhado em uma das próprias VPS economiza, mas tem trade-off: se a VPS que hospeda o proxy cair inteira, o site cai junto (mesmo com a outra VPS rodando). Pra time pequeno isso é aceitável. Quando crescer, o proxy migra pra VPS dedicada ou vira par redundante.",[12,209,210],{},"DNS A record do seu domínio aponta pro IP do proxy. Apps em A e B se conectam ao mesmo banco — sem essa parte compartilhada, as duas instâncias divergem e o usuário vê resultado diferente dependendo de qual respondeu.",[57,212],{},[19,214,216],{"id":215},"passo-1-provisionar-duas-vps-15-min","Passo 1 — Provisionar duas VPS (15 min)",[12,218,219],{},"Eu uso Hetzner CPX11 (€4,75 ≈ R$30) como referência. DigitalOcean Droplet de US$6, Vultr Cloud Compute de US$6 ou Linode Nanode de US$5 entregam algo parecido. O importante é Linux moderno (Ubuntu 24.04 LTS ou Debian 12) com Docker.",[12,221,222],{},"Provisione as duas máquinas com a mesma chave SSH:",[224,225,230],"pre",{"className":226,"code":227,"language":228,"meta":229,"style":229},"language-bash shiki shiki-themes github-dark-default","# do seu laptop\nssh-keygen -t ed25519 -f ~\u002F.ssh\u002Fdeploy_key -C \"deploy@meudominio.com\"\n# adicione ~\u002F.ssh\u002Fdeploy_key.pub na console do provedor antes de criar a VPS\n","bash","",[231,232,233,242,269],"code",{"__ignoreMap":229},[234,235,238],"span",{"class":236,"line":237},"line",1,[234,239,241],{"class":240},"sH3jZ","# do seu laptop\n",[234,243,245,249,253,257,260,263,266],{"class":236,"line":244},2,[234,246,248],{"class":247},"sQhOw","ssh-keygen",[234,250,252],{"class":251},"sFSAA"," -t",[234,254,256],{"class":255},"s9uIt"," ed25519",[234,258,259],{"class":251}," -f",[234,261,262],{"class":255}," ~\u002F.ssh\u002Fdeploy_key",[234,264,265],{"class":251}," -C",[234,267,268],{"class":255}," \"deploy@meudominio.com\"\n",[234,270,272],{"class":236,"line":271},3,[234,273,274],{"class":240},"# adicione ~\u002F.ssh\u002Fdeploy_key.pub na console do provedor antes de criar a VPS\n",[12,276,277,278,281,282,285],{},"Crie cada VPS, anote os IPs. Vou usar ",[231,279,280],{},"203.0.113.10"," (VPS A) e ",[231,283,284],{},"203.0.113.20"," (VPS B) como placeholders no resto do post.",[12,287,288],{},"Instale Docker em cada uma:",[224,290,292],{"className":226,"code":291,"language":228,"meta":229,"style":229},"ssh root@203.0.113.10 \"curl -fsSL https:\u002F\u002Fget.docker.com | sh\"\nssh root@203.0.113.20 \"curl -fsSL https:\u002F\u002Fget.docker.com | sh\"\n",[231,293,294,305],{"__ignoreMap":229},[234,295,296,299,302],{"class":236,"line":237},[234,297,298],{"class":247},"ssh",[234,300,301],{"class":255}," root@203.0.113.10",[234,303,304],{"class":255}," \"curl -fsSL https:\u002F\u002Fget.docker.com | sh\"\n",[234,306,307,309,312],{"class":236,"line":244},[234,308,298],{"class":247},[234,310,311],{"class":255}," root@203.0.113.20",[234,313,304],{"class":255},[12,315,316],{},"Configure firewall pra permitir só 22 (SSH) e 8080 (porta interna onde a app vai escutar). Tráfego HTTP\u002FHTTPS chega só no proxy:",[224,318,320],{"className":226,"code":319,"language":228,"meta":229,"style":229},"ssh root@203.0.113.10 \"ufw allow 22 && ufw allow 8080\u002Ftcp && ufw --force enable\"\nssh root@203.0.113.20 \"ufw allow 22 && ufw allow 8080\u002Ftcp && ufw --force enable\"\n",[231,321,322,331],{"__ignoreMap":229},[234,323,324,326,328],{"class":236,"line":237},[234,325,298],{"class":247},[234,327,301],{"class":255},[234,329,330],{"class":255}," \"ufw allow 22 && ufw allow 8080\u002Ftcp && ufw --force enable\"\n",[234,332,333,335,337],{"class":236,"line":244},[234,334,298],{"class":247},[234,336,311],{"class":255},[234,338,330],{"class":255},[12,340,341,342,345],{},"Validação: ",[231,343,344],{},"docker run --rm hello-world"," em cada máquina deve completar sem erro.",[19,347,349],{"id":348},"passo-2-app-com-health-check-decente-30-min","Passo 2 — App com health check decente (30 min)",[12,351,352,353,356],{},"O endpoint ",[231,354,355],{},"\u002Fhealthz"," é o coração do esquema. Se ele retorna 200 quando a app não está realmente pronta, o proxy manda tráfego pra instância quebrada e o usuário vê erro. Se ele retorna 500 quando a app está saudável, o proxy tira a instância boa do balanceamento. Ou seja: o health check é a fonte de verdade do sistema inteiro.",[12,358,359,360,362,363,366],{},"Regra de ouro: o ",[231,361,355],{}," valida ",[27,364,365],{},"dependências reais que a app precisa pra responder",". Mínimo: conexão com o banco. Se você tem cache (Redis), inclui. Se você tem fila (SQS, RabbitMQ), inclui. NÃO retorne 200 logo no boot — espere assets compilarem, cache aquecer, conexões abrirem.",[368,369,371],"h3",{"id":370},"nodejs-express","Node.js (Express)",[224,373,377],{"className":374,"code":375,"language":376,"meta":229,"style":229},"language-js shiki shiki-themes github-dark-default","import express from \"express\"\nimport { Pool } from \"pg\"\n\nconst app = express()\nconst pool = new Pool({ connectionString: process.env.DATABASE_URL })\n\nlet ready = false\n\n\u002F\u002F warm-up assíncrono — só fica ready quando dependencies validam\n;(async () => {\n  await pool.query(\"SELECT 1\")\n  \u002F\u002F outras inicializações: cache prime, etc.\n  ready = true\n})()\n\napp.get(\"\u002Fhealthz\", async (_req, res) => {\n  if (!ready) return res.status(503).send(\"warming up\")\n  try {\n    await pool.query(\"SELECT 1\")\n    res.status(200).send(\"ok\")\n  } catch (e) {\n    res.status(503).send(\"db down\")\n  }\n})\n\napp.get(\"\u002F\", (_req, res) => res.send(\"Hello v1\"))\n\nconst server = app.listen(8080, () => console.log(\"listening 8080\"))\n\n\u002F\u002F graceful shutdown — drena conexões antes de morrer\nprocess.on(\"SIGTERM\", () => {\n  ready = false  \u002F\u002F health check passa a falhar imediatamente\n  setTimeout(() => {\n    server.close(() => process.exit(0))\n  }, 5000)  \u002F\u002F 5s pro proxy notar e parar de mandar tráfego novo\n})\n","js",[231,378,379,395,407,413,432,457,462,477,482,488,506,527,533,544,550,555,592,633,641,657,681,693,715,721,727,732,769,774,813,818,824,844,857,870,896,911],{"__ignoreMap":229},[234,380,381,385,389,392],{"class":236,"line":237},[234,382,384],{"class":383},"suJrU","import",[234,386,388],{"class":387},"sZEs4"," express ",[234,390,391],{"class":383},"from",[234,393,394],{"class":255}," \"express\"\n",[234,396,397,399,402,404],{"class":236,"line":244},[234,398,384],{"class":383},[234,400,401],{"class":387}," { Pool } ",[234,403,391],{"class":383},[234,405,406],{"class":255}," \"pg\"\n",[234,408,409],{"class":236,"line":271},[234,410,412],{"emptyLinePlaceholder":411},true,"\n",[234,414,416,419,422,425,429],{"class":236,"line":415},4,[234,417,418],{"class":383},"const",[234,420,421],{"class":251}," app",[234,423,424],{"class":383}," =",[234,426,428],{"class":427},"sc3cj"," express",[234,430,431],{"class":387},"()\n",[234,433,435,437,440,442,445,448,451,454],{"class":236,"line":434},5,[234,436,418],{"class":383},[234,438,439],{"class":251}," pool",[234,441,424],{"class":383},[234,443,444],{"class":383}," new",[234,446,447],{"class":427}," Pool",[234,449,450],{"class":387},"({ connectionString: process.env.",[234,452,453],{"class":251},"DATABASE_URL",[234,455,456],{"class":387}," })\n",[234,458,460],{"class":236,"line":459},6,[234,461,412],{"emptyLinePlaceholder":411},[234,463,465,468,471,474],{"class":236,"line":464},7,[234,466,467],{"class":383},"let",[234,469,470],{"class":387}," ready ",[234,472,473],{"class":383},"=",[234,475,476],{"class":251}," false\n",[234,478,480],{"class":236,"line":479},8,[234,481,412],{"emptyLinePlaceholder":411},[234,483,485],{"class":236,"line":484},9,[234,486,487],{"class":240},"\u002F\u002F warm-up assíncrono — só fica ready quando dependencies validam\n",[234,489,491,494,497,500,503],{"class":236,"line":490},10,[234,492,493],{"class":387},";(",[234,495,496],{"class":383},"async",[234,498,499],{"class":387}," () ",[234,501,502],{"class":383},"=>",[234,504,505],{"class":387}," {\n",[234,507,509,512,515,518,521,524],{"class":236,"line":508},11,[234,510,511],{"class":383},"  await",[234,513,514],{"class":387}," pool.",[234,516,517],{"class":427},"query",[234,519,520],{"class":387},"(",[234,522,523],{"class":255},"\"SELECT 1\"",[234,525,526],{"class":387},")\n",[234,528,530],{"class":236,"line":529},12,[234,531,532],{"class":240},"  \u002F\u002F outras inicializações: cache prime, etc.\n",[234,534,536,539,541],{"class":236,"line":535},13,[234,537,538],{"class":387},"  ready ",[234,540,473],{"class":383},[234,542,543],{"class":251}," true\n",[234,545,547],{"class":236,"line":546},14,[234,548,549],{"class":387},"})()\n",[234,551,553],{"class":236,"line":552},15,[234,554,412],{"emptyLinePlaceholder":411},[234,556,558,561,564,566,569,572,574,577,580,582,585,588,590],{"class":236,"line":557},16,[234,559,560],{"class":387},"app.",[234,562,563],{"class":427},"get",[234,565,520],{"class":387},[234,567,568],{"class":255},"\"\u002Fhealthz\"",[234,570,571],{"class":387},", ",[234,573,496],{"class":383},[234,575,576],{"class":387}," (",[234,578,579],{"class":247},"_req",[234,581,571],{"class":387},[234,583,584],{"class":247},"res",[234,586,587],{"class":387},") ",[234,589,502],{"class":383},[234,591,505],{"class":387},[234,593,595,598,600,603,606,609,612,615,617,620,623,626,628,631],{"class":236,"line":594},17,[234,596,597],{"class":383},"  if",[234,599,576],{"class":387},[234,601,602],{"class":383},"!",[234,604,605],{"class":387},"ready) ",[234,607,608],{"class":383},"return",[234,610,611],{"class":387}," res.",[234,613,614],{"class":427},"status",[234,616,520],{"class":387},[234,618,619],{"class":251},"503",[234,621,622],{"class":387},").",[234,624,625],{"class":427},"send",[234,627,520],{"class":387},[234,629,630],{"class":255},"\"warming up\"",[234,632,526],{"class":387},[234,634,636,639],{"class":236,"line":635},18,[234,637,638],{"class":383},"  try",[234,640,505],{"class":387},[234,642,644,647,649,651,653,655],{"class":236,"line":643},19,[234,645,646],{"class":383},"    await",[234,648,514],{"class":387},[234,650,517],{"class":427},[234,652,520],{"class":387},[234,654,523],{"class":255},[234,656,526],{"class":387},[234,658,660,663,665,667,670,672,674,676,679],{"class":236,"line":659},20,[234,661,662],{"class":387},"    res.",[234,664,614],{"class":427},[234,666,520],{"class":387},[234,668,669],{"class":251},"200",[234,671,622],{"class":387},[234,673,625],{"class":427},[234,675,520],{"class":387},[234,677,678],{"class":255},"\"ok\"",[234,680,526],{"class":387},[234,682,684,687,690],{"class":236,"line":683},21,[234,685,686],{"class":387},"  } ",[234,688,689],{"class":383},"catch",[234,691,692],{"class":387}," (e) {\n",[234,694,696,698,700,702,704,706,708,710,713],{"class":236,"line":695},22,[234,697,662],{"class":387},[234,699,614],{"class":427},[234,701,520],{"class":387},[234,703,619],{"class":251},[234,705,622],{"class":387},[234,707,625],{"class":427},[234,709,520],{"class":387},[234,711,712],{"class":255},"\"db down\"",[234,714,526],{"class":387},[234,716,718],{"class":236,"line":717},23,[234,719,720],{"class":387},"  }\n",[234,722,724],{"class":236,"line":723},24,[234,725,726],{"class":387},"})\n",[234,728,730],{"class":236,"line":729},25,[234,731,412],{"emptyLinePlaceholder":411},[234,733,735,737,739,741,744,747,749,751,753,755,757,759,761,763,766],{"class":236,"line":734},26,[234,736,560],{"class":387},[234,738,563],{"class":427},[234,740,520],{"class":387},[234,742,743],{"class":255},"\"\u002F\"",[234,745,746],{"class":387},", (",[234,748,579],{"class":247},[234,750,571],{"class":387},[234,752,584],{"class":247},[234,754,587],{"class":387},[234,756,502],{"class":383},[234,758,611],{"class":387},[234,760,625],{"class":427},[234,762,520],{"class":387},[234,764,765],{"class":255},"\"Hello v1\"",[234,767,768],{"class":387},"))\n",[234,770,772],{"class":236,"line":771},27,[234,773,412],{"emptyLinePlaceholder":411},[234,775,777,779,782,784,787,790,792,795,798,800,803,806,808,811],{"class":236,"line":776},28,[234,778,418],{"class":383},[234,780,781],{"class":251}," server",[234,783,424],{"class":383},[234,785,786],{"class":387}," app.",[234,788,789],{"class":427},"listen",[234,791,520],{"class":387},[234,793,794],{"class":251},"8080",[234,796,797],{"class":387},", () ",[234,799,502],{"class":383},[234,801,802],{"class":387}," console.",[234,804,805],{"class":427},"log",[234,807,520],{"class":387},[234,809,810],{"class":255},"\"listening 8080\"",[234,812,768],{"class":387},[234,814,816],{"class":236,"line":815},29,[234,817,412],{"emptyLinePlaceholder":411},[234,819,821],{"class":236,"line":820},30,[234,822,823],{"class":240},"\u002F\u002F graceful shutdown — drena conexões antes de morrer\n",[234,825,827,830,833,835,838,840,842],{"class":236,"line":826},31,[234,828,829],{"class":387},"process.",[234,831,832],{"class":427},"on",[234,834,520],{"class":387},[234,836,837],{"class":255},"\"SIGTERM\"",[234,839,797],{"class":387},[234,841,502],{"class":383},[234,843,505],{"class":387},[234,845,847,849,851,854],{"class":236,"line":846},32,[234,848,538],{"class":387},[234,850,473],{"class":383},[234,852,853],{"class":251}," false",[234,855,856],{"class":240},"  \u002F\u002F health check passa a falhar imediatamente\n",[234,858,860,863,866,868],{"class":236,"line":859},33,[234,861,862],{"class":427},"  setTimeout",[234,864,865],{"class":387},"(() ",[234,867,502],{"class":383},[234,869,505],{"class":387},[234,871,873,876,879,881,883,886,889,891,894],{"class":236,"line":872},34,[234,874,875],{"class":387},"    server.",[234,877,878],{"class":427},"close",[234,880,865],{"class":387},[234,882,502],{"class":383},[234,884,885],{"class":387}," process.",[234,887,888],{"class":427},"exit",[234,890,520],{"class":387},[234,892,893],{"class":251},"0",[234,895,768],{"class":387},[234,897,899,902,905,908],{"class":236,"line":898},35,[234,900,901],{"class":387},"  }, ",[234,903,904],{"class":251},"5000",[234,906,907],{"class":387},")  ",[234,909,910],{"class":240},"\u002F\u002F 5s pro proxy notar e parar de mandar tráfego novo\n",[234,912,914],{"class":236,"line":913},36,[234,915,726],{"class":387},[368,917,919],{"id":918},"python-django-gunicorn","Python (Django + gunicorn)",[224,921,925],{"className":922,"code":923,"language":924,"meta":229,"style":229},"language-python shiki shiki-themes github-dark-default","# health\u002Fviews.py\nfrom django.db import connection\nfrom django.http import JsonResponse, HttpResponse\nimport redis, os\n\n_r = redis.from_url(os.environ[\"REDIS_URL\"])\n\ndef healthz(request):\n    try:\n        with connection.cursor() as c:\n            c.execute(\"SELECT 1\")\n        _r.ping()\n        return HttpResponse(\"ok\", status=200)\n    except Exception as e:\n        return HttpResponse(f\"unhealthy: {e}\", status=503)\n","python",[231,926,927,932,937,942,947,951,956,960,965,970,975,980,985,990,995],{"__ignoreMap":229},[234,928,929],{"class":236,"line":237},[234,930,931],{},"# health\u002Fviews.py\n",[234,933,934],{"class":236,"line":244},[234,935,936],{},"from django.db import connection\n",[234,938,939],{"class":236,"line":271},[234,940,941],{},"from django.http import JsonResponse, HttpResponse\n",[234,943,944],{"class":236,"line":415},[234,945,946],{},"import redis, os\n",[234,948,949],{"class":236,"line":434},[234,950,412],{"emptyLinePlaceholder":411},[234,952,953],{"class":236,"line":459},[234,954,955],{},"_r = redis.from_url(os.environ[\"REDIS_URL\"])\n",[234,957,958],{"class":236,"line":464},[234,959,412],{"emptyLinePlaceholder":411},[234,961,962],{"class":236,"line":479},[234,963,964],{},"def healthz(request):\n",[234,966,967],{"class":236,"line":484},[234,968,969],{},"    try:\n",[234,971,972],{"class":236,"line":490},[234,973,974],{},"        with connection.cursor() as c:\n",[234,976,977],{"class":236,"line":508},[234,978,979],{},"            c.execute(\"SELECT 1\")\n",[234,981,982],{"class":236,"line":529},[234,983,984],{},"        _r.ping()\n",[234,986,987],{"class":236,"line":535},[234,988,989],{},"        return HttpResponse(\"ok\", status=200)\n",[234,991,992],{"class":236,"line":546},[234,993,994],{},"    except Exception as e:\n",[234,996,997],{"class":236,"line":552},[234,998,999],{},"        return HttpResponse(f\"unhealthy: {e}\", status=503)\n",[368,1001,1003],{"id":1002},"ruby-rails","Ruby (Rails)",[224,1005,1009],{"className":1006,"code":1007,"language":1008,"meta":229,"style":229},"language-ruby shiki shiki-themes github-dark-default","# config\u002Froutes.rb\nget \"\u002Fhealthz\", to: \"health#show\"\n\n# app\u002Fcontrollers\u002Fhealth_controller.rb\nclass HealthController \u003C ApplicationController\n  def show\n    ActiveRecord::Base.connection.execute(\"SELECT 1\")\n    Rails.cache.read(\"__healthcheck__\")\n    head :ok\n  rescue => e\n    Rails.logger.warn(\"healthcheck failed: #{e.message}\")\n    head :service_unavailable\n  end\nend\n","ruby",[231,1010,1011,1016,1021,1025,1030,1035,1040,1045,1050,1055,1060,1065,1070,1075],{"__ignoreMap":229},[234,1012,1013],{"class":236,"line":237},[234,1014,1015],{},"# config\u002Froutes.rb\n",[234,1017,1018],{"class":236,"line":244},[234,1019,1020],{},"get \"\u002Fhealthz\", to: \"health#show\"\n",[234,1022,1023],{"class":236,"line":271},[234,1024,412],{"emptyLinePlaceholder":411},[234,1026,1027],{"class":236,"line":415},[234,1028,1029],{},"# app\u002Fcontrollers\u002Fhealth_controller.rb\n",[234,1031,1032],{"class":236,"line":434},[234,1033,1034],{},"class HealthController \u003C ApplicationController\n",[234,1036,1037],{"class":236,"line":459},[234,1038,1039],{},"  def show\n",[234,1041,1042],{"class":236,"line":464},[234,1043,1044],{},"    ActiveRecord::Base.connection.execute(\"SELECT 1\")\n",[234,1046,1047],{"class":236,"line":479},[234,1048,1049],{},"    Rails.cache.read(\"__healthcheck__\")\n",[234,1051,1052],{"class":236,"line":484},[234,1053,1054],{},"    head :ok\n",[234,1056,1057],{"class":236,"line":490},[234,1058,1059],{},"  rescue => e\n",[234,1061,1062],{"class":236,"line":508},[234,1063,1064],{},"    Rails.logger.warn(\"healthcheck failed: #{e.message}\")\n",[234,1066,1067],{"class":236,"line":529},[234,1068,1069],{},"    head :service_unavailable\n",[234,1071,1072],{"class":236,"line":535},[234,1073,1074],{},"  end\n",[234,1076,1077],{"class":236,"line":546},[234,1078,1079],{},"end\n",[12,1081,1082,1083,1086,1087,1090,1091,1093],{},"O detalhe que diferencia health check amador de profissional é o ",[27,1084,1085],{},"graceful shutdown",": ao receber ",[231,1088,1089],{},"SIGTERM",", a app passa a retornar 503 no ",[231,1092,355],{}," imediatamente, mas continua aceitando conexões em curso por mais alguns segundos. O proxy nota o 503, para de mandar tráfego novo, e quando o app finalmente fecha não tem mais ninguém esperando resposta.",[12,1095,1096],{},"Sem isso, o cutover sempre vaza alguns erros mesmo com tudo o resto certo.",[19,1098,1100],{"id":1099},"passo-3-subir-duas-instancias-docker-15-min","Passo 3 — Subir duas instâncias Docker (15 min)",[12,1102,1103],{},"Faça build da sua app em imagem Docker. Pro tutorial vou usar uma imagem genérica que você substitui:",[224,1105,1107],{"className":226,"code":1106,"language":228,"meta":229,"style":229},"# no seu laptop, push pra registry (Docker Hub, ECR, GHCR)\ndocker build -t meuusuario\u002Fmyapp:v1 .\ndocker push meuusuario\u002Fmyapp:v1\n",[231,1108,1109,1114,1130],{"__ignoreMap":229},[234,1110,1111],{"class":236,"line":237},[234,1112,1113],{"class":240},"# no seu laptop, push pra registry (Docker Hub, ECR, GHCR)\n",[234,1115,1116,1119,1122,1124,1127],{"class":236,"line":244},[234,1117,1118],{"class":247},"docker",[234,1120,1121],{"class":255}," build",[234,1123,252],{"class":251},[234,1125,1126],{"class":255}," meuusuario\u002Fmyapp:v1",[234,1128,1129],{"class":255}," .\n",[234,1131,1132,1134,1137],{"class":236,"line":271},[234,1133,1118],{"class":247},[234,1135,1136],{"class":255}," push",[234,1138,1139],{"class":255}," meuusuario\u002Fmyapp:v1\n",[12,1141,1142],{},"Sobe instância em VPS A:",[224,1144,1146],{"className":226,"code":1145,"language":228,"meta":229,"style":229},"ssh root@203.0.113.10 \"\n  docker pull meuusuario\u002Fmyapp:v1 &&\n  docker run -d --name app --restart=unless-stopped \\\n    -p 8080:8080 \\\n    -e DATABASE_URL='postgres:\u002F\u002Fuser:pass@db.example.com:5432\u002Fapp' \\\n    --health-cmd='curl -f http:\u002F\u002Flocalhost:8080\u002Fhealthz || exit 1' \\\n    --health-interval=5s --health-timeout=2s --health-retries=3 \\\n    meuusuario\u002Fmyapp:v1\n\"\n",[231,1147,1148,1157,1162,1170,1177,1184,1191,1198,1203],{"__ignoreMap":229},[234,1149,1150,1152,1154],{"class":236,"line":237},[234,1151,298],{"class":247},[234,1153,301],{"class":255},[234,1155,1156],{"class":255}," \"\n",[234,1158,1159],{"class":236,"line":244},[234,1160,1161],{"class":255},"  docker pull meuusuario\u002Fmyapp:v1 &&\n",[234,1163,1164,1167],{"class":236,"line":271},[234,1165,1166],{"class":255},"  docker run -d --name app --restart=unless-stopped ",[234,1168,1169],{"class":383},"\\\n",[234,1171,1172,1175],{"class":236,"line":415},[234,1173,1174],{"class":255},"    -p 8080:8080 ",[234,1176,1169],{"class":383},[234,1178,1179,1182],{"class":236,"line":434},[234,1180,1181],{"class":255},"    -e DATABASE_URL='postgres:\u002F\u002Fuser:pass@db.example.com:5432\u002Fapp' ",[234,1183,1169],{"class":383},[234,1185,1186,1189],{"class":236,"line":459},[234,1187,1188],{"class":255},"    --health-cmd='curl -f http:\u002F\u002Flocalhost:8080\u002Fhealthz || exit 1' ",[234,1190,1169],{"class":383},[234,1192,1193,1196],{"class":236,"line":464},[234,1194,1195],{"class":255},"    --health-interval=5s --health-timeout=2s --health-retries=3 ",[234,1197,1169],{"class":383},[234,1199,1200],{"class":236,"line":479},[234,1201,1202],{"class":255},"    meuusuario\u002Fmyapp:v1\n",[234,1204,1205],{"class":236,"line":484},[234,1206,1207],{"class":255},"\"\n",[12,1209,1210],{},"Repete pra VPS B trocando o IP. Valide:",[224,1212,1214],{"className":226,"code":1213,"language":228,"meta":229,"style":229},"curl http:\u002F\u002F203.0.113.10:8080\u002Fhealthz   # deve retornar \"ok\"\ncurl http:\u002F\u002F203.0.113.20:8080\u002Fhealthz   # deve retornar \"ok\"\n",[231,1215,1216,1227],{"__ignoreMap":229},[234,1217,1218,1221,1224],{"class":236,"line":237},[234,1219,1220],{"class":247},"curl",[234,1222,1223],{"class":255}," http:\u002F\u002F203.0.113.10:8080\u002Fhealthz",[234,1225,1226],{"class":240},"   # deve retornar \"ok\"\n",[234,1228,1229,1231,1234],{"class":236,"line":244},[234,1230,1220],{"class":247},[234,1232,1233],{"class":255}," http:\u002F\u002F203.0.113.20:8080\u002Fhealthz",[234,1235,1226],{"class":240},[12,1237,1238],{},"Se as duas voltam 200, a base está pronta.",[19,1240,1242],{"id":1241},"passo-4-caddy-como-reverse-proxy-balanceador-30-min","Passo 4 — Caddy como reverse proxy + balanceador (30 min)",[12,1244,1245],{},"Caddy é mais fácil de começar que nginx por causa do TLS automático embutido — Let's Encrypt funciona out of the box, sem configurar bot externo. nginx é mais flexível e tem ecossistema maior; Caddy é mais simples pra esse caso. Pro tutorial vou de Caddy.",[12,1247,1248],{},"Vou rodar o Caddy na VPS A, compartilhando a máquina com uma das instâncias da app. Se preferir uma terceira VPS dedicada, troca o IP onde for relevante.",[12,1250,1251],{},"Primeiro, libere portas 80 e 443 na VPS A:",[224,1253,1255],{"className":226,"code":1254,"language":228,"meta":229,"style":229},"ssh root@203.0.113.10 \"ufw allow 80 && ufw allow 443\"\n",[231,1256,1257],{"__ignoreMap":229},[234,1258,1259,1261,1263],{"class":236,"line":237},[234,1260,298],{"class":247},[234,1262,301],{"class":255},[234,1264,1265],{"class":255}," \"ufw allow 80 && ufw allow 443\"\n",[12,1267,1268,1269,1272],{},"Crie o ",[231,1270,1271],{},"Caddyfile",":",[224,1274,1278],{"className":1275,"code":1276,"language":1277,"meta":229,"style":229},"language-caddyfile shiki shiki-themes github-dark-default","meudominio.com {\n    reverse_proxy 203.0.113.10:8080 203.0.113.20:8080 {\n        lb_policy round_robin\n        health_uri \u002Fhealthz\n        health_interval 5s\n        health_timeout 2s\n        health_status 200\n\n        fail_duration 30s\n        max_fails 2\n        unhealthy_status 5xx\n\n        transport http {\n            dial_timeout 2s\n        }\n    }\n}\n","caddyfile",[231,1279,1280,1285,1290,1295,1300,1305,1310,1315,1319,1324,1329,1334,1338,1343,1348,1353,1358],{"__ignoreMap":229},[234,1281,1282],{"class":236,"line":237},[234,1283,1284],{},"meudominio.com {\n",[234,1286,1287],{"class":236,"line":244},[234,1288,1289],{},"    reverse_proxy 203.0.113.10:8080 203.0.113.20:8080 {\n",[234,1291,1292],{"class":236,"line":271},[234,1293,1294],{},"        lb_policy round_robin\n",[234,1296,1297],{"class":236,"line":415},[234,1298,1299],{},"        health_uri \u002Fhealthz\n",[234,1301,1302],{"class":236,"line":434},[234,1303,1304],{},"        health_interval 5s\n",[234,1306,1307],{"class":236,"line":459},[234,1308,1309],{},"        health_timeout 2s\n",[234,1311,1312],{"class":236,"line":464},[234,1313,1314],{},"        health_status 200\n",[234,1316,1317],{"class":236,"line":479},[234,1318,412],{"emptyLinePlaceholder":411},[234,1320,1321],{"class":236,"line":484},[234,1322,1323],{},"        fail_duration 30s\n",[234,1325,1326],{"class":236,"line":490},[234,1327,1328],{},"        max_fails 2\n",[234,1330,1331],{"class":236,"line":508},[234,1332,1333],{},"        unhealthy_status 5xx\n",[234,1335,1336],{"class":236,"line":529},[234,1337,412],{"emptyLinePlaceholder":411},[234,1339,1340],{"class":236,"line":535},[234,1341,1342],{},"        transport http {\n",[234,1344,1345],{"class":236,"line":546},[234,1346,1347],{},"            dial_timeout 2s\n",[234,1349,1350],{"class":236,"line":552},[234,1351,1352],{},"        }\n",[234,1354,1355],{"class":236,"line":557},[234,1356,1357],{},"    }\n",[234,1359,1360],{"class":236,"line":594},[234,1361,1362],{},"}\n",[12,1364,1365,1366,1368],{},"Quinze linhas. Tudo o que importa está aí: round-robin entre os dois IPs, health check ativo a cada cinco segundos no ",[231,1367,355],{},", marca como unhealthy depois de duas falhas seguidas em 30s, timeout de dois segundos pra abrir conexão.",[12,1370,1371],{},"Sobe Caddy:",[224,1373,1375],{"className":226,"code":1374,"language":228,"meta":229,"style":229},"ssh root@203.0.113.10 \"\n  mkdir -p \u002Fetc\u002Fcaddy &&\n  docker run -d --name caddy --restart=unless-stopped \\\n    --network host \\\n    -v \u002Fetc\u002Fcaddy\u002FCaddyfile:\u002Fetc\u002Fcaddy\u002FCaddyfile \\\n    -v caddy_data:\u002Fdata \\\n    -v caddy_config:\u002Fconfig \\\n    caddy:2-alpine\n\"\n",[231,1376,1377,1385,1390,1397,1404,1411,1418,1425,1430],{"__ignoreMap":229},[234,1378,1379,1381,1383],{"class":236,"line":237},[234,1380,298],{"class":247},[234,1382,301],{"class":255},[234,1384,1156],{"class":255},[234,1386,1387],{"class":236,"line":244},[234,1388,1389],{"class":255},"  mkdir -p \u002Fetc\u002Fcaddy &&\n",[234,1391,1392,1395],{"class":236,"line":271},[234,1393,1394],{"class":255},"  docker run -d --name caddy --restart=unless-stopped ",[234,1396,1169],{"class":383},[234,1398,1399,1402],{"class":236,"line":415},[234,1400,1401],{"class":255},"    --network host ",[234,1403,1169],{"class":383},[234,1405,1406,1409],{"class":236,"line":434},[234,1407,1408],{"class":255},"    -v \u002Fetc\u002Fcaddy\u002FCaddyfile:\u002Fetc\u002Fcaddy\u002FCaddyfile ",[234,1410,1169],{"class":383},[234,1412,1413,1416],{"class":236,"line":459},[234,1414,1415],{"class":255},"    -v caddy_data:\u002Fdata ",[234,1417,1169],{"class":383},[234,1419,1420,1423],{"class":236,"line":464},[234,1421,1422],{"class":255},"    -v caddy_config:\u002Fconfig ",[234,1424,1169],{"class":383},[234,1426,1427],{"class":236,"line":479},[234,1428,1429],{"class":255},"    caddy:2-alpine\n",[234,1431,1432],{"class":236,"line":484},[234,1433,1207],{"class":255},[12,1435,1436,1437,1439],{},"Aponte o DNS A do seu domínio pra ",[231,1438,280],{},". Em alguns minutos:",[224,1441,1443],{"className":226,"code":1442,"language":228,"meta":229,"style":229},"curl https:\u002F\u002Fmeudominio.com\u002F\n# deve retornar \"Hello v1\" (alternando entre as duas instâncias)\n",[231,1444,1445,1452],{"__ignoreMap":229},[234,1446,1447,1449],{"class":236,"line":237},[234,1448,1220],{"class":247},[234,1450,1451],{"class":255}," https:\u002F\u002Fmeudominio.com\u002F\n",[234,1453,1454],{"class":236,"line":244},[234,1455,1456],{"class":240},"# deve retornar \"Hello v1\" (alternando entre as duas instâncias)\n",[12,1458,1459],{},"Caddy emitiu certificado Let's Encrypt automaticamente. Isso funciona porque o domínio resolve pro IP onde Caddy está escutando na porta 80 (challenge HTTP-01).",[19,1461,1463],{"id":1462},"passo-5-script-bash-de-deploy-60-min","Passo 5 — Script bash de deploy (60 min)",[12,1465,1466],{},"Esse é o coração do tutorial. Um script que orquestra rolling update entre as duas VPS:",[224,1468,1470],{"className":226,"code":1469,"language":228,"meta":229,"style":229},"#!\u002Fusr\u002Fbin\u002Fenv bash\n# deploy.sh — rolling deploy zero-downtime entre duas VPS\nset -euo pipefail\n\nIMAGE=\"${1:?Uso: .\u002Fdeploy.sh meuusuario\u002Fmyapp:v2}\"\nHOSTS=(\"203.0.113.10\" \"203.0.113.20\")\nHEALTH_DEADLINE=300   # max segundos esperando health check\nMIN_HEALTHY_TIME=10   # segundos saudável sustentado antes de prosseguir\nSSH_OPTS=\"-o StrictHostKeyChecking=no -o ConnectTimeout=5\"\n\ndeploy_host() {\n  local host=$1\n  local image=$2\n  echo \"==> [${host}] pulling ${image}\"\n  ssh ${SSH_OPTS} \"root@${host}\" \"docker pull ${image}\"\n\n  # guarda imagem antiga pro caso de rollback\n  local old_image\n  old_image=$(ssh ${SSH_OPTS} \"root@${host}\" \"docker inspect app --format '{{.Config.Image}}' 2>\u002Fdev\u002Fnull || echo none\")\n  echo \"==> [${host}] versão atual: ${old_image}\"\n\n  echo \"==> [${host}] substituindo contêiner\"\n  ssh ${SSH_OPTS} \"root@${host}\" \"\n    docker stop app 2>\u002Fdev\u002Fnull || true\n    docker rm app 2>\u002Fdev\u002Fnull || true\n    docker run -d --name app --restart=unless-stopped \\\n      -p 8080:8080 \\\n      -e DATABASE_URL='${DATABASE_URL}' \\\n      --health-cmd='curl -f http:\u002F\u002Flocalhost:8080\u002Fhealthz || exit 1' \\\n      --health-interval=5s --health-timeout=2s --health-retries=3 \\\n      ${image}\n  \"\n\n  echo \"==> [${host}] esperando health check (max ${HEALTH_DEADLINE}s)\"\n  local start=$(date +%s)\n  local healthy_since=0\n  while true; do\n    local now=$(date +%s)\n    if (( now - start > HEALTH_DEADLINE )); then\n      echo \"!!  [${host}] healthy_deadline excedido — fazendo rollback pra ${old_image}\"\n      ssh ${SSH_OPTS} \"root@${host}\" \"\n        docker stop app && docker rm app &&\n        docker run -d --name app --restart=unless-stopped \\\n          -p 8080:8080 -e DATABASE_URL='${DATABASE_URL}' \\\n          ${old_image}\n      \"\n      return 1\n    fi\n\n    if curl -sf --max-time 2 \"http:\u002F\u002F${host}:8080\u002Fhealthz\" > \u002Fdev\u002Fnull; then\n      if (( healthy_since == 0 )); then\n        healthy_since=${now}\n        echo \"    [${host}] saudável — confirmando por ${MIN_HEALTHY_TIME}s\"\n      elif (( now - healthy_since >= MIN_HEALTHY_TIME )); then\n        echo \"==> [${host}] saudável sustentado — promovendo\"\n        return 0\n      fi\n    else\n      healthy_since=0\n    fi\n    sleep 2\n  done\n}\n\necho \"### Deploy ${IMAGE} em ${#HOSTS[@]} hosts (rolling, max_parallel=1)\"\nfor host in \"${HOSTS[@]}\"; do\n  if ! deploy_host \"${host}\" \"${IMAGE}\"; then\n    echo \"### Deploy abortado em ${host}. Hosts anteriores mantidos como estavam.\"\n    exit 1\n  fi\ndone\necho \"### Deploy completo: todos os hosts em ${IMAGE}\"\n",[231,1471,1472,1477,1482,1493,1497,1550,1567,1580,1593,1603,1607,1615,1628,1640,1660,1683,1687,1692,1699,1724,1740,1744,1755,1769,1774,1779,1786,1793,1805,1812,1819,1828,1833,1837,1853,1872,1884,1899,1918,1942,1960,1976,1982,1990,2002,2012,2018,2027,2033,2038,2073,2093,2104,2123,2144,2156,2165,2171,2177,2187,2192,2201,2207,2212,2217,2245,2273,2300,2314,2322,2328,2334],{"__ignoreMap":229},[234,1473,1474],{"class":236,"line":237},[234,1475,1476],{"class":240},"#!\u002Fusr\u002Fbin\u002Fenv bash\n",[234,1478,1479],{"class":236,"line":244},[234,1480,1481],{"class":240},"# deploy.sh — rolling deploy zero-downtime entre duas VPS\n",[234,1483,1484,1487,1490],{"class":236,"line":271},[234,1485,1486],{"class":251},"set",[234,1488,1489],{"class":251}," -euo",[234,1491,1492],{"class":255}," pipefail\n",[234,1494,1495],{"class":236,"line":415},[234,1496,412],{"emptyLinePlaceholder":411},[234,1498,1499,1502,1504,1507,1510,1513,1516,1518,1521,1524,1527,1529,1532,1535,1537,1540,1542,1545,1548],{"class":236,"line":434},[234,1500,1501],{"class":387},"IMAGE",[234,1503,473],{"class":383},[234,1505,1506],{"class":255},"\"",[234,1508,1509],{"class":251},"${1",[234,1511,1512],{"class":383},":?",[234,1514,1515],{"class":387},"Uso",[234,1517,1272],{"class":383},[234,1519,1520],{"class":255}," .",[234,1522,1523],{"class":383},"\u002F",[234,1525,1526],{"class":387},"deploy",[234,1528,101],{"class":255},[234,1530,1531],{"class":387},"sh",[234,1533,1534],{"class":387}," meuusuario",[234,1536,1523],{"class":383},[234,1538,1539],{"class":387},"myapp",[234,1541,1272],{"class":383},[234,1543,1544],{"class":387},"v2",[234,1546,1547],{"class":251},"}",[234,1549,1207],{"class":255},[234,1551,1552,1555,1557,1559,1562,1565],{"class":236,"line":459},[234,1553,1554],{"class":387},"HOSTS",[234,1556,473],{"class":383},[234,1558,520],{"class":387},[234,1560,1561],{"class":255},"\"203.0.113.10\"",[234,1563,1564],{"class":255}," \"203.0.113.20\"",[234,1566,526],{"class":387},[234,1568,1569,1572,1574,1577],{"class":236,"line":464},[234,1570,1571],{"class":387},"HEALTH_DEADLINE",[234,1573,473],{"class":383},[234,1575,1576],{"class":255},"300",[234,1578,1579],{"class":240},"   # max segundos esperando health check\n",[234,1581,1582,1585,1587,1590],{"class":236,"line":479},[234,1583,1584],{"class":387},"MIN_HEALTHY_TIME",[234,1586,473],{"class":383},[234,1588,1589],{"class":255},"10",[234,1591,1592],{"class":240},"   # segundos saudável sustentado antes de prosseguir\n",[234,1594,1595,1598,1600],{"class":236,"line":484},[234,1596,1597],{"class":387},"SSH_OPTS",[234,1599,473],{"class":383},[234,1601,1602],{"class":255},"\"-o StrictHostKeyChecking=no -o ConnectTimeout=5\"\n",[234,1604,1605],{"class":236,"line":490},[234,1606,412],{"emptyLinePlaceholder":411},[234,1608,1609,1612],{"class":236,"line":508},[234,1610,1611],{"class":427},"deploy_host",[234,1613,1614],{"class":387},"() {\n",[234,1616,1617,1620,1623,1625],{"class":236,"line":529},[234,1618,1619],{"class":383},"  local",[234,1621,1622],{"class":387}," host",[234,1624,473],{"class":383},[234,1626,1627],{"class":247},"$1\n",[234,1629,1630,1632,1635,1637],{"class":236,"line":535},[234,1631,1619],{"class":383},[234,1633,1634],{"class":387}," image",[234,1636,473],{"class":383},[234,1638,1639],{"class":247},"$2\n",[234,1641,1642,1645,1648,1651,1654,1657],{"class":236,"line":546},[234,1643,1644],{"class":251},"  echo",[234,1646,1647],{"class":255}," \"==> [${",[234,1649,1650],{"class":387},"host",[234,1652,1653],{"class":255},"}] pulling ${",[234,1655,1656],{"class":387},"image",[234,1658,1659],{"class":255},"}\"\n",[234,1661,1662,1665,1668,1671,1673,1676,1679,1681],{"class":236,"line":552},[234,1663,1664],{"class":247},"  ssh",[234,1666,1667],{"class":387}," ${SSH_OPTS} ",[234,1669,1670],{"class":255},"\"root@${",[234,1672,1650],{"class":387},[234,1674,1675],{"class":255},"}\"",[234,1677,1678],{"class":255}," \"docker pull ${",[234,1680,1656],{"class":387},[234,1682,1659],{"class":255},[234,1684,1685],{"class":236,"line":557},[234,1686,412],{"emptyLinePlaceholder":411},[234,1688,1689],{"class":236,"line":594},[234,1690,1691],{"class":240},"  # guarda imagem antiga pro caso de rollback\n",[234,1693,1694,1696],{"class":236,"line":635},[234,1695,1619],{"class":383},[234,1697,1698],{"class":387}," old_image\n",[234,1700,1701,1704,1706,1709,1711,1713,1715,1717,1719,1722],{"class":236,"line":643},[234,1702,1703],{"class":387},"  old_image",[234,1705,473],{"class":383},[234,1707,1708],{"class":387},"$(",[234,1710,298],{"class":247},[234,1712,1667],{"class":387},[234,1714,1670],{"class":255},[234,1716,1650],{"class":387},[234,1718,1675],{"class":255},[234,1720,1721],{"class":255}," \"docker inspect app --format '{{.Config.Image}}' 2>\u002Fdev\u002Fnull || echo none\"",[234,1723,526],{"class":387},[234,1725,1726,1728,1730,1732,1735,1738],{"class":236,"line":659},[234,1727,1644],{"class":251},[234,1729,1647],{"class":255},[234,1731,1650],{"class":387},[234,1733,1734],{"class":255},"}] versão atual: ${",[234,1736,1737],{"class":387},"old_image",[234,1739,1659],{"class":255},[234,1741,1742],{"class":236,"line":683},[234,1743,412],{"emptyLinePlaceholder":411},[234,1745,1746,1748,1750,1752],{"class":236,"line":695},[234,1747,1644],{"class":251},[234,1749,1647],{"class":255},[234,1751,1650],{"class":387},[234,1753,1754],{"class":255},"}] substituindo contêiner\"\n",[234,1756,1757,1759,1761,1763,1765,1767],{"class":236,"line":717},[234,1758,1664],{"class":247},[234,1760,1667],{"class":387},[234,1762,1670],{"class":255},[234,1764,1650],{"class":387},[234,1766,1675],{"class":255},[234,1768,1156],{"class":255},[234,1770,1771],{"class":236,"line":723},[234,1772,1773],{"class":255},"    docker stop app 2>\u002Fdev\u002Fnull || true\n",[234,1775,1776],{"class":236,"line":729},[234,1777,1778],{"class":255},"    docker rm app 2>\u002Fdev\u002Fnull || true\n",[234,1780,1781,1784],{"class":236,"line":734},[234,1782,1783],{"class":255},"    docker run -d --name app --restart=unless-stopped ",[234,1785,1169],{"class":383},[234,1787,1788,1791],{"class":236,"line":771},[234,1789,1790],{"class":255},"      -p 8080:8080 ",[234,1792,1169],{"class":383},[234,1794,1795,1798,1800,1803],{"class":236,"line":776},[234,1796,1797],{"class":255},"      -e DATABASE_URL='${",[234,1799,453],{"class":387},[234,1801,1802],{"class":255},"}' ",[234,1804,1169],{"class":383},[234,1806,1807,1810],{"class":236,"line":815},[234,1808,1809],{"class":255},"      --health-cmd='curl -f http:\u002F\u002Flocalhost:8080\u002Fhealthz || exit 1' ",[234,1811,1169],{"class":383},[234,1813,1814,1817],{"class":236,"line":820},[234,1815,1816],{"class":255},"      --health-interval=5s --health-timeout=2s --health-retries=3 ",[234,1818,1169],{"class":383},[234,1820,1821,1824,1826],{"class":236,"line":826},[234,1822,1823],{"class":255},"      ${",[234,1825,1656],{"class":387},[234,1827,1362],{"class":255},[234,1829,1830],{"class":236,"line":846},[234,1831,1832],{"class":255},"  \"\n",[234,1834,1835],{"class":236,"line":859},[234,1836,412],{"emptyLinePlaceholder":411},[234,1838,1839,1841,1843,1845,1848,1850],{"class":236,"line":872},[234,1840,1644],{"class":251},[234,1842,1647],{"class":255},[234,1844,1650],{"class":387},[234,1846,1847],{"class":255},"}] esperando health check (max ${",[234,1849,1571],{"class":387},[234,1851,1852],{"class":255},"}s)\"\n",[234,1854,1855,1857,1860,1862,1864,1867,1870],{"class":236,"line":898},[234,1856,1619],{"class":383},[234,1858,1859],{"class":387}," start",[234,1861,473],{"class":383},[234,1863,1708],{"class":387},[234,1865,1866],{"class":247},"date",[234,1868,1869],{"class":255}," +%s",[234,1871,526],{"class":387},[234,1873,1874,1876,1879,1881],{"class":236,"line":913},[234,1875,1619],{"class":383},[234,1877,1878],{"class":387}," healthy_since",[234,1880,473],{"class":383},[234,1882,1883],{"class":251},"0\n",[234,1885,1887,1890,1893,1896],{"class":236,"line":1886},37,[234,1888,1889],{"class":383},"  while",[234,1891,1892],{"class":251}," true",[234,1894,1895],{"class":387},"; ",[234,1897,1898],{"class":383},"do\n",[234,1900,1902,1905,1908,1910,1912,1914,1916],{"class":236,"line":1901},38,[234,1903,1904],{"class":383},"    local",[234,1906,1907],{"class":387}," now",[234,1909,473],{"class":383},[234,1911,1708],{"class":387},[234,1913,1866],{"class":247},[234,1915,1869],{"class":255},[234,1917,526],{"class":387},[234,1919,1921,1924,1927,1930,1933,1936,1939],{"class":236,"line":1920},39,[234,1922,1923],{"class":383},"    if",[234,1925,1926],{"class":387}," (( now ",[234,1928,1929],{"class":383},"-",[234,1931,1932],{"class":387}," start ",[234,1934,1935],{"class":383},">",[234,1937,1938],{"class":387}," HEALTH_DEADLINE )); ",[234,1940,1941],{"class":383},"then\n",[234,1943,1945,1948,1951,1953,1956,1958],{"class":236,"line":1944},40,[234,1946,1947],{"class":251},"      echo",[234,1949,1950],{"class":255}," \"!!  [${",[234,1952,1650],{"class":387},[234,1954,1955],{"class":255},"}] healthy_deadline excedido — fazendo rollback pra ${",[234,1957,1737],{"class":387},[234,1959,1659],{"class":255},[234,1961,1963,1966,1968,1970,1972,1974],{"class":236,"line":1962},41,[234,1964,1965],{"class":247},"      ssh",[234,1967,1667],{"class":387},[234,1969,1670],{"class":255},[234,1971,1650],{"class":387},[234,1973,1675],{"class":255},[234,1975,1156],{"class":255},[234,1977,1979],{"class":236,"line":1978},42,[234,1980,1981],{"class":255},"        docker stop app && docker rm app &&\n",[234,1983,1985,1988],{"class":236,"line":1984},43,[234,1986,1987],{"class":255},"        docker run -d --name app --restart=unless-stopped ",[234,1989,1169],{"class":383},[234,1991,1993,1996,1998,2000],{"class":236,"line":1992},44,[234,1994,1995],{"class":255},"          -p 8080:8080 -e DATABASE_URL='${",[234,1997,453],{"class":387},[234,1999,1802],{"class":255},[234,2001,1169],{"class":383},[234,2003,2005,2008,2010],{"class":236,"line":2004},45,[234,2006,2007],{"class":255},"          ${",[234,2009,1737],{"class":387},[234,2011,1362],{"class":255},[234,2013,2015],{"class":236,"line":2014},46,[234,2016,2017],{"class":255},"      \"\n",[234,2019,2021,2024],{"class":236,"line":2020},47,[234,2022,2023],{"class":383},"      return",[234,2025,2026],{"class":251}," 1\n",[234,2028,2030],{"class":236,"line":2029},48,[234,2031,2032],{"class":383},"    fi\n",[234,2034,2036],{"class":236,"line":2035},49,[234,2037,412],{"emptyLinePlaceholder":411},[234,2039,2041,2043,2046,2049,2052,2055,2058,2060,2063,2066,2069,2071],{"class":236,"line":2040},50,[234,2042,1923],{"class":383},[234,2044,2045],{"class":247}," curl",[234,2047,2048],{"class":251}," -sf",[234,2050,2051],{"class":251}," --max-time",[234,2053,2054],{"class":251}," 2",[234,2056,2057],{"class":255}," \"http:\u002F\u002F${",[234,2059,1650],{"class":387},[234,2061,2062],{"class":255},"}:8080\u002Fhealthz\"",[234,2064,2065],{"class":383}," >",[234,2067,2068],{"class":255}," \u002Fdev\u002Fnull",[234,2070,1895],{"class":387},[234,2072,1941],{"class":383},[234,2074,2076,2079,2082,2085,2088,2091],{"class":236,"line":2075},51,[234,2077,2078],{"class":383},"      if",[234,2080,2081],{"class":387}," (( healthy_since ",[234,2083,2084],{"class":383},"==",[234,2086,2087],{"class":251}," 0",[234,2089,2090],{"class":387}," )); ",[234,2092,1941],{"class":383},[234,2094,2096,2099,2101],{"class":236,"line":2095},52,[234,2097,2098],{"class":387},"        healthy_since",[234,2100,473],{"class":383},[234,2102,2103],{"class":387},"${now}\n",[234,2105,2107,2110,2113,2115,2118,2120],{"class":236,"line":2106},53,[234,2108,2109],{"class":251},"        echo",[234,2111,2112],{"class":255}," \"    [${",[234,2114,1650],{"class":387},[234,2116,2117],{"class":255},"}] saudável — confirmando por ${",[234,2119,1584],{"class":387},[234,2121,2122],{"class":255},"}s\"\n",[234,2124,2126,2129,2131,2133,2136,2139,2142],{"class":236,"line":2125},54,[234,2127,2128],{"class":383},"      elif",[234,2130,1926],{"class":387},[234,2132,1929],{"class":383},[234,2134,2135],{"class":387}," healthy_since ",[234,2137,2138],{"class":383},">=",[234,2140,2141],{"class":387}," MIN_HEALTHY_TIME )); ",[234,2143,1941],{"class":383},[234,2145,2147,2149,2151,2153],{"class":236,"line":2146},55,[234,2148,2109],{"class":251},[234,2150,1647],{"class":255},[234,2152,1650],{"class":387},[234,2154,2155],{"class":255},"}] saudável sustentado — promovendo\"\n",[234,2157,2159,2162],{"class":236,"line":2158},56,[234,2160,2161],{"class":383},"        return",[234,2163,2164],{"class":251}," 0\n",[234,2166,2168],{"class":236,"line":2167},57,[234,2169,2170],{"class":383},"      fi\n",[234,2172,2174],{"class":236,"line":2173},58,[234,2175,2176],{"class":383},"    else\n",[234,2178,2180,2183,2185],{"class":236,"line":2179},59,[234,2181,2182],{"class":387},"      healthy_since",[234,2184,473],{"class":383},[234,2186,1883],{"class":255},[234,2188,2190],{"class":236,"line":2189},60,[234,2191,2032],{"class":383},[234,2193,2195,2198],{"class":236,"line":2194},61,[234,2196,2197],{"class":247},"    sleep",[234,2199,2200],{"class":251}," 2\n",[234,2202,2204],{"class":236,"line":2203},62,[234,2205,2206],{"class":383},"  done\n",[234,2208,2210],{"class":236,"line":2209},63,[234,2211,1362],{"class":387},[234,2213,2215],{"class":236,"line":2214},64,[234,2216,412],{"emptyLinePlaceholder":411},[234,2218,2220,2223,2226,2228,2231,2234,2236,2239,2242],{"class":236,"line":2219},65,[234,2221,2222],{"class":251},"echo",[234,2224,2225],{"class":255}," \"### Deploy ${",[234,2227,1501],{"class":387},[234,2229,2230],{"class":255},"} em ${",[234,2232,2233],{"class":383},"#",[234,2235,1554],{"class":387},[234,2237,2238],{"class":255},"[",[234,2240,2241],{"class":383},"@",[234,2243,2244],{"class":255},"]} hosts (rolling, max_parallel=1)\"\n",[234,2246,2248,2251,2254,2257,2260,2262,2264,2266,2269,2271],{"class":236,"line":2247},66,[234,2249,2250],{"class":383},"for",[234,2252,2253],{"class":387}," host ",[234,2255,2256],{"class":383},"in",[234,2258,2259],{"class":255}," \"${",[234,2261,1554],{"class":387},[234,2263,2238],{"class":255},[234,2265,2241],{"class":383},[234,2267,2268],{"class":255},"]}\"",[234,2270,1895],{"class":387},[234,2272,1898],{"class":383},[234,2274,2276,2278,2281,2284,2286,2288,2290,2292,2294,2296,2298],{"class":236,"line":2275},67,[234,2277,597],{"class":383},[234,2279,2280],{"class":383}," !",[234,2282,2283],{"class":247}," deploy_host",[234,2285,2259],{"class":255},[234,2287,1650],{"class":387},[234,2289,1675],{"class":255},[234,2291,2259],{"class":255},[234,2293,1501],{"class":387},[234,2295,1675],{"class":255},[234,2297,1895],{"class":387},[234,2299,1941],{"class":383},[234,2301,2303,2306,2309,2311],{"class":236,"line":2302},68,[234,2304,2305],{"class":251},"    echo",[234,2307,2308],{"class":255}," \"### Deploy abortado em ${",[234,2310,1650],{"class":387},[234,2312,2313],{"class":255},"}. Hosts anteriores mantidos como estavam.\"\n",[234,2315,2317,2320],{"class":236,"line":2316},69,[234,2318,2319],{"class":251},"    exit",[234,2321,2026],{"class":251},[234,2323,2325],{"class":236,"line":2324},70,[234,2326,2327],{"class":383},"  fi\n",[234,2329,2331],{"class":236,"line":2330},71,[234,2332,2333],{"class":383},"done\n",[234,2335,2337,2339,2342,2344],{"class":236,"line":2336},72,[234,2338,2222],{"class":251},[234,2340,2341],{"class":255}," \"### Deploy completo: todos os hosts em ${",[234,2343,1501],{"class":387},[234,2345,1659],{"class":255},[12,2347,2348,2349,2352,2353,2356],{},"Salva como ",[231,2350,2351],{},"deploy.sh",", dá ",[231,2354,2355],{},"chmod +x",", e:",[224,2358,2360],{"className":226,"code":2359,"language":228,"meta":229,"style":229},"export DATABASE_URL='postgres:\u002F\u002Fuser:pass@db.example.com:5432\u002Fapp'\n.\u002Fdeploy.sh meuusuario\u002Fmyapp:v2\n",[231,2361,2362,2375],{"__ignoreMap":229},[234,2363,2364,2367,2370,2372],{"class":236,"line":237},[234,2365,2366],{"class":383},"export",[234,2368,2369],{"class":387}," DATABASE_URL",[234,2371,473],{"class":383},[234,2373,2374],{"class":255},"'postgres:\u002F\u002Fuser:pass@db.example.com:5432\u002Fapp'\n",[234,2376,2377,2380],{"class":236,"line":244},[234,2378,2379],{"class":247},".\u002Fdeploy.sh",[234,2381,2382],{"class":255}," meuusuario\u002Fmyapp:v2\n",[12,2384,2385],{},"O algoritmo é literalmente o que orquestradores grandes fazem internamente:",[67,2387,2388,2394,2408,2414,2419,2425,2438],{},[70,2389,2390,2393],{},[27,2391,2392],{},"Para cada host, sequencialmente"," (max_parallel = 1)",[70,2395,2396,2399,2400,2403,2404,2407],{},[27,2397,2398],{},"Pull da nova imagem"," antes de mexer no contêiner — assim o downtime entre ",[231,2401,2402],{},"docker stop"," e ",[231,2405,2406],{},"docker run"," é mínimo",[70,2409,2410,2413],{},[27,2411,2412],{},"Guarda referência da imagem antiga"," pra rollback se algo der errado",[70,2415,2416],{},[27,2417,2418],{},"Substitui o contêiner",[70,2420,2421,2424],{},[27,2422,2423],{},"Loop esperando health check"," com deadline de cinco minutos",[70,2426,2427,2430,2431,2433,2434,2437],{},[27,2428,2429],{},"Min healthy time de dez segundos",": só avança quando o ",[231,2432,355],{}," retornou 200 ",[179,2435,2436],{},"sustentadamente"," por dez segundos (se cair no meio, reinicia a contagem)",[70,2439,2440,2443],{},[27,2441,2442],{},"Rollback automático"," se passar do deadline",[12,2445,2446],{},"Os números (max_parallel: 1, min_healthy_time: 10s, healthy_deadline: 300s) são exatamente os defaults que usamos no HeroCtl. Não é coincidência — são os valores que sobreviveram a anos de tentativa e erro. Min healthy time muito curto detecta sintomas transitórios como \"saudável\" e quebra; muito longo deixa o deploy lento sem ganho. Dez segundos é o ponto onde ruído some e o deploy ainda termina rápido.",[19,2448,2450],{"id":2449},"passo-6-validar-com-teste-de-carga-durante-deploy-15-min","Passo 6 — Validar com teste de carga durante deploy (15 min)",[12,2452,2453],{},"Esse é o teste de fogo: rodar carga sustentada e fazer deploy ao mesmo tempo. Se algum 5xx aparecer, alguma parte do esquema está quebrada.",[12,2455,2456],{},"Numa máquina externa (seu laptop ou outra VPS):",[224,2458,2460],{"className":226,"code":2459,"language":228,"meta":229,"style":229},"# instale hey\ngo install github.com\u002Frakyll\u002Fhey@latest\n\n# carga sustentada de 60s, 5 conexões concorrentes\nhey -z 60s -c 5 https:\u002F\u002Fmeudominio.com\u002F\n",[231,2461,2462,2467,2478,2482,2487],{"__ignoreMap":229},[234,2463,2464],{"class":236,"line":237},[234,2465,2466],{"class":240},"# instale hey\n",[234,2468,2469,2472,2475],{"class":236,"line":244},[234,2470,2471],{"class":247},"go",[234,2473,2474],{"class":255}," install",[234,2476,2477],{"class":255}," github.com\u002Frakyll\u002Fhey@latest\n",[234,2479,2480],{"class":236,"line":271},[234,2481,412],{"emptyLinePlaceholder":411},[234,2483,2484],{"class":236,"line":415},[234,2485,2486],{"class":240},"# carga sustentada de 60s, 5 conexões concorrentes\n",[234,2488,2489,2492,2495,2498,2501,2504],{"class":236,"line":434},[234,2490,2491],{"class":247},"hey",[234,2493,2494],{"class":251}," -z",[234,2496,2497],{"class":255}," 60s",[234,2499,2500],{"class":251}," -c",[234,2502,2503],{"class":251}," 5",[234,2505,1451],{"class":255},[12,2507,2508],{},"Em outra janela, simultaneamente:",[224,2510,2512],{"className":226,"code":2511,"language":228,"meta":229,"style":229},".\u002Fdeploy.sh meuusuario\u002Fmyapp:v2\n",[231,2513,2514],{"__ignoreMap":229},[234,2515,2516,2518],{"class":236,"line":237},[234,2517,2379],{"class":247},[234,2519,2382],{"class":255},[12,2521,2522,2523,1272],{},"No final do ",[231,2524,2491],{},[224,2526,2531],{"className":2527,"code":2529,"language":2530},[2528],"language-text","Status code distribution:\n  [200] 1847 responses\n","text",[231,2532,2529],{"__ignoreMap":229},[12,2534,2535],{},"Só 200. Se aparecer um 502 ou 503, alguma das três peças tá fraca: health check retornando 200 cedo demais, graceful shutdown ausente, ou min healthy time curto. Investiga e corrige.",[57,2537],{},[19,2539,2541],{"id":2540},"os-seis-detalhes-que-separam-zero-downtime-real-de-aproximacao","Os seis detalhes que separam zero-downtime real de aproximação",[12,2543,2544],{},"Cobrimos boa parte deles ao longo do tutorial, mas vale consolidar — porque um único desses ausentes converte o esquema todo em \"mostly zero-downtime\", que é diferente.",[67,2546,2547,2556,2573,2589,2595,2601],{},[70,2548,2549,2552,2553,2555],{},[27,2550,2551],{},"Connection draining no SIGTERM."," Quando o contêiner recebe sinal de parada, a app marca ",[231,2554,355],{}," como falhando imediatamente, mas continua aceitando conexões em curso por alguns segundos. Sem isso, conexões abertas no momento do stop são cortadas.",[70,2557,2558,2561,2562,2565,2566,2569,2570,101],{},[27,2559,2560],{},"Pre-stop hook se você tem worker assíncrono."," Filas que processam jobs em background precisam de pausa explícita antes de matar o processo, ou o job em execução fica órfão. Em Sidekiq, é o ",[231,2563,2564],{},":quiet"," antes de ",[231,2567,2568],{},":term",". Em Celery, é ",[231,2571,2572],{},"--soft-time-limit",[70,2574,2575,2578,2579,2582,2583,2585,2586,2588],{},[27,2576,2577],{},"Health check ANTES de promover, não \"container running\"."," ",[231,2580,2581],{},"docker ps"," mostra \"running\" milisegundos depois do ",[231,2584,2406],{},". Não significa nada. Promover só depois de ",[231,2587,355],{}," retornar 200 sustentadamente.",[70,2590,2591,2594],{},[27,2592,2593],{},"Min healthy time de dez segundos sustentados."," Não vale ver um único 200 e seguir em frente — apps com warm-up irregular passam um momento e voltam a falhar.",[70,2596,2597,2600],{},[27,2598,2599],{},"Versão anterior pré-puxada pra rollback rápido."," Se você confiou em \"manter a imagem antiga em cache do Docker\", em algum momento ela é apagada por garbage collection e o rollback fica lento. Mantenha as últimas três imagens explicitamente.",[70,2602,2603,2606],{},[27,2604,2605],{},"Auto-revert ao passar do healthy deadline."," Sem isso, o deploy trava num estado parcial — metade dos hosts em v2, metade em v1, sem ninguém pra decidir o que fazer.",[19,2608,2610],{"id":2609},"database-migrations-zero-downtime-a-parte-que-quebra-deploy-de-gente-experiente","Database migrations + zero-downtime (a parte que quebra deploy de gente experiente)",[12,2612,2613,2614,2617],{},"Esse é o tópico que eu mais vejo desenvolvedor sênior errar. Rolling deploy assume que ",[27,2615,2616],{},"as duas versões da app rodam simultaneamente em produção por algum período",". Se a v2 espera schema incompatível com o que a v1 entende, alguma das duas quebra durante a janela de transição.",[12,2619,2620,2621,101],{},"Regra de ouro inegociável: ",[27,2622,2623],{},"migrations são sempre backward-compatible",[12,2625,2626,2627,2630,2631,2634,2635,2637],{},"Caso clássico: você quer renomear coluna ",[231,2628,2629],{},"email"," pra ",[231,2632,2633],{},"email_address",". Solução errada: faz a migration que renomeia direto antes do deploy. Resultado: durante o rolling, instâncias v1 ainda escrevem em ",[231,2636,2629],{}," (que não existe mais) e quebram. Solução certa, em três deploys:",[119,2639,2640,2653],{},[122,2641,2642],{},[125,2643,2644,2647,2650],{},[128,2645,2646],{},"Deploy",[128,2648,2649],{},"Migration",[128,2651,2652],{},"Código v*",[141,2654,2655,2677,2695],{},[125,2656,2657,2660,2666],{},[146,2658,2659],{},"1",[146,2661,2662,2663,2665],{},"Adiciona ",[231,2664,2633],{}," (nullable). Nenhuma remoção.",[146,2667,2668,2669,2671,2672,2674,2675,101],{},"App escreve em ",[231,2670,2629],{}," E em ",[231,2673,2633],{},"; lê de ",[231,2676,2629],{},[125,2678,2679,2682,2689],{},[146,2680,2681],{},"2",[146,2683,2684,2685,2688],{},"Backfill: ",[231,2686,2687],{},"UPDATE users SET email_address = email WHERE email_address IS NULL",". NOT NULL constraint.",[146,2690,2691,2692,2694],{},"App lê de ",[231,2693,2633],{},"; ainda escreve nas duas.",[125,2696,2697,2700,2705],{},[146,2698,2699],{},"3",[146,2701,2702,2703,101],{},"Drop ",[231,2704,2629],{},[146,2706,2707,2708,101],{},"App só usa ",[231,2709,2633],{},[12,2711,2712],{},"Três deploys, semanas de espaçamento. É chato, é o jeito. Drop de coluna direto sempre quebra. Mudança de tipo direto sempre quebra. Adicionar NOT NULL sem default direto sempre quebra.",[12,2714,2715,2716,2403,2719,2722,2723,2726],{},"Ferramentas que ajudam: ",[231,2717,2718],{},"pg-osc",[231,2720,2721],{},"pgroll"," (Postgres), ",[231,2724,2725],{},"gh-ost"," (MySQL) — fazem schema change online, sem lock longo. Pra migrations leves, o jeito manual em três passos resolve.",[19,2728,2730],{"id":2729},"padroes-alem-de-rolling","Padrões além de rolling",[12,2732,2733],{},"Rolling é o padrão default e mais econômico. Tem outros que valem conhecer:",[2735,2736,2737,2743,2749],"ul",{},[70,2738,2739,2742],{},[27,2740,2741],{},"Blue-green."," Dois ambientes paralelos completos — \"blue\" rodando v1, \"green\" provisionado com v2 vazio. Você sobe v2 inteiro em green, valida, troca DNS (ou cutover do balanceador). Vantagem: rollback instantâneo (volta DNS pra blue). Desvantagem: custa o dobro de recursos durante a janela de deploy.",[70,2744,2745,2748],{},[27,2746,2747],{},"Canary."," Manda 5% do tráfego pra v2, observa métricas (erros, latência, taxa de conversão), decide se promove pra 100% ou aborta. Detecta bugs sutis que health check não pega — tipo regression em conversão de checkout. Exige proxy com weighted routing e observabilidade decente.",[70,2750,2751,2754],{},[27,2752,2753],{},"Rainbow \u002F N+1."," Generalização do blue-green com N versões coexistindo. Útil quando você quer A\u002FB test de longa duração entre versões inteiras.",[12,2756,2757],{},"Pro tutorial, rolling é o que faz sentido. Os outros valem quando o tamanho do tráfego justifica investimento extra.",[19,2759,2761],{"id":2760},"versao-facil-coolify-ou-dokploy","Versão \"fácil\" — Coolify ou Dokploy",[12,2763,2764],{},"Se você não quer scriptar, dois painéis modernos fazem rolling deploy automaticamente:",[2735,2766,2767,2773],{},[70,2768,2769,2772],{},[27,2770,2771],{},"Coolify"," em modo multi-server faz rolling com health check configurável. Multi-server foi adicionado nas versões mais recentes — antes era single-server only. Vale checar a versão.",[70,2774,2775,2778,2779,2782],{},[27,2776,2777],{},"Dokploy"," em cima de Docker Swarm faz rolling com ",[231,2780,2781],{},"--update-parallelism 1 --update-delay",". Aproveita o que o Swarm já oferece.",[12,2784,2785],{},"Trade-off: você troca o script de cinquenta linhas (que entende tudo o que está acontecendo) por um painel (que é mais rápido pra subir, mas vira caixa-preta quando algo dá errado). Pra time pequeno onde uma pessoa cuida de operação parcialmente, o painel ganha. Pra time onde você precisa entender exatamente o que rolou na 3h da manhã, o script ganha.",[19,2787,2789],{"id":2788},"versao-robusta-heroctl","Versão \"robusta\" — HeroCtl",[12,2791,2792],{},"Pra quem quer parar de scriptar mas não quer caixa-preta, o HeroCtl combina rolling deploy automático com plano de controle replicado. Você descreve o serviço em arquivo de configuração e o orquestrador faz o resto:",[224,2794,2798],{"className":2795,"code":2796,"language":2797,"meta":229,"style":229},"language-hcl shiki shiki-themes github-dark-default","job \"minhaapp\" {\n  group \"web\" {\n    count = 2\n\n    task \"app\" {\n      driver = \"docker\"\n      config {\n        image = \"meuusuario\u002Fmyapp:v2\"\n        ports = [\"http\"]\n      }\n\n      service {\n        port = \"http\"\n        check {\n          type     = \"http\"\n          path     = \"\u002Fhealthz\"\n          interval = \"5s\"\n          timeout  = \"2s\"\n        }\n      }\n    }\n\n    update {\n      max_parallel      = 1\n      min_healthy_time  = \"10s\"\n      healthy_deadline  = \"5m\"\n      auto_revert       = true\n    }\n  }\n}\n","hcl",[231,2799,2800,2805,2810,2815,2819,2824,2829,2834,2839,2844,2849,2853,2858,2863,2868,2873,2878,2883,2888,2892,2896,2900,2904,2909,2914,2919,2924,2929,2933,2937],{"__ignoreMap":229},[234,2801,2802],{"class":236,"line":237},[234,2803,2804],{},"job \"minhaapp\" {\n",[234,2806,2807],{"class":236,"line":244},[234,2808,2809],{},"  group \"web\" {\n",[234,2811,2812],{"class":236,"line":271},[234,2813,2814],{},"    count = 2\n",[234,2816,2817],{"class":236,"line":415},[234,2818,412],{"emptyLinePlaceholder":411},[234,2820,2821],{"class":236,"line":434},[234,2822,2823],{},"    task \"app\" {\n",[234,2825,2826],{"class":236,"line":459},[234,2827,2828],{},"      driver = \"docker\"\n",[234,2830,2831],{"class":236,"line":464},[234,2832,2833],{},"      config {\n",[234,2835,2836],{"class":236,"line":479},[234,2837,2838],{},"        image = \"meuusuario\u002Fmyapp:v2\"\n",[234,2840,2841],{"class":236,"line":484},[234,2842,2843],{},"        ports = [\"http\"]\n",[234,2845,2846],{"class":236,"line":490},[234,2847,2848],{},"      }\n",[234,2850,2851],{"class":236,"line":508},[234,2852,412],{"emptyLinePlaceholder":411},[234,2854,2855],{"class":236,"line":529},[234,2856,2857],{},"      service {\n",[234,2859,2860],{"class":236,"line":535},[234,2861,2862],{},"        port = \"http\"\n",[234,2864,2865],{"class":236,"line":546},[234,2866,2867],{},"        check {\n",[234,2869,2870],{"class":236,"line":552},[234,2871,2872],{},"          type     = \"http\"\n",[234,2874,2875],{"class":236,"line":557},[234,2876,2877],{},"          path     = \"\u002Fhealthz\"\n",[234,2879,2880],{"class":236,"line":594},[234,2881,2882],{},"          interval = \"5s\"\n",[234,2884,2885],{"class":236,"line":635},[234,2886,2887],{},"          timeout  = \"2s\"\n",[234,2889,2890],{"class":236,"line":643},[234,2891,1352],{},[234,2893,2894],{"class":236,"line":659},[234,2895,2848],{},[234,2897,2898],{"class":236,"line":683},[234,2899,1357],{},[234,2901,2902],{"class":236,"line":695},[234,2903,412],{"emptyLinePlaceholder":411},[234,2905,2906],{"class":236,"line":717},[234,2907,2908],{},"    update {\n",[234,2910,2911],{"class":236,"line":723},[234,2912,2913],{},"      max_parallel      = 1\n",[234,2915,2916],{"class":236,"line":729},[234,2917,2918],{},"      min_healthy_time  = \"10s\"\n",[234,2920,2921],{"class":236,"line":734},[234,2922,2923],{},"      healthy_deadline  = \"5m\"\n",[234,2925,2926],{"class":236,"line":771},[234,2927,2928],{},"      auto_revert       = true\n",[234,2930,2931],{"class":236,"line":776},[234,2932,1357],{},[234,2934,2935],{"class":236,"line":815},[234,2936,720],{},[234,2938,2939],{"class":236,"line":820},[234,2940,1362],{},[12,2942,2943],{},"Os mesmos parâmetros do script bash, declarativos. A diferença é que o orquestrador coordena rolling entre N servidores (não só dois), faz eleição automática de coordenador em torno de sete segundos se o nó atual cair, e mantém o plano de controle distribuído entre os primeiros três servidores. Cluster sobrevive a perda de qualquer servidor único sem intervenção humana.",[12,2945,2946],{},"Instalação:",[224,2948,2950],{"className":226,"code":2949,"language":228,"meta":229,"style":229},"curl -sSL https:\u002F\u002Fget.heroctl.com\u002Finstall.sh | sh\n",[231,2951,2952],{"__ignoreMap":229},[234,2953,2954,2956,2959,2962,2965],{"class":236,"line":237},[234,2955,1220],{"class":247},[234,2957,2958],{"class":251}," -sSL",[234,2960,2961],{"class":255}," https:\u002F\u002Fget.heroctl.com\u002Finstall.sh",[234,2963,2964],{"class":383}," |",[234,2966,2967],{"class":247}," sh\n",[12,2969,2970],{},"Plano Community é gratuito permanente — sem limite de servidores ou jobs, com todas as features de orquestração descritas no tutorial. Plano Business adiciona SSO\u002FSAML, RBAC granular, auditoria detalhada e suporte com SLA, pra times que têm requisitos formais de plataforma. Plano Enterprise adiciona escrow de código-fonte, contrato de continuidade e suporte 24×7. Os preços de Business e Enterprise estão publicados na página de planos — sem \"fale com vendas\" obrigatório.",[19,2972,2974],{"id":2973},"comparativo-cinco-caminhos-lado-a-lado","Comparativo: cinco caminhos lado a lado",[119,2976,2977,3002],{},[122,2978,2979],{},[125,2980,2981,2984,2987,2990,2993,2996,2999],{},[128,2982,2983],{},"Critério",[128,2985,2986],{},"Script bash (2 servers)",[128,2988,2989],{},"Coolify multi-server",[128,2991,2992],{},"Dokploy + Swarm",[128,2994,2995],{},"HeroCtl",[128,2997,2998],{},"Kamal",[128,3000,3001],{},"Kubernetes",[141,3003,3004,3026,3049,3071,3089,3106,3128,3148],{},[125,3005,3006,3009,3012,3015,3018,3021,3023],{},[146,3007,3008],{},"Tempo de setup",[146,3010,3011],{},"2-3h",[146,3013,3014],{},"30 min",[146,3016,3017],{},"1h",[146,3019,3020],{},"5 min",[146,3022,3017],{},[146,3024,3025],{},"4h-4 dias",[125,3027,3028,3031,3034,3037,3040,3043,3046],{},[146,3029,3030],{},"Linhas de config",[146,3032,3033],{},"~50 (script)",[146,3035,3036],{},"UI",[146,3038,3039],{},"~20",[146,3041,3042],{},"~50",[146,3044,3045],{},"~40",[146,3047,3048],{},"300+",[125,3050,3051,3054,3057,3060,3063,3066,3068],{},[146,3052,3053],{},"HA do plano de controle",[146,3055,3056],{},"N\u002FA",[146,3058,3059],{},"Não",[146,3061,3062],{},"Limitado",[146,3064,3065],{},"Sim",[146,3067,3056],{},[146,3069,3070],{},"Sim (5+ componentes)",[125,3072,3073,3076,3079,3081,3083,3085,3087],{},[146,3074,3075],{},"Health check declarativo",[146,3077,3078],{},"Manual",[146,3080,3065],{},[146,3082,3065],{},[146,3084,3065],{},[146,3086,3065],{},[146,3088,3065],{},[125,3090,3091,3093,3096,3098,3100,3102,3104],{},[146,3092,2442],{},[146,3094,3095],{},"Manual no script",[146,3097,3065],{},[146,3099,3065],{},[146,3101,3065],{},[146,3103,3065],{},[146,3105,3065],{},[125,3107,3108,3111,3114,3117,3120,3123,3125],{},[146,3109,3110],{},"Escala alvo",[146,3112,3113],{},"1-3 servers",[146,3115,3116],{},"1-10 servers",[146,3118,3119],{},"1-20 servers",[146,3121,3122],{},"1-500 servers",[146,3124,3116],{},[146,3126,3127],{},"50+ servers",[125,3129,3130,3133,3136,3138,3141,3144,3146],{},[146,3131,3132],{},"Caixa-preta?",[146,3134,3135],{},"Não (você escreveu)",[146,3137,3065],{},[146,3139,3140],{},"Parcial",[146,3142,3143],{},"Não (declarativo curto)",[146,3145,3059],{},[146,3147,3065],{},[125,3149,3150,3153,3156,3158,3161,3163,3165],{},[146,3151,3152],{},"Curva de aprendizado",[146,3154,3155],{},"Baixa",[146,3157,3155],{},[146,3159,3160],{},"Média",[146,3162,3155],{},[146,3164,3155],{},[146,3166,3167],{},"Alta",[12,3169,3170],{},"Cada coluna tem seu nicho. Script bash é insuperável quando você quer entender cada linha. Coolify ganha quando você só quer um painel. HeroCtl ganha quando você precisa de HA real sem montar plano de controle externo. Kubernetes ganha em escala planetária — onde a complexidade compensa.",[19,3172,3174],{"id":3173},"os-cinco-erros-mais-comuns","Os cinco erros mais comuns",[67,3176,3177,3189,3195,3205,3217],{},[70,3178,3179,3185,3186,3188],{},[27,3180,3181,3182,3184],{},"Health check em ",[231,3183,1523],{}," retornando 200 sem validar dependências."," A app retorna 200 antes de conectar no banco, o proxy promove, e o usuário vê erro 500 nas primeiras requests. Solução: ",[231,3187,355],{}," valida banco, cache, fila — qualquer coisa que a app precise pra responder de verdade.",[70,3190,3191,3194],{},[27,3192,3193],{},"Min healthy time de 1 segundo."," Apps com warm-up irregular podem retornar 200 num momento e 503 logo depois (cache populando, classe sendo lazy-loaded). O orquestrador promove na primeira janela boa, e a próxima request bate em estado ruim. Dez segundos sustentados eliminam noventa por cento desses casos.",[70,3196,3197,3200,3201,3204],{},[27,3198,3199],{},"Sem max_parallel (ou max_parallel = N)."," Se você troca todas as instâncias juntas, durante a janela do cutover não tem ninguém saudável servindo. É single-server downtime disfarçado. Sempre ",[231,3202,3203],{},"max_parallel = 1"," pra começar.",[70,3206,3207,3210,3211,3213,3214,3216],{},[27,3208,3209],{},"Mix de versões em produção sem schema compat."," v1 escreve em ",[231,3212,2629],{},", v2 lê de ",[231,3215,2633],{},", e durante o rolling de cinco minutos as duas convivem — usuários que pegam v2 não veem dados que v1 acabou de gravar. Migration backward-compatible em três passos resolve.",[70,3218,3219,3222],{},[27,3220,3221],{},"Cache stale no cliente (CDN, browser, service worker)."," Backend já é v2 mas o usuário tem o JS de v1 em cache, e o JS antigo chama API que não existe mais. Solução: mantém endpoints antigos por uma janela; versionamento de API; cache-busting forte em assets críticos.",[19,3224,3226],{"id":3225},"faq","FAQ",[12,3228,3229],{},[27,3230,3231],{},"Posso fazer zero-downtime com um servidor só?",[12,3233,3234,3235,3238],{},"Não. Toda variação que prometa isso tem janela de erro mensurável quando você mede com ",[231,3236,3237],{},"hey -c 20",". A única forma de ter zero-downtime real é manter pelo menos uma instância sempre saudável durante todo o deploy — o que exige duas máquinas no mínimo.",[12,3240,3241],{},[27,3242,3243],{},"DNS round-robin funciona como balanceador?",[12,3245,3246],{},"Funciona como balanceador básico, mas não como mecanismo de health check. DNS não tira IP morto da rotação rapidamente — TTLs caching em ISPs e clientes mantêm o IP errado em uso por minutos ou horas. Pra zero-downtime você precisa de um proxy real (Caddy, nginx, HAProxy) que tira instância unhealthy do balanceamento em segundos.",[12,3248,3249],{},[27,3250,3251],{},"Caddy ou Traefik — qual é melhor pra esse setup?",[12,3253,3254],{},"Pra dois servidores e um setup estático, Caddy é mais simples — Caddyfile de quinze linhas resolve. Traefik brilha quando você tem descoberta dinâmica de serviços (tipo Docker labels ou Consul) e muitos backends mudando o tempo todo. nginx fica no meio: mais flexível, sem TLS automático embutido (precisa de certbot externo). Pra esse tutorial, Caddy.",[12,3256,3257],{},[27,3258,3259],{},"WebSocket connections sobrevivem durante rolling?",[12,3261,3262,3263,3265],{},"Conexões abertas em instância que está sendo derrubada são cortadas. O cliente tem que reconectar. Boa biblioteca de WebSocket (Socket.IO, Phoenix Channels) reconecta automaticamente — usuário vê uma piscada de meio segundo no estado. Connection draining ajuda: a instância marca ",[231,3264,355],{}," falhando, o proxy para de mandar conexões novas, mas as existentes continuam até o pre-stop timer. Trinta segundos de drain costumam ser suficientes pra que conexões longas se esvaziem naturalmente.",[12,3267,3268],{},[27,3269,3270],{},"Database migrations — qual a regra de ouro?",[12,3272,3273],{},"Toda migration tem que ser backward-compatible. Drop de coluna nunca direto. Rename nunca direto. Mudança de tipo nunca direto. Em vez disso, três deploys: adiciona estrutura nova, backfill, remove a antiga. Lento, sim. Mas o rolling deploy depende disso pra não quebrar.",[12,3275,3276],{},[27,3277,3278],{},"Rollback automático — como implementar?",[12,3280,3281,3282,101],{},"Duas peças: deadline (tempo máximo esperando health check) e referência da imagem anterior pré-puxada. Se passar do deadline sem ficar saudável, o script reinstala a versão anterior. O exemplo no Passo 5 faz exatamente isso. Em orquestradores declarativos, vira ",[231,3283,3284],{},"auto_revert = true",[12,3286,3287],{},[27,3288,3289],{},"Sticky sessions complicam zero-downtime?",[12,3291,3292],{},"Sim. Se a app guarda estado de sessão em memória do processo, derrubar a instância derruba as sessões dos usuários conectados a ela. Solução: tira sessão da memória — Redis, Postgres ou JWT signed. Aí qualquer instância serve qualquer usuário, e rolling não corta nenhuma sessão.",[12,3294,3295],{},[27,3296,3297],{},"Quanto tempo demora um deploy completo?",[12,3299,3300,3301,3304],{},"Dois servidores, app que sobe em quinze segundos: cerca de um minuto. Detalhamento: pull da imagem (5-15s, depende da rede e do tamanho), substituição do contêiner (1s), warm-up + health check (10-30s), min healthy time de 10s, total uns 30-50s por host, multiplicado por dois hosts em sequência = 1-2 min. Quatro servidores ficam em torno de 2-4 min. Com cinquenta servidores, deploy começa a tomar dez ou quinze minutos — momento de aumentar ",[231,3302,3303],{},"max_parallel"," pra dois ou três (mantendo health check rigoroso).",[57,3306],{},[19,3308,3310],{"id":3309},"fechamento","Fechamento",[12,3312,3313],{},"Zero-downtime deploy é arquitetura, não ferramenta. Os três ingredientes — múltiplas instâncias, proxy com health check, rolling controlado — funcionam com bash e Caddy tanto quanto com orquestrador grande. A diferença está em quanto da operação você quer escrever na mão e quanto delegar.",[12,3315,3316],{},"Pra um SaaS pequeno, três VPS e um script de cinquenta linhas resolvem indefinidamente. Quando o cluster cresce pra dezenas de servidores ou o time precisa de HA real do plano de controle, vale subir pro orquestrador declarativo:",[224,3318,3319],{"className":226,"code":2949,"language":228,"meta":229,"style":229},[231,3320,3321],{"__ignoreMap":229},[234,3322,3323,3325,3327,3329,3331],{"class":236,"line":237},[234,3324,1220],{"class":247},[234,3326,2958],{"class":251},[234,3328,2961],{"class":255},[234,3330,2964],{"class":383},[234,3332,2967],{"class":247},[12,3334,3335,3336,3341,3342,3346],{},"Mais sobre o algoritmo de rolling em ",[3337,3338,3340],"a",{"href":3339},"\u002Fblog\u002Frolling-deploy-seguro-por-que-o-seu-talvez-nao-seja","Rolling deploy seguro: por que o seu talvez não seja",". Pra quem está saindo de Compose pra setup multi-servidor, ",[3337,3343,3345],{"href":3344},"\u002Fblog\u002Fdeploy-docker-producao-do-compose-ao-cluster","Deploy Docker em produção: do compose ao cluster"," cobre o caminho intermediário.",[12,3348,3349],{},"Orquestração de contêineres, sem cerimônia.",[3351,3352,3353],"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 .sFSAA, html code.shiki .sFSAA{--shiki-default:#79C0FF}html pre.shiki code .s9uIt, html code.shiki .s9uIt{--shiki-default:#A5D6FF}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}html pre.shiki code .sc3cj, html code.shiki .sc3cj{--shiki-default:#D2A8FF}",{"title":229,"searchDepth":244,"depth":244,"links":3355},[3356,3357,3358,3359,3360,3361,3366,3367,3368,3369,3370,3371,3372,3373,3374,3375,3376,3377,3378],{"id":21,"depth":244,"text":22},{"id":61,"depth":244,"text":62},{"id":93,"depth":244,"text":94},{"id":113,"depth":244,"text":114},{"id":215,"depth":244,"text":216},{"id":348,"depth":244,"text":349,"children":3362},[3363,3364,3365],{"id":370,"depth":271,"text":371},{"id":918,"depth":271,"text":919},{"id":1002,"depth":271,"text":1003},{"id":1099,"depth":244,"text":1100},{"id":1241,"depth":244,"text":1242},{"id":1462,"depth":244,"text":1463},{"id":2449,"depth":244,"text":2450},{"id":2540,"depth":244,"text":2541},{"id":2609,"depth":244,"text":2610},{"id":2729,"depth":244,"text":2730},{"id":2760,"depth":244,"text":2761},{"id":2788,"depth":244,"text":2789},{"id":2973,"depth":244,"text":2974},{"id":3173,"depth":244,"text":3174},{"id":3225,"depth":244,"text":3226},{"id":3309,"depth":244,"text":3310},"engenharia",null,"2026-06-09","Você não precisa de Kubernetes pra ter deploy sem downtime. Tutorial completo com 2 servidores, Caddy\u002FTraefik na frente, e rolling update via script ou orquestrador leve.",false,"md",{},"\u002Fblog\u002Fdeploy-zero-downtime-sem-kubernetes-tutorial","15 min",{"title":6,"description":3382},{"loc":3386},"blog\u002Fdeploy-zero-downtime-sem-kubernetes-tutorial",[1526,3392,3393,3379],"zero-downtime","tutorial","6-My071frsY_ilBftAnrX2BPg0OBLI-wOOVTkQBW3O0",{"id":3396,"title":3397,"author":7,"body":3398,"category":3379,"cover":3380,"date":4397,"description":4398,"draft":3383,"extension":3384,"lastReviewed":3380,"meta":4399,"navigation":411,"path":4400,"readingTime":4401,"seo":4402,"sitemap":4403,"stem":4404,"tags":4405,"__hash__":4410},"blog_pt\u002Fblog\u002Fapi-gateway-self-hosted-traefik-kong-quando-instalar.md","API Gateway self-hosted: quando vale instalar Kong, Traefik ou similar",{"type":9,"value":3399,"toc":4377},[3400,3403,3406,3410,3417,3420,3427,3431,3434,3516,3522,3582,3585,3589,3592,3596,3599,3605,3611,3617,3621,3624,3629,3634,3639,3643,3646,3651,3656,3661,3665,3668,3673,3678,3683,3687,3690,3695,3700,3705,3709,3712,3750,3753,3757,3760,3812,3815,3819,3822,3828,3831,3834,3838,4124,4131,4135,4138,4144,4150,4156,4162,4168,4171,4175,4178,4184,4190,4196,4202,4206,4209,4241,4245,4255,4261,4267,4277,4283,4289,4295,4309,4315,4319,4322,4325,4328,4331,4347,4361,4371,4374],[12,3401,3402],{},"\"API gateway\" é uma das categorias mais sobrecarregadas de jargão na arquitetura de back-end. O termo virou guarda-chuva pra coisas que reverse proxy simples já faz há vinte anos — rotear, terminar TLS, balancear entre instâncias — misturadas com coisas que de fato exigem um componente dedicado: validação de chave de API por cliente, limite de requisições por usuário, transformação de corpo da requisição, agregação de múltiplos back-ends numa só resposta. A confusão vende muito produto. E faz muita startup instalar componente crítico que ela não precisava — pagando depois em latência, RAM, complexidade operacional e área de superfície de falha.",[12,3404,3405],{},"Este post separa o que cada coisa cobre, lista os cinco jogadores principais com números honestos de consumo de recursos, e desenha uma régua prática: quando o reverse proxy embutido no orquestrador resolve, quando vale subir um Traefik standalone, e quando você de fato precisa de um Kong com plug-ins de autenticação. A audiência é o tech lead que está olhando pro stack atual e tentando decidir se a próxima dor merece mais um componente no caminho crítico — ou se a dor é falsa.",[19,3407,3409],{"id":3408},"tldr-instalar-gateway-dedicado-e-decisao-cara-mantenha-a-regua-curta","TL;DR — instalar gateway dedicado é decisão cara, mantenha a régua curta",[12,3411,3412,3413,3416],{},"Reverse proxy simples cobre o tronco do problema: HTTPS terminado, certificados Let's Encrypt automáticos, roteamento por host e caminho, balanceamento entre back-ends, health check, compressão. Pra um SaaS B2B típico com web app + alguns micro-serviços HTTP, ",[27,3414,3415],{},"isso basta",". Não precisa instalar Kong, não precisa Tyk, não precisa KrakenD.",[12,3418,3419],{},"API gateway dedicado vira investimento defensável quando aparecem três sinais simultâneos: você publica uma API pra terceiros consumirem (não só sua própria web\u002Fmobile), você precisa de limite de requisições por chave de cliente (não por IP), e você quer documentação interativa com tentar-aqui pros consumidores. Nesse cenário, Kong, Tyk ou Traefik com middlewares ricos pagam o próprio custo. Fora desse cenário, você está adicionando 100–300 MB de RAM no caminho crítico, de 1 a 3 milissegundos de latência por requisição, e mais um componente que pode cair em produção — em troca de funcionalidades que ninguém vai usar.",[12,3421,3422,3423,3426],{},"A régua mais simples que conhecemos: ",[27,3424,3425],{},"se o seu cliente final é uma pessoa abrindo um navegador, reverse proxy basta. Se o seu cliente final é um desenvolvedor com chave de API, considere o gateway."," Tudo o mais é variação em cima dessas duas linhas.",[19,3428,3430],{"id":3429},"o-que-reverse-proxy-simples-ja-cobre-e-o-que-ainda-falta","O que reverse proxy simples já cobre, e o que ainda falta",[12,3432,3433],{},"Antes de comparar gateways, vale fixar o piso. Um reverse proxy decente — Caddy, nginx, ou o roteador integrado de um orquestrador moderno — entrega bastante coisa de graça. Esta lista representa o estado da arte em 2026, não o mínimo histórico:",[2735,3435,3436,3442,3448,3465,3471,3477,3483,3489,3495,3501],{},[70,3437,3438,3441],{},[27,3439,3440],{},"Terminação HTTP\u002FHTTPS com HTTP\u002F2 e HTTP\u002F3."," O proxy fala com o cliente em qualquer protocolo moderno e fala com o back-end em HTTP\u002F1.1 limpo se for o caso.",[70,3443,3444,3447],{},[27,3445,3446],{},"Certificados Let's Encrypt automáticos."," Emissão, renovação aos 60 dias, recuperação de erro. Hoje isso é commodity — qualquer roteador sério faz.",[70,3449,3450,2578,3453,3456,3457,3460,3461,3464],{},[27,3451,3452],{},"Roteamento por host e caminho.",[231,3454,3455],{},"api.exemplo.com.br"," vai pra um back-end, ",[231,3458,3459],{},"app.exemplo.com.br"," vai pra outro, ",[231,3462,3463],{},"\u002Fv1\u002Fusuarios"," vai pra um terceiro. Regras com prefixo, regex e prioridade.",[70,3466,3467,3470],{},[27,3468,3469],{},"Balanceamento entre instâncias."," Round-robin, menos conexões, hash por IP. Suficiente pra distribuir carga entre as réplicas de um mesmo serviço.",[70,3472,3473,3476],{},[27,3474,3475],{},"Health check ativo e passivo."," Tira instância doente do pool. Re-inclui quando ela volta.",[70,3478,3479,3482],{},[27,3480,3481],{},"Compressão gzip e brotli."," Negocia com o cliente, comprime o que vale a pena.",[70,3484,3485,3488],{},[27,3486,3487],{},"Cache de conteúdo estático."," Pra arquivos imutáveis, evita ida ao back-end.",[70,3490,3491,3494],{},[27,3492,3493],{},"Limite básico por IP."," Trinta requisições por segundo por endereço, por exemplo. Cobre boa parte de abuso bobo.",[70,3496,3497,3500],{},[27,3498,3499],{},"Timeouts e tentativas."," Falha rápido, tenta de novo num back-end alternativo se for caso.",[70,3502,3503,2578,3506,571,3509,571,3512,3515],{},[27,3504,3505],{},"Cabeçalhos de proxy.",[231,3507,3508],{},"X-Forwarded-For",[231,3510,3511],{},"X-Real-IP",[231,3513,3514],{},"X-Forwarded-Proto",". O back-end enxerga o cliente real.",[12,3517,3518,3519,3521],{},"Isso é muita coisa. Pra 80% das aplicações web de SaaS B2B, é tudo o que se precisa no caminho de entrada. O que reverse proxy simples ",[27,3520,100],{}," cobre é o que diferencia gateway:",[2735,3523,3524,3530,3540,3546,3552,3558,3564,3570,3576],{},[70,3525,3526,3529],{},[27,3527,3528],{},"Validação de chave de API por cliente."," Cada consumidor recebe uma chave, o gateway valida, identifica o cliente, e usa essa identidade pra limites e auditoria.",[70,3531,3532,3535,3536,3539],{},[27,3533,3534],{},"Validação de token JWT com chaves rotacionáveis."," O gateway baixa as chaves públicas do emissor, valida assinatura e tempo, expõe ",[231,3537,3538],{},"claims"," pro back-end.",[70,3541,3542,3545],{},[27,3543,3544],{},"Limite de requisições por chave\u002Fusuário\u002Frota."," Cliente A pode fazer 1.000 chamadas\u002Fhora; cliente B, 100. Por rota, por dia, com janela deslizante. Difícil de fazer em proxy simples.",[70,3547,3548,3551],{},[27,3549,3550],{},"Transformação de requisição e resposta."," Adicionar\u002Fremover cabeçalhos, reescrever corpo JSON, traduzir entre versões da API.",[70,3553,3554,3557],{},[27,3555,3556],{},"Versionamento por cabeçalho ou caminho."," Conviver com clientes em v1 enquanto v2 ganha tração. Política de descontinuação.",[70,3559,3560,3563],{},[27,3561,3562],{},"Agregação de back-ends."," Endpoint composto que chama três micro-serviços e devolve uma resposta unificada (padrão back-end-for-frontend).",[70,3565,3566,3569],{},[27,3567,3568],{},"Validação de esquema da requisição."," Rejeitar no gateway o que não bate com o contrato OpenAPI antes de tocar no back-end.",[70,3571,3572,3575],{},[27,3573,3574],{},"Portal de documentação com tentar-aqui."," Página interativa pra desenvolvedores explorarem a API.",[70,3577,3578,3581],{},[27,3579,3580],{},"Métricas granulares por chave de API."," Quem chamou, quanto, quando, com que latência. Vital se a API é o produto.",[12,3583,3584],{},"Cada item dessa segunda lista é uma feature que custa caro pra fazer em código de aplicação espalhado. Se você precisa da maioria, gateway paga. Se você não precisa de quase nada — o que é o caso comum em SaaS de produto — gateway é peso morto.",[19,3586,3588],{"id":3587},"os-cinco-jogadores-que-importam-em-2026","Os cinco jogadores que importam em 2026",[12,3590,3591],{},"O mercado se assentou. Há cinco escolhas defensáveis pra gateway self-hosted, com perfis razoavelmente distintos. Os números de RAM e latência abaixo são medidos com configuração padrão e workload modesto (algumas dezenas de chamadas por segundo); plug-ins pesados ou volume alto mudam tudo.",[368,3593,3595],{"id":3594},"kong-baseado-em-lua-sobre-openresty","Kong (baseado em Lua, sobre OpenResty)",[12,3597,3598],{},"O nome mais conhecido da categoria. Kong começou em 2015 e tem o maior catálogo de plug-ins do espaço — autenticação OAuth, validação JWT, transformação, log pra Elasticsearch, integração com cofres externos, tudo pré-pronto. A versão de código aberto cobre a maioria dos casos; a paga adiciona portal de desenvolvedor mais polido, RBAC fino, e suporte com SLA.",[12,3600,3601,3604],{},[27,3602,3603],{},"Recursos:"," mínimo realista de 200 MB de RAM por instância, mais o banco de dados se você não usar modo sem-banco. Latência adicionada de 1 a 2 milissegundos por requisição em chamada simples. Plug-ins pesados (validação de esquema com OpenAPI grande, transformação JSON complexa) podem dobrar isso.",[12,3606,3607,3610],{},[27,3608,3609],{},"Quando faz sentido:"," API pública séria com vários consumidores externos, necessidade de plug-ins do catálogo, time disposto a aprender Lua se precisar customizar. Empresa de meio de pagamentos, plataforma de comunicação, qualquer negócio onde a API é o produto vendido.",[12,3612,3613,3616],{},[27,3614,3615],{},"Pegadinha:"," o modo com PostgreSQL coloca o banco no caminho crítico. Banco caiu, gateway não atualiza configuração. Use o modo sem-banco (configuração declarativa via arquivo) sempre que possível — elimina essa dependência.",[368,3618,3620],{"id":3619},"traefik-escrito-em-go-falando-varios-proxies-de-orquestrador","Traefik (escrito em Go, falando vários proxies de orquestrador)",[12,3622,3623],{},"Conhecido como controlador de entrada de Kubernetes, mas tem middlewares ricos suficientes pra cobrir muitos casos de gateway. Limite de requisições por cliente, validação básica de JWT, transformação de cabeçalho, redirecionamentos complexos, autenticação encaminhada (delegando pra serviço externo). Versão paga adiciona plug-ins comerciais e dashboard mais robusto.",[12,3625,3626,3628],{},[27,3627,3603],{}," 50 a 100 MB de RAM, latência adicionada de 0,5 a 1 milissegundo. Discovery automático de back-ends via etiquetas de contêiner é o ponto forte — você não escreve configuração de rota, ela aparece quando o serviço sobe.",[12,3630,3631,3633],{},[27,3632,3609],{}," já está usando Traefik como roteador de entrada e quer evitar adicionar mais um componente; precisa de middlewares razoáveis mas não do catálogo gigante de Kong; valoriza configuração declarativa por etiqueta em vez de banco de dados.",[12,3635,3636,3638],{},[27,3637,3615],{}," alguns padrões avançados (agregação de chamadas, validação OpenAPI completa, portal de documentação interativo) não cabem em Traefik. Se você precisa disso, a tentação de \"esticar\" Traefik via plug-ins customizados leva a complexidade que Kong resolveria mais limpo.",[368,3640,3642],{"id":3641},"tyk-escrito-em-go-foco-em-portal-de-desenvolvedor","Tyk (escrito em Go, foco em portal de desenvolvedor)",[12,3644,3645],{},"A versão de código aberto entrega muito mais do que a maioria — limite de requisições por chave, gerenciamento de chaves, portal de desenvolvedor, todos no plano grátis. A versão paga adiciona painel multi-tenant, replicação multi-região, e suporte.",[12,3647,3648,3650],{},[27,3649,3603],{}," 100 MB de RAM, latência adicionada de 1 a 2 milissegundos. Banco de dados (Redis) é parte central da arquitetura — limite de requisições e contadores vivem ali.",[12,3652,3653,3655],{},[27,3654,3609],{}," API com muitos consumidores externos, portal de desenvolvedor é parte do produto, você quer pagar menos do que Kong cobra pelo equivalente em recursos. Times pequenos publicando API pra parceiros têm encontrado bom encaixe aqui.",[12,3657,3658,3660],{},[27,3659,3615],{}," menos plug-ins prontos do que Kong. Se a sua integração esperada existe na lista de Kong mas não na de Tyk, o trade-off muda.",[368,3662,3664],{"id":3663},"krakend-escrito-em-go-sem-banco-de-dados-foco-em-agregacao","KrakenD (escrito em Go, sem banco de dados, foco em agregação)",[12,3666,3667],{},"KrakenD é o gateway pequeno que se especializa em agregação. Configuração 100% em arquivo, sem estado externo, projetado pra compor endpoints — o cliente faz uma chamada, KrakenD chama três back-ends em paralelo e devolve uma resposta combinada. Ótimo pra padrão back-end-for-frontend.",[12,3669,3670,3672],{},[27,3671,3603],{}," 50 MB de RAM, latência adicionada de 0,5 milissegundo. O mais leve da categoria. Sem banco, sem painel — tudo é arquivo de configuração estático.",[12,3674,3675,3677],{},[27,3676,3609],{}," você tem múltiplos micro-serviços e quer expor uma API mais limpa pro front-end mobile\u002Fweb. Você não precisa de gerenciamento dinâmico de chaves nem portal de desenvolvedor. Você gosta de configuração imutável: muda arquivo, faz deploy novo, pronto.",[12,3679,3680,3682],{},[27,3681,3615],{}," tudo é estático. Adicionar uma chave nova é deploy. Pra time pequeno isso é simplificação; pra plataforma de API com terceiros se cadastrando, vira gargalo.",[368,3684,3686],{"id":3685},"envoy-gateway-cncf-sobre-proxy-envoy","Envoy Gateway (CNCF, sobre proxy Envoy)",[12,3688,3689],{},"O caçula sério da lista. Envoy é o proxy de altíssimo desempenho usado em malhas de serviço grandes. Envoy Gateway é o projeto que empacota Envoy como gateway de API com configuração declarativa. Foco em Kubernetes, alta vazão, integração com malha.",[12,3691,3692,3694],{},[27,3693,3603],{}," Envoy puro consome 50 a 100 MB no proxy de dados; o plano de controle pesa mais. Latência adicionada baixa (\u003C 1 milissegundo) em chamada simples. Mas a complexidade operacional é a mais alta da lista.",[12,3696,3697,3699],{},[27,3698,3609],{}," você já roda malha de serviço com Envoy (Istio, Consul, Linkerd com proxy compatível) e quer consistência de configuração entre malha e gateway. Você opera em escala alta o suficiente pra que a vazão de Envoy importe (dezenas de milhares de requisições por segundo).",[12,3701,3702,3704],{},[27,3703,3615],{}," pra startup com 4 servidores e algumas dezenas de requisições por segundo, Envoy Gateway é overkill por dois ou três tamanhos. A complexidade de configuração não se paga.",[19,3706,3708],{"id":3707},"quando-reverse-proxy-simples-basta","Quando reverse proxy simples basta?",[12,3710,3711],{},"Esta é a pergunta que economiza dinheiro. A resposta honesta é: na grande maioria dos SaaS B2B brasileiros que vemos rodando, basta. Os critérios pra \"basta\":",[2735,3713,3714,3720,3726,3732,3738,3744],{},[70,3715,3716,3719],{},[27,3717,3718],{},"Público da sua API é a sua própria aplicação."," Web, mobile, integrações internas. Não há terceiros desconhecidos chamando endpoints com chaves emitidas por você.",[70,3721,3722,3725],{},[27,3723,3724],{},"Autenticação acontece no aplicativo, não no caminho."," Sessão por cookie, token JWT emitido pelo próprio back-end e validado por middleware da aplicação, OAuth via biblioteca dentro do código. O proxy não precisa enxergar o usuário.",[70,3727,3728,3731],{},[27,3729,3730],{},"Limite de requisições é \"evite abuso bobo\"."," Trinta por segundo por IP, talvez. Não há plano comercial que limite Cliente A a 1.000 chamadas\u002Fdia e Cliente B a 10.000.",[70,3733,3734,3737],{},[27,3735,3736],{},"Você não precisa combinar back-ends."," Cada chamada do front-end vai pra um endpoint, esse endpoint chama o que precisa internamente. Sem agregação no caminho.",[70,3739,3740,3743],{},[27,3741,3742],{},"Documentação da API é interna ou inexistente."," Não há portal de desenvolvedor com tentar-aqui pra terceiros.",[70,3745,3746,3749],{},[27,3747,3748],{},"Versionamento, se existir, é gerenciado em código."," O back-end roteia internamente entre v1 e v2 quando preciso. Não há política formal no gateway.",[12,3751,3752],{},"Se cinco dos seis itens acima são verdade, instalar gateway dedicado é caro pelo benefício real. Reverse proxy embutido no orquestrador, ou Caddy\u002Fnginx standalone, cobre tudo.",[19,3754,3756],{"id":3755},"quando-vale-gateway-dedicado","Quando vale gateway dedicado?",[12,3758,3759],{},"A inversão da lista anterior. Gateway compensa quando aparecem alguns destes:",[2735,3761,3762,3768,3774,3780,3794,3800,3806],{},[70,3763,3764,3767],{},[27,3765,3766],{},"API pública é parte do produto."," Você cobra (ou planeja cobrar) por uso de API. Terceiros se cadastram, recebem chave, consomem.",[70,3769,3770,3773],{},[27,3771,3772],{},"Limite por chave\u002Fusuário\u002Frota é regra de negócio."," Plano gratuito tem teto, plano pago tem teto maior, plano enterprise é negociado. Esse limite precisa viver em algum lugar — gateway é o lugar certo.",[70,3775,3776,3779],{},[27,3777,3778],{},"Múltiplos back-ends precisam ser combinados em uma resposta."," Padrão back-end-for-frontend, agregação de micro-serviços, fan-out e fan-in. Custos altos no aplicativo, custos modestos no gateway.",[70,3781,3782,3785,3786,3789,3790,3793],{},[27,3783,3784],{},"Versionamento formal de API."," Você suporta v1 e v2 simultaneamente, com data de descontinuação anunciada. Cabeçalho ",[231,3787,3788],{},"Accept-Version"," ou caminho ",[231,3791,3792],{},"\u002Fv2\u002F",". Cliente legado não pode quebrar.",[70,3795,3796,3799],{},[27,3797,3798],{},"Autenticação complexa."," Validação de JWT emitido por terceiro, com chaves públicas baixadas e cacheadas, com rotação automática. OAuth com vários provedores. Autenticação por certificado de cliente (TLS mútuo) pra integrações entre empresas.",[70,3801,3802,3805],{},[27,3803,3804],{},"Portal de desenvolvedor com tentar-aqui."," Documentação interativa, gerenciamento de chave self-service, painel de uso pro consumidor.",[70,3807,3808,3811],{},[27,3809,3810],{},"Métricas por chave de API."," Quem chama o quê, quando, latência por consumidor. Dashboards comerciais, relatórios de uso, SLA por cliente.",[12,3813,3814],{},"Três ou mais desses critérios verdadeiros, gateway é defensável. Um ou dois, ainda dá pra resolver de outras formas (autenticação no aplicativo, limite por aplicativo, métricas estruturadas no log).",[19,3816,3818],{"id":3817},"roteador-integrado-do-heroctl-onde-ele-fica-nessa-regua","Roteador integrado do HeroCtl — onde ele fica nessa régua",[12,3820,3821],{},"O roteador embutido no HeroCtl não tenta ser gateway. Cobre o lado de reverse proxy bem feito: HTTPS terminado, Let's Encrypt automático com renovação, roteamento por host e caminho, balanceamento entre as réplicas que o orquestrador subiu, health check coordenado com o agente em cada nó, compressão, cabeçalhos de proxy, limite básico por IP, política de tentativa em caso de falha de back-end.",[12,3823,3824,3825,3827],{},"O que o roteador integrado ",[27,3826,100],{}," faz: validação de chave de API por consumidor, limite por chave\u002Fusuário, transformação de corpo, agregação de back-ends, validação de esquema OpenAPI, portal de desenvolvedor. Pra os 80% dos casos onde o cliente final é navegador ou app mobile da própria empresa, o roteador embutido cobre o caminho de entrada inteiro — você não instala mais nada na frente.",[12,3829,3830],{},"Pros 20% que precisam de gateway dedicado, o caminho é direto: instale Kong, Traefik standalone, Tyk ou KrakenD como mais um job no cluster, atrás do roteador embutido. O roteador termina TLS na borda, o gateway faz o trabalho de gateway, os back-ends ficam atrás. Sem cerimônia, sem dependência circular.",[12,3832,3833],{},"Plano de controle do HeroCtl ocupa entre 200 e 400 MB por servidor — ou seja, um Kong instalado adiciona praticamente o mesmo peso do plano de controle inteiro. Vale lembrar a ordem de grandeza antes de \"só instalar\".",[19,3835,3837],{"id":3836},"tabela-comparativa-12-criterios","Tabela comparativa — 12 critérios",[119,3839,3840,3867],{},[122,3841,3842],{},[125,3843,3844,3846,3849,3852,3855,3858,3861,3864],{},[128,3845,2983],{},[128,3847,3848],{},"Reverse proxy simples (Caddy\u002Fnginx)",[128,3850,3851],{},"Roteador HeroCtl",[128,3853,3854],{},"Traefik standalone",[128,3856,3857],{},"KrakenD",[128,3859,3860],{},"Tyk OSS",[128,3862,3863],{},"Kong OSS",[128,3865,3866],{},"Envoy Gateway",[141,3868,3869,3895,3919,3940,3959,3978,3997,4017,4036,4057,4078,4099],{},[125,3870,3871,3874,3877,3880,3883,3886,3889,3892],{},[146,3872,3873],{},"RAM mínima",[146,3875,3876],{},"20–50 MB",[146,3878,3879],{},"embutido no plano de controle",[146,3881,3882],{},"50–100 MB",[146,3884,3885],{},"~50 MB",[146,3887,3888],{},"~100 MB",[146,3890,3891],{},"~200 MB",[146,3893,3894],{},"~100 MB + plano de controle",[125,3896,3897,3900,3903,3905,3908,3911,3914,3916],{},[146,3898,3899],{},"Latência adicionada",[146,3901,3902],{},"\u003C 0,5 ms",[146,3904,3902],{},[146,3906,3907],{},"0,5–1 ms",[146,3909,3910],{},"~0,5 ms",[146,3912,3913],{},"1–2 ms",[146,3915,3913],{},[146,3917,3918],{},"\u003C 1 ms (pode crescer)",[125,3920,3921,3924,3927,3929,3931,3934,3936,3938],{},[146,3922,3923],{},"Certificados automáticos",[146,3925,3926],{},"Sim (Caddy nativo)",[146,3928,3065],{},[146,3930,3065],{},[146,3932,3933],{},"Não direto",[146,3935,3065],{},[146,3937,3065],{},[146,3939,3065],{},[125,3941,3942,3945,3947,3949,3951,3953,3955,3957],{},[146,3943,3944],{},"Roteamento host\u002Fcaminho",[146,3946,3065],{},[146,3948,3065],{},[146,3950,3065],{},[146,3952,3065],{},[146,3954,3065],{},[146,3956,3065],{},[146,3958,3065],{},[125,3960,3961,3964,3966,3968,3970,3972,3974,3976],{},[146,3962,3963],{},"Balanceamento + health",[146,3965,3065],{},[146,3967,3065],{},[146,3969,3065],{},[146,3971,3065],{},[146,3973,3065],{},[146,3975,3065],{},[146,3977,3065],{},[125,3979,3980,3983,3985,3987,3989,3991,3993,3995],{},[146,3981,3982],{},"Limite por IP",[146,3984,3065],{},[146,3986,3065],{},[146,3988,3065],{},[146,3990,3065],{},[146,3992,3065],{},[146,3994,3065],{},[146,3996,3065],{},[125,3998,3999,4002,4004,4006,4009,4011,4013,4015],{},[146,4000,4001],{},"Limite por chave\u002Fusuário",[146,4003,3059],{},[146,4005,3059],{},[146,4007,4008],{},"Sim (com middleware)",[146,4010,3065],{},[146,4012,3065],{},[146,4014,3065],{},[146,4016,3065],{},[125,4018,4019,4022,4024,4026,4028,4030,4032,4034],{},[146,4020,4021],{},"Validação de JWT",[146,4023,3059],{},[146,4025,3059],{},[146,4027,3140],{},[146,4029,3065],{},[146,4031,3065],{},[146,4033,3065],{},[146,4035,3065],{},[125,4037,4038,4041,4043,4045,4047,4050,4052,4055],{},[146,4039,4040],{},"Agregação de back-ends",[146,4042,3059],{},[146,4044,3059],{},[146,4046,3059],{},[146,4048,4049],{},"Sim (foco)",[146,4051,3140],{},[146,4053,4054],{},"Sim (com plug-in)",[146,4056,3065],{},[125,4058,4059,4062,4064,4066,4068,4071,4073,4076],{},[146,4060,4061],{},"Validação OpenAPI",[146,4063,3059],{},[146,4065,3059],{},[146,4067,3059],{},[146,4069,4070],{},"Sim (assinante)",[146,4072,3065],{},[146,4074,4075],{},"Sim (plug-in)",[146,4077,3065],{},[125,4079,4080,4083,4085,4087,4089,4091,4094,4097],{},[146,4081,4082],{},"Portal de desenvolvedor",[146,4084,3059],{},[146,4086,3059],{},[146,4088,3059],{},[146,4090,3059],{},[146,4092,4093],{},"Sim (incluso)",[146,4095,4096],{},"Sim (pago no OSS robusto)",[146,4098,3059],{},[125,4100,4101,4104,4107,4110,4113,4115,4118,4121],{},[146,4102,4103],{},"Configuração",[146,4105,4106],{},"Arquivo",[146,4108,4109],{},"Painel + API",[146,4111,4112],{},"Etiquetas\u002Farquivo",[146,4114,4106],{},[146,4116,4117],{},"Arquivo + painel",[146,4119,4120],{},"Arquivo + painel + banco",[146,4122,4123],{},"Custom Resources",[12,4125,4126,4127,4130],{},"A tabela tem zonas claras. As três primeiras colunas resolvem o caminho de entrada com peso baixo. As quatro últimas resolvem caminho de entrada ",[27,4128,4129],{},"mais"," trabalho de gateway, com peso e complexidade crescentes.",[19,4132,4134],{"id":4133},"stack-tipica-por-estagio-da-empresa","Stack típica por estágio da empresa",[12,4136,4137],{},"Esta é a régua que recomendamos. Não é prescrição rígida — é o que vemos funcionar em times brasileiros de SaaS.",[12,4139,4140,4143],{},[27,4141,4142],{},"MVP (1 back-end, 1 desenvolvedor)."," Caddy standalone num servidor, ou roteador embutido se você já está em orquestrador. Não instala nada. Não pensa em gateway. Foca no produto.",[12,4145,4146,4149],{},[27,4147,4148],{},"Indie hacker (3 a 5 back-ends, time de 1 a 3)."," Roteador embutido no orquestrador, ponto. O caminho de entrada já cobre o que importa. Autenticação no aplicativo, limite básico por IP no proxy. Tempo gasto com gateway é tempo não gasto em feature do produto.",[12,4151,4152,4155],{},[27,4153,4154],{},"Startup early (10 a 20 back-ends, primeiros consumidores externos da API)."," Hora de avaliar. Se a API externa é experimento que ainda pode morrer, deixe a autenticação no aplicativo e limite por chave numa biblioteca compartilhada. Se a API é parte central da promessa do produto, instale Traefik standalone com middlewares de autenticação e limite, ou Tyk OSS pelo portal incluso. Kong nesta fase costuma ser pesado demais.",[12,4157,4158,4161],{},[27,4159,4160],{},"Startup mid (50+ back-ends, plataforma de API pública vira produto)."," Kong OSS ou Tyk pago. Você precisa de plug-ins, portal robusto, gerenciamento de chave self-service, métricas comerciais. O peso de Kong agora se justifica — você está cobrando por uso de API e o gateway é receita, não custo.",[12,4163,4164,4167],{},[27,4165,4166],{},"Empresa grande (centenas de serviços, integrações com parceiros sérios)."," Kong Enterprise ou Envoy Gateway, dependendo do contexto. Equipe dedicada cuidando do gateway. Política formal de versionamento, descontinuação, SLA por cliente.",[12,4169,4170],{},"A migração natural — reverse proxy → Traefik\u002FTyk → Kong — funciona porque cada degrau resolve dor real do degrau anterior. Pular degraus é caro: instalar Kong na fase de MVP é colocar um caminhão pra entregar uma pizza.",[19,4172,4174],{"id":4173},"os-4-erros-caros-mais-comuns-com-gateway","Os 4 erros caros mais comuns com gateway",[12,4176,4177],{},"Os tropeços que vemos em produção:",[12,4179,4180,4183],{},[27,4181,4182],{},"Instalar Kong com PostgreSQL no caminho crítico sem precisar."," O modo sem-banco existe e é perfeito pra maioria dos casos. Configuração declarativa via arquivo, sem dependência externa, sem ponto extra de falha. Muitos times caem na configuração padrão com banco e descobrem isso só quando o banco fica indisponível e o gateway não consegue mais propagar mudanças. Se você precisa de gerenciamento dinâmico de chaves (consumidores se cadastrando self-service), banco compensa. Senão, modo sem-banco.",[12,4185,4186,4189],{},[27,4187,4188],{},"Não monitorar o gateway com a mesma severidade dos back-ends."," Gateway na frente vira caixa-preta de fácil esquecimento. Latência cresce 5 ms, taxa de erro vai de 0,01% pra 0,5%, ninguém percebe até cliente reclamar. Métricas de gateway (latência por rota, taxa de erro 4xx\u002F5xx, uso de memória, lag de propagação de configuração) merecem dashboard próprio e alertas, não só inclusão tímida no painel geral.",[12,4191,4192,4195],{},[27,4193,4194],{},"Plug-in customizado em Lua\u002FJS rodando em produção."," Kong permite plug-in customizado em Lua. Tyk em JavaScript. Tentação enorme pra resolver \"só essa transformação\" no gateway. Bug nesse plug-in derruba o gateway inteiro — bug que você criou, sem teste de carga, no caminho crítico de tudo. Se você precisa de transformação custom, faça em micro-serviço atrás do gateway. Plug-in customizado só com revisão séria, teste de carga, e plano de rollback automático.",[12,4197,4198,4201],{},[27,4199,4200],{},"Versão do gateway desatualizada."," Kong, Envoy e Tyk recebem CVEs (vulnerabilidades de segurança) com regularidade. Gateway é exposto à internet — superfície de ataque relevante. Versão de 18 meses atrás é vulnerabilidade conhecida acumulando. Faça parte do ciclo de manutenção: atualizar o gateway é tão importante quanto atualizar o sistema operacional.",[19,4203,4205],{"id":4204},"cenarios-reais-em-que-nao-instalar-gateway","Cenários reais em que NÃO instalar gateway",[12,4207,4208],{},"Lista forte. Se você está em algum destes, evite o gateway dedicado mesmo que o tópico apareça no conselho:",[2735,4210,4211,4217,4223,4229,4235],{},[70,4212,4213,4216],{},[27,4214,4215],{},"SaaS web com 5 endpoints, sem API externa pública."," Cliente final é o navegador. Autenticação por sessão. Reverse proxy resolve o caminho de entrada inteiro. Adicionar gateway aqui é vaidade arquitetural.",[70,4218,4219,4222],{},[27,4220,4221],{},"Time pequeno (1 a 3 desenvolvedores)."," Curva de aprendizado de Kong custa duas a quatro semanas de produtividade total do time. Em time de 3 pessoas, isso é trimestre de feature parado. A não ser que o gateway resolva dor concreta hoje, adia.",[70,4224,4225,4228],{},[27,4226,4227],{},"Workload onde latência sub-10ms é requisito duro."," Trading de baixa latência, leilão em tempo real, jogo multiplayer. Cada milissegundo conta. Gateway adiciona 1 a 3 ms — em workload sensível, é caro. Coloque a inteligência no aplicativo.",[70,4230,4231,4234],{},[27,4232,4233],{},"Aplicação monolítica sem agregação."," O monolito atende o front-end direto, sem composição entre serviços. Não há o que agregar. Gateway é solução procurando problema.",[70,4236,4237,4240],{},[27,4238,4239],{},"Compliance que prefere superfície de ataque mínima."," Cada componente exposto à internet é mais um item pra auditoria, mais um patch a aplicar, mais um log a guardar. Se a auditoria valoriza minimalismo, justifique cada componente — gateway que não cobre dor concreta é minus.",[19,4242,4244],{"id":4243},"perguntas-frequentes","Perguntas frequentes",[12,4246,4247,4250,4251,4254],{},[27,4248,4249],{},"Kong sem banco é estável em 2026?","\nSim. O modo declarativo (",[231,4252,4253],{},"db_less = on",") é maduro, recomendado pelo próprio Kong pra grande parte dos casos, e elimina o PostgreSQL como dependência. Você perde gerenciamento dinâmico de chave via API admin (precisa fazer deploy de configuração nova), mas ganha simplicidade operacional enorme. Pra time pequeno, troca quase sempre vale.",[12,4256,4257,4260],{},[27,4258,4259],{},"Traefik faz tudo que Kong faz?","\nNão. Traefik com middlewares cobre a maioria dos casos comuns — autenticação básica, limite por chave simples, transformação de cabeçalho, encaminhamento de autenticação. Não cobre catálogo de plug-ins de Kong, validação OpenAPI nativa, portal de desenvolvedor robusto, integrações comerciais prontas. Se a sua dor cabe em middleware de Traefik, fica em Traefik (mais leve, mais simples). Se você precisa de coisa do catálogo de Kong, Kong.",[12,4262,4263,4266],{},[27,4264,4265],{},"Posso ter dois gateways em série?","\nTecnicamente sim, na prática quase sempre é sintoma de organização confusa. Dois gateways = duas configurações pra manter, duas latências somadas, dois pontos de falha. O caso defensável é: roteador de borda fazendo TLS e roteamento básico, gateway dedicado atrás fazendo trabalho específico (validação de chave, agregação). Isso é diferente de \"dois gateways completos em série\" — é divisão de responsabilidade.",[12,4268,4269,4272,4273,101],{},[27,4270,4271],{},"API gateway substitui malha de serviço?","\nNão. Gateway cuida do tráfego norte-sul (cliente externo → seu sistema). Malha cuida do tráfego leste-oeste (serviço interno → serviço interno). Funções parecidas (autenticação, limite, observabilidade) mas escopo diferente. Pra startup média, gateway resolve a parte que importa; malha de serviço completa só vira investimento defensável em escala maior. Tratamos esse limite em ",[3337,4274,4276],{"href":4275},"\u002Fblog\u002Fservice-mesh-quando-vale-pra-saas-pequeno-medio","service mesh: quando vale pra SaaS pequeno e médio",[12,4278,4279,4282],{},[27,4280,4281],{},"Quanta latência Kong adiciona em chamada típica?","\nEm hardware moderno, com configuração padrão e plug-ins leves (validação de chave + limite por chave): 1 a 2 milissegundos por requisição. Plug-ins pesados (validação OpenAPI completa em payload grande, transformação JSON complexa, log síncrono pra serviço externo) podem somar 3 a 10 ms. Meça antes e depois — não confie em número de blog post genérico.",[12,4284,4285,4288],{},[27,4286,4287],{},"Provedor de OAuth self-hosted — Keycloak ou Hydra?","\nKeycloak é o padrão pra quem quer painel de administração robusto, federação com LDAP\u002FSAML, gerenciamento de usuário completo. Pesa mais (1 GB de RAM mínimo, JVM). Hydra é minimalista, foca só em OAuth\u002FOIDC, sem gerenciamento de usuário (você integra com seu sistema de usuários existente). Pra time pequeno que já tem sistema de usuário próprio, Hydra é mais adequado. Pra empresa que quer um lugar único pra identidade, Keycloak. Os dois falam protocolos padrão, então o gateway não diferencia entre eles.",[12,4290,4291,4294],{},[27,4292,4293],{},"Validação de esquema — OpenAPI ou JSON Schema?","\nOpenAPI (anteriormente Swagger) é o padrão pra descrever API HTTP — engloba caminhos, métodos, requisição e resposta. Inclui JSON Schema pra descrever payloads. Kong, Tyk e validadores standalone falam OpenAPI direto. JSON Schema puro é mais portável (não amarrado a HTTP) mas exige mais cola. Use OpenAPI quando o gateway suportar; vale ter o esquema do contrato vivo, não desatualizado.",[12,4296,4297,4300,4301,4304,4305,4308],{},[27,4298,4299],{},"Posso fazer limite no aplicativo em vez de gateway?","\nPode. Bibliotecas como ",[231,4302,4303],{},"golang.org\u002Fx\u002Ftime\u002Frate"," ou Redis com ",[231,4306,4307],{},"INCR"," resolvem limite por usuário no nível da aplicação. A questão é onde o limite é mais barato: no gateway, antes do back-end ser tocado (economiza recursos do back-end, aplica antes do trabalho começar) ou no aplicativo, com regra de negócio mais perto do código (mais fácil de raciocinar, mais fácil de testar). Pra limite simples, aplicativo basta. Pra limite por plano comercial com múltiplos níveis e auditoria, gateway é o lugar certo.",[12,4310,4311,4314],{},[27,4312,4313],{},"Posso usar dois gateways diferentes em rotas distintas?","\nPode. Algumas empresas rodam Kong pra rotas \"produto\" (API pública vendida) e Traefik pra rotas \"interno\" (admin, ops, cron). A justificativa é que cada gateway resolve uma dor diferente, e ter um só forçaria comprometimento. Vale quando os perfis de uso de fato divergem. Não vale só pelo prazer de ter variedade — duas peças pra manter.",[19,4316,4318],{"id":4317},"fechamento-comece-pelo-minimo-suba-quando-a-dor-for-real","Fechamento — comece pelo mínimo, suba quando a dor for real",[12,4320,4321],{},"A cilada da categoria \"API gateway\" é tratar a decisão como binária — instalar ou não — quando ela é gradativa. Reverse proxy bem feito cobre 80% das aplicações. Roteador integrado no orquestrador cobre as mesmas 80% sem componente separado. Gateway dedicado é investimento defensável quando aparecem três ou quatro sinais simultâneos: API externa pública, limite por chave, agregação, portal de desenvolvedor.",[12,4323,4324],{},"A régua honesta: instale o mínimo até a dor concreta forçar o próximo degrau. Pular degraus custa caro em latência, RAM, complexidade, área de falha. Time pequeno que instala Kong \"porque vai precisar\" passa três semanas configurando algo que não usa, e ainda assim tem mais um componente pra monitorar.",[12,4326,4327],{},"HeroCtl entrega o degrau mais baixo embutido — roteador integrado com TLS automático, balanceamento, health check, limite por IP. Quando a dor de gateway aparecer pra valer, você sobe Kong, Traefik standalone, Tyk ou KrakenD como mais um job no cluster. Sem migração dolorosa, sem cerimônia.",[12,4329,4330],{},"Pra subir um cluster e testar:",[224,4332,4333],{"className":226,"code":2949,"language":228,"meta":229,"style":229},[231,4334,4335],{"__ignoreMap":229},[234,4336,4337,4339,4341,4343,4345],{"class":236,"line":237},[234,4338,1220],{"class":247},[234,4340,2958],{"class":251},[234,4342,2961],{"class":255},[234,4344,2964],{"class":383},[234,4346,2967],{"class":247},[12,4348,4349,4352,4353,4356,4357,4360],{},[27,4350,4351],{},"Community"," é gratuito pra sempre, sem teto de servidores, sem teto de jobs, sem feature gate. ",[27,4354,4355],{},"Business"," adiciona SSO\u002FSAML, RBAC granular, auditoria detalhada e suporte com SLA. ",[27,4358,4359],{},"Enterprise"," acrescenta escrow de código-fonte, contrato de continuidade e suporte 24×7.",[12,4362,4363,4364,2403,4366,4370],{},"Posts próximos: ",[3337,4365,4276],{"href":4275},[3337,4367,4369],{"href":4368},"\u002Fblog\u002Fmulti-tenant-saas-isolamento-real","multi-tenant SaaS: isolamento real entre clientes",". Os três tópicos juntos cobrem a maior parte das decisões de plataforma pra startup brasileira na faixa de 1 a 500 servidores.",[12,4372,4373],{},"Orquestração de contêineres, sem cerimônia. Gateway só quando a dor pedir.",[3351,4375,4376],{},"html pre.shiki code .sQhOw, html code.shiki .sQhOw{--shiki-default:#FFA657}html pre.shiki code .sFSAA, html code.shiki .sFSAA{--shiki-default:#79C0FF}html pre.shiki code .s9uIt, html code.shiki .s9uIt{--shiki-default:#A5D6FF}html pre.shiki code .suJrU, html code.shiki .suJrU{--shiki-default:#FF7B72}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);}",{"title":229,"searchDepth":244,"depth":244,"links":4378},[4379,4380,4381,4388,4389,4390,4391,4392,4393,4394,4395,4396],{"id":3408,"depth":244,"text":3409},{"id":3429,"depth":244,"text":3430},{"id":3587,"depth":244,"text":3588,"children":4382},[4383,4384,4385,4386,4387],{"id":3594,"depth":271,"text":3595},{"id":3619,"depth":271,"text":3620},{"id":3641,"depth":271,"text":3642},{"id":3663,"depth":271,"text":3664},{"id":3685,"depth":271,"text":3686},{"id":3707,"depth":244,"text":3708},{"id":3755,"depth":244,"text":3756},{"id":3817,"depth":244,"text":3818},{"id":3836,"depth":244,"text":3837},{"id":4133,"depth":244,"text":4134},{"id":4173,"depth":244,"text":4174},{"id":4204,"depth":244,"text":4205},{"id":4243,"depth":244,"text":4244},{"id":4317,"depth":244,"text":4318},"2026-06-03","API gateway resolve auth, rate limit, transformações e observabilidade — em troca de mais um componente crítico. Quando reverse proxy simples basta vs quando vale o gateway dedicado.",{},"\u002Fblog\u002Fapi-gateway-self-hosted-traefik-kong-quando-instalar","13 min",{"title":3397,"description":4398},{"loc":4400},"blog\u002Fapi-gateway-self-hosted-traefik-kong-quando-instalar",[4406,4407,4408,3379,4409],"api-gateway","kong","traefik","arquitetura","kBrp5dr1WQbf0cuzoPbd3WN6wGW7e6FTXkQ0XgGvTQs",{"id":4412,"title":4413,"author":7,"body":4414,"category":3379,"cover":3380,"date":5370,"description":5371,"draft":3383,"extension":3384,"lastReviewed":3380,"meta":5372,"navigation":411,"path":4275,"readingTime":4401,"seo":5373,"sitemap":5374,"stem":5375,"tags":5376,"__hash__":5380},"blog_pt\u002Fblog\u002Fservice-mesh-quando-vale-pra-saas-pequeno-medio.md","Service mesh é overkill pra startup brasileira? Quando vale instalar Istio\u002FLinkerd",{"type":9,"value":4415,"toc":5352},[4416,4419,4423,4426,4429,4433,4436,4526,4529,4533,4536,4577,4580,4584,4587,4621,4624,4628,4631,4707,4710,4736,4739,4743,4746,4763,4766,4773,4777,4780,4783,4803,4806,4820,4823,4827,4830,5028,5035,5039,5042,5068,5071,5075,5078,5110,5114,5117,5143,5146,5150,5153,5173,5176,5180,5183,5203,5206,5210,5213,5250,5252,5258,5267,5273,5279,5285,5291,5297,5303,5309,5311,5314,5317,5335,5347,5350],[12,4417,4418],{},"A pergunta chega sempre no mesmo formato. Um tech lead de uma SaaS brasileira com seis ou oito serviços rodando lê três posts em inglês sobre malha de serviço, vê toda a indústria americana usando Istio, e abre o terminal pra instalar — junto com a dúvida: \"isso aqui não é demais pro tamanho da minha empresa?\". Provavelmente é. Mas a resposta honesta exige separar quatro problemas que a malha de serviço resolve, mostrar o custo em RAM e CPU por servidor, e descrever o ponto exato em que o benefício passa a superar o overhead.",[19,4420,4422],{"id":4421},"tldr-service-mesh-vale-pra-startup-pequenamedia","TL;DR — Service mesh vale pra startup pequena\u002Fmédia?",[12,4424,4425],{},"Malha de serviço (Istio, Linkerd, Cilium Service Mesh, Consul Connect) resolve quatro problemas reais entre serviços de uma arquitetura de microserviços: criptografia automática (chamada entre pods sem TLS por default vaza tráfego em texto puro), retries e circuit breakers (resiliência configurável), observabilidade granular (qual serviço chama qual, com qual latência), e traffic shaping para canary releases. Em troca, adiciona um proxy paralelo em cada pod (normalmente Envoy) que consome entre 50 e 100 MB de RAM e adiciona 5 a 10 ms de latência por chamada interna.",[12,4427,4428],{},"Pra startup com menos de dez serviços ativos e menos de cinquenta pods, malha de serviço é overkill — o overhead operacional supera o benefício, e o time gasta semanas estudando uma camada que resolve um problema que ainda não tem. Pra empresa com cinquenta ou mais microserviços onde diagnosticar \"qual serviço está atrasando a chamada?\" leva horas, malha paga em produtividade. O meio-termo são clusters com criptografia entre serviços embutida no próprio plano de controle — cobrem cerca de 60% do que malha oferece sem o sidecar paralelo, e atendem a maioria dos casos brasileiros até a faixa dos trinta serviços.",[19,4430,4432],{"id":4431},"o-que-service-mesh-resolve-em-uma-frase","O que service mesh resolve, em uma frase",[12,4434,4435],{},"Antes de discutir custo, é preciso ser claro sobre o que está sendo comprado. Malha de serviço é uma camada de rede que se intromete em cada chamada entre serviços e adiciona seis comportamentos:",[2735,4437,4438,4451,4463,4475,4502,4511],{},[70,4439,4440,4443,4444,2630,4447,4450],{},[27,4441,4442],{},"Criptografia automática entre pods."," Sem malha, uma chamada de ",[231,4445,4446],{},"pedidos",[231,4448,4449],{},"usuarios"," dentro do cluster trafega em HTTP simples. Qualquer agente com acesso à rede do nó vê o conteúdo. Com malha, cada chamada é cifrada com certificados emitidos automaticamente, sem alteração no código da aplicação.",[70,4452,4453,4456,4457,4459,4460,4462],{},[27,4454,4455],{},"Retries automáticos em chamadas internas."," Quando ",[231,4458,4446],{}," chama ",[231,4461,4449],{}," e a primeira tentativa falha por uma flap de rede de 200 ms, a malha reenvia. Sem malha, a aplicação precisa implementar essa lógica em cada cliente HTTP que ela cria.",[70,4464,4465,4468,4469,4471,4472,4474],{},[27,4466,4467],{},"Circuit breakers configuráveis."," Se ",[231,4470,4449],{}," começa a responder com latência de cinco segundos, a malha abre o circuito e faz ",[231,4473,4446],{}," falhar rápido em vez de empilhar conexões. Sem malha, o time precisa adicionar uma biblioteca em cada serviço.",[70,4476,4477,4480,4481,571,4484,4487,4488,4490,4491,4493,4494,2403,4496,4499,4500,101],{},[27,4478,4479],{},"Distributed tracing automático."," A malha propaga cabeçalhos de correlação (",[231,4482,4483],{},"x-request-id",[231,4485,4486],{},"traceparent",") por toda a cadeia de chamadas. O time consegue ver, num painel, que uma requisição entrou no ",[231,4489,4406],{},", passou por ",[231,4492,4446],{},", chamou ",[231,4495,4449],{},[231,4497,4498],{},"estoque",", e gastou a maior parte do tempo no ",[231,4501,4498],{},[70,4503,4504,4507,4508,4510],{},[27,4505,4506],{},"Traffic shaping fino."," Encaminhar 5% do tráfego de ",[231,4509,4446],{}," pra uma versão nova (canary), espelhar 100% pra uma versão de teste sem afetar o cliente (mirror), ou alternar entre duas versões inteiras (blue-green) — tudo configurado declarativamente, sem código.",[70,4512,4513,4516,4517,2403,4519,4522,4523,4525],{},[27,4514,4515],{},"Authorization policies entre serviços."," Declarar que apenas ",[231,4518,4446],{},[231,4520,4521],{},"relatorios"," podem chamar ",[231,4524,4449],{},", e qualquer outro serviço recebe 403. É a base da chamada \"rede zero-trust\" entre pods.",[12,4527,4528],{},"Esses seis comportamentos são reais e o valor é mensurável. A pergunta é se o seu cluster de hoje tem volume e complexidade suficientes pra justificar pagar por eles.",[19,4530,4532],{"id":4531},"o-que-nao-e-problema-de-service-mesh","O que NÃO é problema de service mesh",[12,4534,4535],{},"Antes de avançar, vale eliminar quatro problemas que muito time confunde com motivo pra instalar malha — e que orquestrador moderno já resolve sozinho:",[2735,4537,4538,4552,4558,4567],{},[70,4539,4540,4543,4544,4547,4548,4551],{},[27,4541,4542],{},"Roteamento de entrada (ingress HTTP)."," Receber tráfego externo, terminar TLS, rotear ",[231,4545,4546],{},"api.exemplo.com"," pra um serviço e ",[231,4549,4550],{},"app.exemplo.com"," pra outro. Isso é trabalho de roteador integrado ao orquestrador, não de malha.",[70,4553,4554,4557],{},[27,4555,4556],{},"Balanceamento de carga simples."," Distribuir requisições entre três réplicas de um mesmo serviço com round-robin. Orquestrador faz isso com DNS interno e health checks. Malha agrega só quando a política de balanceamento precisa ser sofisticada (peso por região, sticky sessions complexas).",[70,4559,4560,4563,4564,4566],{},[27,4561,4562],{},"Service discovery."," Encontrar onde ",[231,4565,4449],{}," está rodando. DNS interno do cluster resolve. Malha não traz nada novo aqui.",[70,4568,4569,4572,4573,4576],{},[27,4570,4571],{},"Terminação HTTP\u002FHTTPS na borda."," Ingress controller resolve. Malha cuida do tráfego ",[179,4574,4575],{},"entre"," serviços, não da entrada.",[12,4578,4579],{},"Quem instala malha esperando que ela resolva esses quatro está pagando duas vezes pelo mesmo trabalho.",[19,4581,4583],{"id":4582},"os-quatro-jogadores-principais","Os quatro jogadores principais",[12,4585,4586],{},"Quatro produtos dominam essa categoria em 2026. As diferenças importam quando o tradeoff é overhead vs features.",[2735,4588,4589,4599,4609,4615],{},[70,4590,4591,4594,4595,4598],{},[27,4592,4593],{},"Istio."," O mais antigo, mais completo, mais documentado — e mais pesado. Usa Envoy como sidecar em cada pod. Padrão de fato em empresas grandes que adotaram malha entre 2019 e 2022. A versão Ambient Mode (sem sidecar, com ",[231,4596,4597],{},"ztunnel"," por nó) reduz overhead, mas ainda está estabilizando em produção.",[70,4600,4601,4604,4605,4608],{},[27,4602,4603],{},"Linkerd."," Foco em simplicidade. Proxy próprio escrito em Rust (",[231,4606,4607],{},"linkerd2-proxy","), bem mais leve que Envoy. Curva de aprendizado curta — instalação cabe num par de comandos. CNCF graduado, mas com comunidade menor que Istio.",[70,4610,4611,4614],{},[27,4612,4613],{},"Cilium Service Mesh."," Aproveita eBPF no kernel pra implementar boa parte da malha sem sidecar. Overhead por pod beira zero. Em troca, o setup do cluster precisa de kernel recente e CNI compatível, e algumas features avançadas (como autorização L7 sofisticada) ainda dependem de proxy auxiliar.",[70,4616,4617,4620],{},[27,4618,4619],{},"Consul Connect."," Da Hashicorp. Integra com o cofre de segredos da própria casa, e funciona bem em ambientes mistos (VMs + contêineres). Comunidade brasileira menor que Istio\u002FLinkerd.",[12,4622,4623],{},"Existem outros (Kuma, Open Service Mesh, AWS App Mesh), mas concentrar no quarteto acima cobre 95% das decisões reais que um tech lead brasileiro vai enfrentar.",[19,4625,4627],{"id":4626},"quanto-custa-em-ram-e-cpu","Quanto custa em RAM e CPU?",[12,4629,4630],{},"A pergunta que decide a discussão.",[119,4632,4633,4649],{},[122,4634,4635],{},[125,4636,4637,4640,4643,4646],{},[128,4638,4639],{},"Malha",[128,4641,4642],{},"RAM por pod",[128,4644,4645],{},"CPU por pod",[128,4647,4648],{},"Latência adicional",[141,4650,4651,4665,4679,4693],{},[125,4652,4653,4656,4659,4662],{},[146,4654,4655],{},"Istio (Envoy sidecar)",[146,4657,4658],{},"+80–120 MB",[146,4660,4661],{},"+10–15%",[146,4663,4664],{},"5–10 ms",[125,4666,4667,4670,4673,4676],{},[146,4668,4669],{},"Linkerd (linkerd2-proxy Rust)",[146,4671,4672],{},"+20–40 MB",[146,4674,4675],{},"+3–6%",[146,4677,4678],{},"1–3 ms",[125,4680,4681,4684,4687,4690],{},[146,4682,4683],{},"Cilium Service Mesh (eBPF)",[146,4685,4686],{},"~0 MB por pod",[146,4688,4689],{},"~2% no nó",[146,4691,4692],{},"\u003C1 ms",[125,4694,4695,4698,4701,4704],{},[146,4696,4697],{},"Consul Connect (Envoy sidecar)",[146,4699,4700],{},"+70–110 MB",[146,4702,4703],{},"+8–12%",[146,4705,4706],{},"4–8 ms",[12,4708,4709],{},"Em cluster com cem pods ativos:",[2735,4711,4712,4718,4724,4730],{},[70,4713,4714,4717],{},[27,4715,4716],{},"Istio"," consome cerca de 10 GB de RAM apenas em proxies paralelos, antes de qualquer aplicação.",[70,4719,4720,4723],{},[27,4721,4722],{},"Linkerd"," consome cerca de 3 GB.",[70,4725,4726,4729],{},[27,4727,4728],{},"Cilium"," consome quase nada por pod, mas exige um agente por nó (cerca de 200–400 MB cada).",[70,4731,4732,4735],{},[27,4733,4734],{},"Consul Connect"," fica próximo de Istio.",[12,4737,4738],{},"Pra cluster brasileiro típico de startup — quatro servidores com 4 GB de RAM cada, totalizando 16 GB — Istio sozinho ocupa um terço da memória do cluster antes de qualquer linha de código rodar. Linkerd ocupa um quinto. Cilium ocupa quase nada por pod, mas exige planejamento de CNI.",[19,4740,4742],{"id":4741},"minha-startup-precisa-disso","Minha startup precisa disso?",[12,4744,4745],{},"Resposta direta: provavelmente não. Os critérios honestos pra \"precisa\":",[2735,4747,4748,4751,4754,4757,4760],{},[70,4749,4750],{},"Trinta ou mais microserviços ativos em produção.",[70,4752,4753],{},"Tráfego entre serviços é mais de 50% do volume HTTP total do cluster.",[70,4755,4756],{},"Mais de um incidente por mês relacionado a \"qual serviço caiu, atrasou ou está estourando timeout\".",[70,4758,4759],{},"Compliance formal exige rede zero-trust entre pods (PCI-DSS nível 1, certos contratos com Banco Central, frameworks de saúde).",[70,4761,4762],{},"Time tem pelo menos uma pessoa dedicada à plataforma, com tempo pra estudar e operar a malha.",[12,4764,4765],{},"Se você não bate em pelo menos três desses cinco critérios, malha é overkill. A complexidade acrescentada não retorna em valor — retorna em chamadas no plantão tentando entender por que o sidecar está reciclando.",[12,4767,4768,4769,4772],{},"Critério mais importante e menos discutido: ",[27,4770,4771],{},"quanto do tráfego é interno?",". Aplicação que recebe requisição na borda, faz uma única consulta no banco e responde, gasta 95% do tempo entre cliente externo e banco — não entre serviços. Aplicação que recebe requisição na borda, chama dez serviços internos pra montar a resposta, gasta a maior parte do tempo no tráfego interno. Pra primeira, malha não acrescenta nada perceptível. Pra segunda, malha pode cortar horas de debugging por mês.",[19,4774,4776],{"id":4775},"o-substituto-cluster-native","O substituto cluster-native",[12,4778,4779],{},"Aqui mora a parte que o discurso americano subestima. Em 2026, vários orquestradores modernos — incluindo o HeroCtl e algumas distribuições do colosso ortodoxo — vêm com criptografia entre serviços embutida no plano de controle. Sem sidecar, sem proxy paralelo, sem instalar produto adicional.",[12,4781,4782],{},"O que isso cobre:",[2735,4784,4785,4791,4797],{},[70,4786,4787,4790],{},[27,4788,4789],{},"Criptografia entre serviços."," Cada serviço recebe certificado emitido automaticamente pelo cluster. Chamada interna é cifrada por padrão.",[70,4792,4793,4796],{},[27,4794,4795],{},"Identidade de serviço."," Cada serviço se autentica pelo certificado, não por IP ou DNS.",[70,4798,4799,4802],{},[27,4800,4801],{},"Authorization básica."," Listas de quem pode chamar quem, declarativas no arquivo de configuração do serviço.",[12,4804,4805],{},"O que isso NÃO cobre:",[2735,4807,4808,4811,4814,4817],{},[70,4809,4810],{},"Traffic shaping fino (canary com 5% de tráfego, mirror).",[70,4812,4813],{},"Distributed tracing completamente automático.",[70,4815,4816],{},"Circuit breakers configuráveis por chamada.",[70,4818,4819],{},"Políticas de retry sofisticadas.",[12,4821,4822],{},"Pra startup média que estava pensando em instalar malha apenas pra ter \"criptografia entre serviços\", o cluster-native já basta. Cobre o tópico de auditoria mais comum sem custar 10 GB de RAM.",[19,4824,4826],{"id":4825},"lado-a-lado-sem-floreio","Lado a lado, sem floreio",[12,4828,4829],{},"A tabela compara Istio, Linkerd, Cilium e a opção de não instalar malha (com criptografia cluster-native ativa) em doze critérios. Não tem coluna sem ressalva.",[119,4831,4832,4848],{},[122,4833,4834],{},[125,4835,4836,4838,4840,4842,4845],{},[128,4837,2983],{},[128,4839,4716],{},[128,4841,4722],{},[128,4843,4844],{},"Cilium SM",[128,4846,4847],{},"Sem malha + cluster-native",[141,4849,4850,4864,4877,4892,4909,4925,4941,4954,4968,4982,4995,5011],{},[125,4851,4852,4855,4857,4859,4862],{},[146,4853,4854],{},"RAM overhead por pod",[146,4856,4658],{},[146,4858,4672],{},[146,4860,4861],{},"~0",[146,4863,4861],{},[125,4865,4866,4869,4871,4873,4875],{},[146,4867,4868],{},"CPU overhead por pod",[146,4870,4661],{},[146,4872,4675],{},[146,4874,4689],{},[146,4876,4861],{},[125,4878,4879,4882,4884,4886,4889],{},[146,4880,4881],{},"Complexidade de setup",[146,4883,3167],{},[146,4885,3155],{},[146,4887,4888],{},"Média (kernel)",[146,4890,4891],{},"Mínima",[125,4893,4894,4897,4900,4903,4906],{},[146,4895,4896],{},"Documentação em PT-BR",[146,4898,4899],{},"Boa",[146,4901,4902],{},"Razoável",[146,4904,4905],{},"Pouca",[146,4907,4908],{},"Embutida no orquestrador",[125,4910,4911,4914,4917,4919,4922],{},[146,4912,4913],{},"Comunidade brasileira",[146,4915,4916],{},"Grande",[146,4918,3160],{},[146,4920,4921],{},"Pequena",[146,4923,4924],{},"Cresce com o orquestrador",[125,4926,4927,4930,4933,4936,4939],{},[146,4928,4929],{},"Sidecar paralelo",[146,4931,4932],{},"Sim (Envoy)",[146,4934,4935],{},"Sim (Rust)",[146,4937,4938],{},"Não (eBPF)",[146,4940,3059],{},[125,4942,4943,4946,4948,4950,4952],{},[146,4944,4945],{},"Criptografia entre serviços automática",[146,4947,3065],{},[146,4949,3065],{},[146,4951,3065],{},[146,4953,3065],{},[125,4955,4956,4959,4961,4963,4965],{},[146,4957,4958],{},"Distributed tracing automático",[146,4960,3065],{},[146,4962,3065],{},[146,4964,3140],{},[146,4966,4967],{},"Não (precisa OpenTelemetry)",[125,4969,4970,4973,4975,4977,4979],{},[146,4971,4972],{},"Traffic shaping fino (canary 5%)",[146,4974,3065],{},[146,4976,3065],{},[146,4978,3140],{},[146,4980,4981],{},"Básico (rolling, blue-green)",[125,4983,4984,4987,4989,4991,4993],{},[146,4985,4986],{},"Circuit breakers configuráveis",[146,4988,3065],{},[146,4990,3065],{},[146,4992,3062],{},[146,4994,3059],{},[125,4996,4997,4999,5002,5005,5008],{},[146,4998,3152],{},[146,5000,5001],{},"6–10 semanas",[146,5003,5004],{},"2–4 semanas",[146,5006,5007],{},"4–6 semanas",[146,5009,5010],{},"Dias",[125,5012,5013,5016,5019,5022,5025],{},[146,5014,5015],{},"Faixa de aplicação ideal",[146,5017,5018],{},"50+ serviços",[146,5020,5021],{},"10–50 serviços",[146,5023,5024],{},"30+ serviços com kernel novo",[146,5026,5027],{},"1–30 serviços",[12,5029,5030,5031,5034],{},"A coluna que importa é a última linha — ",[27,5032,5033],{},"faixa de aplicação ideal",". Quem está abaixo da faixa, paga overhead sem retorno. Quem está acima, sente que falta feature.",[19,5036,5038],{"id":5037},"quando-service-mesh-paga-o-preco","Quando service mesh paga o preço",[12,5040,5041],{},"Quatro cenários em que o investimento se justifica:",[2735,5043,5044,5050,5056,5062],{},[70,5045,5046,5049],{},[27,5047,5048],{},"Trinta ou mais microserviços ativos."," A complexidade operacional sem malha vira pior que com malha — diagnosticar uma cadeia de seis chamadas internas em três times diferentes é caro sem tracing automático.",[70,5051,5052,5055],{},[27,5053,5054],{},"Compliance enterprise com requisitos de zero-trust."," Alguns frameworks de auditoria pedem que a stack tenha \"rede zero-trust nominalmente\". Malha resolve a checkbox formalmente.",[70,5057,5058,5061],{},[27,5059,5060],{},"Federação multi-cluster."," Roteamento de serviço entre dois ou três clusters em regiões diferentes, com failover automático. Malha facilita esse cenário; cluster-native resolve mal.",[70,5063,5064,5067],{},[27,5065,5066],{},"Time de plataforma com cinco ou mais pessoas dedicadas."," Você tem capacidade pra extrair valor da malha — operar, evoluir, dimensionar o plano de controle dela. Sem esse time, a malha vira passivo.",[12,5069,5070],{},"Se você bate em dois ou mais desses, comece a avaliar. Comece pelo Linkerd — é o que dá menos dor por menos retorno relativo perdido.",[19,5072,5074],{"id":5073},"quando-nao-instala-a-maioria-dos-casos","Quando NÃO instala (a maioria dos casos)",[12,5076,5077],{},"Cinco cenários em que instalar malha hoje custa mais do que retorna:",[2735,5079,5080,5086,5092,5098,5104],{},[70,5081,5082,5085],{},[27,5083,5084],{},"Monolito com cinco a dez microserviços auxiliares."," Zero ganho, custo grande. O overhead em RAM cai direto em conta de servidor.",[70,5087,5088,5091],{},[27,5089,5090],{},"Time pequeno, menos de três pessoas em plataforma."," Operar malha exige plantão dedicado pra ela. Time pequeno absorve esse custo às custas de feature de produto.",[70,5093,5094,5097],{},[27,5095,5096],{},"Cluster com menos de trinta pods totais."," Gerenciar trinta pods é trabalho humano, não exige tracing automático. O custo de aprender a malha não retorna.",[70,5099,5100,5103],{},[27,5101,5102],{},"Workload HTTP simples sem requisitos de canary."," Se você nunca precisou liberar 5% do tráfego pra uma versão nova porque rolling update sempre serviu, malha é solução pra problema que não existe.",[70,5105,5106,5109],{},[27,5107,5108],{},"Custo de cluster sob pressão."," Se cada gigabyte de RAM tá sendo contado, gastar 10 GB em sidecars é decisão difícil de defender pra investidor.",[19,5111,5113],{"id":5112},"decisao-evolutiva-por-estagio","Decisão evolutiva, por estágio",[12,5115,5116],{},"A decisão correta muda com o tamanho do sistema. Quatro estágios:",[2735,5118,5119,5125,5131,5137],{},[70,5120,5121,5124],{},[27,5122,5123],{},"Estágio 1 — 1 a 10 serviços."," Sem malha. Se precisa de criptografia entre serviços, faça TLS no código (a maioria das linguagens tem cliente HTTPS pronto). Não vale o aprendizado. Foque em entregar produto.",[70,5126,5127,5130],{},[27,5128,5129],{},"Estágio 2 — 10 a 30 serviços."," Cluster com criptografia embutida no plano de controle (HeroCtl, alguns presets do colosso). Resolve criptografia + identidade + descoberta de serviço sem sidecar. Cobre a maior parte do que malha oferece, sem o custo.",[70,5132,5133,5136],{},[27,5134,5135],{},"Estágio 3 — 30 a 50 serviços com time de plataforma."," Avalie Linkerd primeiro. Curva curta, overhead baixo, resolve tracing e circuit breakers. Istio só se features avançadas (authorization L7 sofisticada, federação multi-cluster real) forem requisito imediato.",[70,5138,5139,5142],{},[27,5140,5141],{},"Estágio 4 — 50+ serviços, compliance enterprise."," Istio ou Cilium Service Mesh. Compliance vai pedir uma das duas; restante são detalhes.",[12,5144,5145],{},"Sair de um estágio pro próximo é decisão deliberada, não gradual. Adicione o componente quando o time topa o aprendizado e o cluster topa o overhead. Não antes.",[19,5147,5149],{"id":5148},"a-trampa-do-vamos-instalar-agora-pra-estar-preparado","A trampa do \"vamos instalar agora pra estar preparado\"",[12,5151,5152],{},"Argumento que aparece em toda discussão: \"se vou crescer pra cinquenta serviços ano que vem, melhor instalar agora e aprender\". A trampa tem três faces:",[2735,5154,5155,5161,5167],{},[70,5156,5157,5160],{},[27,5158,5159],{},"Aprender malha custa de quatro a oito semanas por pessoa do time."," Em time de cinco, são vinte a quarenta semanas-pessoa. Multiplicado por R$ 200\u002Fhora, dá entre R$ 160 mil e R$ 320 mil só em aprendizado. Esse dinheiro adquire feature ou compra prazo de runway.",[70,5162,5163,5166],{},[27,5164,5165],{},"Cada componente novo é mais um ponto de falha crítico."," Plano de controle da malha (Istio Pilot, Linkerd controller, Cilium operator) pode falhar e levar conectividade interna junto. Mais componentes em quórum, mais surface de incidente. Adicione apenas quando o ganho compensa esse risco.",[70,5168,5169,5172],{},[27,5170,5171],{},"Quando você precisar, instalar leva uma semana, não um mês."," Linkerd em particular é instalável num par de comandos. Cilium em algumas horas se o cluster topar kernel recente. Adiar a decisão não é dívida técnica — é dívida adiada com juros menores.",[12,5174,5175],{},"Não funciona \"antecipar pra estar preparado\". O que funciona é monitorar os critérios objetivos da seção anterior e instalar quando dois ou mais virarem realidade.",[19,5177,5179],{"id":5178},"como-o-heroctl-encara-o-problema","Como o HeroCtl encara o problema",[12,5181,5182],{},"A nossa posição é deliberada: malha de serviço, na maioria dos casos brasileiros, é decisão pra estágio três ou quatro. Pra cobrir os estágios um e dois, o HeroCtl traz embutido no plano de controle:",[2735,5184,5185,5191,5197],{},[70,5186,5187,5190],{},[27,5188,5189],{},"Criptografia entre serviços automática."," Cada serviço submetido recebe identidade própria. Chamada interna entre dois serviços é cifrada por padrão, sem alteração no código da aplicação e sem sidecar paralelo.",[70,5192,5193,5196],{},[27,5194,5195],{},"Distributed tracing via OpenTelemetry exporter integrado."," O cluster propaga cabeçalhos de correlação e exporta pra qualquer coletor que entenda OTLP. Não é tão rico quanto malha completa (que injeta tracing automaticamente nos sidecars), mas cobre 80% do uso real.",[70,5198,5199,5202],{},[27,5200,5201],{},"Traffic shaping básico embutido."," Rolling update, canary com porcentagem fixa de tráfego, blue-green. Suficiente pra startup que faz dez deploys por dia. Não cobre mirror nem canary com peso por header — pra isso, precisa instalar malha.",[12,5204,5205],{},"Pra startup brasileira até a faixa dos trinta serviços, isso cobre cerca de 80% do que uma malha completa entrega — sem o sidecar, sem as quatro semanas de aprendizado, sem os 10 GB de RAM. Quando o sistema cresce além disso, instalar Linkerd em cima do HeroCtl é caminho documentado.",[19,5207,5209],{"id":5208},"os-quatro-erros-mais-caros-instalando-service-mesh","Os quatro erros mais caros instalando service mesh",[12,5211,5212],{},"Pra time que decidiu pelo passo, quatro armadilhas que custam de duas semanas a três meses de retrabalho:",[2735,5214,5215,5221,5238,5244],{},[70,5216,5217,5220],{},[27,5218,5219],{},"Instalar antes de precisar."," Cobertura desnecessária vira passivo. Componente novo no quórum, custo de RAM, tempo de aprendizado — sem retorno equivalente.",[70,5222,5223,5226,5227,5230,5231,5234,5235,5237],{},[27,5224,5225],{},"Configurar criptografia estrita no dia um sem pensar em legacy."," Modo ",[231,5228,5229],{},"STRICT"," quebra qualquer serviço que ainda não foi migrado. A migração correta é gradual: modo ",[231,5232,5233],{},"PERMISSIVE"," no início (aceita tráfego cifrado e não-cifrado), só vira ",[231,5236,5229],{}," quando todos os serviços estão dentro da malha.",[70,5239,5240,5243],{},[27,5241,5242],{},"Não dimensionar o plano de controle."," Istio Pilot e similares precisam de RAM e CPU suficientes pra distribuir configuração pra todos os sidecars. Em cluster crescendo, virar gargalo do plano de controle é incidente clássico de quem não planejou.",[70,5245,5246,5249],{},[27,5247,5248],{},"Pular Linkerd pra Istio \"porque é mais popular\"."," Linkerd resolve 80% dos casos com 30% do overhead. A escolha por Istio só se justifica quando uma feature específica (authorization L7 sofisticada, integração com serviço de identidade externo, multi-cluster federation) for requisito real, não preferência de currículo.",[19,5251,4244],{"id":4243},[12,5253,5254,5257],{},[27,5255,5256],{},"Linkerd é leve o suficiente pra cluster pequeno?","\nMais leve que Istio em uma ordem de grandeza, mas ainda assim é sidecar paralelo em cada pod. Pra cluster com vinte pods e quatro nós de 4 GB, Linkerd come cerca de 600 MB de RAM total — significativo mas tolerável. Pra cluster com dez pods, ainda é exagero. Linkerd entra em cena no estágio três (10–50 serviços), não antes.",[12,5259,5260,5263,5264,5266],{},[27,5261,5262],{},"Istio Ambient Mode (sem sidecar) muda essa decisão?","\nReduz o overhead por pod (vai pra um agente por nó, ",[231,5265,4597],{},"), mas ainda exige operar o plano de controle do Istio inteiro. Em produção estável desde 2024, mas a comunidade brasileira ainda é pequena — esperar mais alguns trimestres pra adoção em projeto crítico é prudente.",[12,5268,5269,5272],{},[27,5270,5271],{},"Cilium eBPF realmente tem zero overhead?","\nPor pod, sim — não tem sidecar paralelo. Mas o agente Cilium em cada nó consome de 200 a 400 MB e adiciona carga no kernel. Pra cluster com kernel Linux moderno e CNI compatível, é a opção mais eficiente. Pra cluster que ainda roda kernel antigo ou usa CNI específico, o setup vira projeto.",[12,5274,5275,5278],{},[27,5276,5277],{},"Como faço criptografia entre serviços sem service mesh?","\nTrês caminhos. Primeiro, TLS no código da aplicação — cada serviço expõe HTTPS, cada cliente confia em CA interna. Funciona, mas exige distribuir certificados manualmente (ou via cofre de segredos). Segundo, plano de controle do orquestrador emitir certificados automaticamente — HeroCtl e algumas distribuições do colosso fazem isso, é o caminho mais limpo. Terceiro, VPN ou rede overlay cifrada (WireGuard) entre nós — protege o tráfego dentro do cluster, mas não a identidade serviço-a-serviço.",[12,5280,5281,5284],{},[27,5282,5283],{},"Distributed tracing precisa malha?","\nNão. OpenTelemetry SDK em cada serviço, exportando pra um coletor central (Tempo, Jaeger, ou serviço gerenciado), cobre 90% do uso. Malha automatiza a injeção sem mudar código, o que é confortável — mas não é requisito. Pra startup, começar com OpenTelemetry no código é mais barato.",[12,5286,5287,5290],{},[27,5288,5289],{},"Service mesh em cluster gerenciado é mais fácil?","\nMais fácil de instalar, sim — boa parte dos provedores oferece add-on de Istio ou Linkerd com um clique. Mais fácil de operar, não — você ainda precisa entender o plano de controle, dimensionar, debugar quando uma sidecar reciclar. Não ganhe tempo de instalação às custas de despreparo operacional.",[12,5292,5293,5296],{},[27,5294,5295],{},"Qual malha é mais usada em startup brasileira?","\nPor experiência de comunidade, Istio domina nas empresas que adotaram entre 2020 e 2022 (efeito moda CNCF). Linkerd cresce desde 2024 entre quem migrou ou começou novo, especialmente fintechs de tamanho médio. Cilium aparece em casos específicos (clusters muito grandes, otimização de custo). Consul Connect raríssimo no Brasil.",[12,5298,5299,5302],{},[27,5300,5301],{},"Vale a pena pra monolito + 3 microserviços?","\nNão. Monolito + três microserviços não tem complexidade interna que malha ajude a domar. TLS no código resolve criptografia. Logs centralizados resolvem visibilidade. Rolling update do orquestrador resolve deploy seguro. Instalar malha nesse cenário é trazer um problema pra resolver outro problema que não existe.",[12,5304,5305,5308],{},[27,5306,5307],{},"O HeroCtl substitui completamente uma service mesh?","\nPra estágios um e dois (até trinta serviços), substitui em cerca de 80% do uso real. Pra estágios três e quatro (acima de trinta serviços, ou compliance específico), o HeroCtl convive com Linkerd ou Istio rodando como jobs em cima. A criptografia entre serviços do plano de controle do HeroCtl coexiste com a malha — a malha cuida do tráfego entre seus pods, o HeroCtl cuida da identidade dos serviços e da comunicação com o plano de controle.",[19,5310,3310],{"id":3309},[12,5312,5313],{},"A regra prática que a gente recomenda pra tech lead brasileiro: instale malha quando dois ou mais dos critérios objetivos virarem realidade — trinta serviços ativos, mais de um incidente por mês relacionado a chamadas internas, compliance formal pedindo zero-trust, time de plataforma de cinco pessoas, federação multi-cluster real. Antes disso, cluster com criptografia embutida no plano de controle resolve a maior parte do que você ia comprar com malha, sem os 10 GB de RAM e sem as oito semanas de aprendizado.",[12,5315,5316],{},"Pra começar a explorar essa via — orquestrador com criptografia entre serviços já incluída, sem sidecar paralelo, plano de controle ocupando entre 200 e 400 MB por servidor e eleição de coordenador em cerca de sete segundos quando algo cai — instala em um servidor Linux qualquer e abre o painel:",[224,5318,5320],{"className":226,"code":5319,"language":228,"meta":229,"style":229},"curl -sSL get.heroctl.com\u002Finstall.sh | sh\n",[231,5321,5322],{"__ignoreMap":229},[234,5323,5324,5326,5328,5331,5333],{"class":236,"line":237},[234,5325,1220],{"class":247},[234,5327,2958],{"class":251},[234,5329,5330],{"class":255}," get.heroctl.com\u002Finstall.sh",[234,5332,2964],{"class":383},[234,5334,2967],{"class":247},[12,5336,5337,5338,5341,5342,5346],{},"Pra continuar nessa linha, dois posts diretos. Em ",[3337,5339,5340],{"href":4368},"Multi-tenant SaaS — isolamento real ou só namespace?"," tratamos do problema vizinho — separar clientes dentro do mesmo cluster sem quebrar o orçamento. Em ",[3337,5343,5345],{"href":5344},"\u002Fblog\u002Fk3s-vs-heroctl-quando-cada-um-faz-sentido","K3s vs HeroCtl — quando cada um faz sentido"," comparamos a alternativa mais comum quando o time já decidiu que o colosso ortodoxo é exagero.",[12,5348,5349],{},"A escolha por malha de serviço é, no fundo, uma escolha de quando absorver complexidade. A pergunta certa não é \"preciso de Istio?\" — é \"qual é o menor sistema que ainda resolve o meu problema atual?\". Pra grande parte das startups brasileiras, a resposta é mais simples do que a indústria americana sugere.",[3351,5351,4376],{},{"title":229,"searchDepth":244,"depth":244,"links":5353},[5354,5355,5356,5357,5358,5359,5360,5361,5362,5363,5364,5365,5366,5367,5368,5369],{"id":4421,"depth":244,"text":4422},{"id":4431,"depth":244,"text":4432},{"id":4531,"depth":244,"text":4532},{"id":4582,"depth":244,"text":4583},{"id":4626,"depth":244,"text":4627},{"id":4741,"depth":244,"text":4742},{"id":4775,"depth":244,"text":4776},{"id":4825,"depth":244,"text":4826},{"id":5037,"depth":244,"text":5038},{"id":5073,"depth":244,"text":5074},{"id":5112,"depth":244,"text":5113},{"id":5148,"depth":244,"text":5149},{"id":5178,"depth":244,"text":5179},{"id":5208,"depth":244,"text":5209},{"id":4243,"depth":244,"text":4244},{"id":3309,"depth":244,"text":3310},"2026-05-29","Service mesh resolve problemas reais (mTLS, observabilidade entre serviços, traffic shaping). Mas adiciona 30-50% overhead de RAM\u002FCPU e complexidade. Quando vale e quando é overkill.",{},{"title":4413,"description":5371},{"loc":4275},"blog\u002Fservice-mesh-quando-vale-pra-saas-pequeno-medio",[5377,5378,5379,3379,4409],"service-mesh","istio","linkerd","m8RjibPP4k692WT8tLFOC71w3Bgt3Gh-Pv2e5s4vS-M",{"id":5382,"title":5383,"author":7,"body":5384,"category":6383,"cover":3380,"date":6384,"description":6385,"draft":3383,"extension":3384,"lastReviewed":3380,"meta":6386,"navigation":411,"path":6387,"readingTime":6388,"seo":6389,"sitemap":6390,"stem":6391,"tags":6392,"__hash__":6398},"blog_pt\u002Fblog\u002Fcomo-sair-da-aws-sem-reescrever-toda-stack.md","Como sair da AWS sem reescrever toda a stack: guia prático 2026",{"type":9,"value":5385,"toc":6345},[5386,5389,5393,5396,5399,5405,5408,5412,5415,5418,5421,5425,5428,5502,5505,5509,5512,5715,5718,5722,5725,5728,5732,5739,5742,5745,5751,5755,5758,5761,5765,5768,5775,5778,5782,5785,5788,5792,5795,5798,5802,5805,5809,5812,5815,5819,5822,5825,5828,5832,5835,5845,5848,5852,5855,5858,5862,5865,5871,5877,5880,5884,5887,5893,5899,5909,5915,5921,5927,5933,5939,5943,5946,5978,5982,5985,5990,6027,6032,6063,6066,6069,6073,6076,6079,6096,6099,6102,6106,6109,6115,6121,6127,6133,6137,6140,6154,6157,6161,6164,6167,6192,6195,6207,6210,6212,6216,6220,6223,6227,6230,6234,6237,6241,6247,6251,6270,6274,6277,6281,6284,6288,6291,6295,6298,6300,6304,6307,6310,6326,6329,6340,6343],[12,5387,5388],{},"A maioria dos times brasileiros que pensa em sair da AWS adia indefinidamente porque acredita estar diante de um projeto de \"reescrever toda a stack\". Não é. É um projeto de mapeamento, não de reescrita. E o mapeamento cabe numa planilha de doze linhas.",[19,5390,5392],{"id":5391},"tldr-o-que-voce-vai-ler-em-tres-minutos","TL;DR — o que você vai ler em três minutos",[12,5394,5395],{},"Stack típica de SaaS brasileiro usa cerca de doze serviços AWS, e cada um deles tem alternativa portável que custa entre três e sete vezes menos. EC2 vira VPS em qualquer provedor (Hetzner, DigitalOcean, Magalu Cloud). RDS vira Postgres em VPS dedicado, Neon ou Supabase. ElastiCache vira Valkey auto-hospedado. S3 vira Cloudflare R2 ou Backblaze B2 — ambos com API S3-compatível, então o código nem muda. SQS vira fila baseada em Redis ou RabbitMQ. Lambda vira endpoint no app server tradicional ou Cloudflare Workers. ALB vira o roteador integrado do orquestrador. CloudFront vira Cloudflare grátis. IAM vira injeção de secrets no orquestrador.",[12,5397,5398],{},"Cronograma realista pra startup com cinco a dez aplicações: seis a oito semanas, oitenta a cento e sessenta horas de desenvolvimento. Economia típica: três a sete vezes na conta de infra, com retorno em menos de um mês de salário sênior.",[12,5400,5401,5404],{},[27,5402,5403],{},"Não migre se"," o seu compliance exige AWS nominalmente, se o time é único e foca em produto, ou se a stack usa lock-in profundo (DynamoDB com features específicas, Aurora Serverless v2, IAM cross-account complexo).",[12,5406,5407],{},"━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━",[19,5409,5411],{"id":5410},"por-que-tantos-times-brasileiros-adiam-a-saida-da-aws","Por que tantos times brasileiros adiam a saída da AWS?",[12,5413,5414],{},"A resposta honesta é confusão entre dois projetos diferentes. \"Sair da AWS\" virou sinônimo mental de \"reescrever a aplicação\". Não é a mesma coisa.",[12,5416,5417],{},"Reescrever a aplicação é trocar tecnologia central — banco relacional por NoSQL, framework síncrono por reativo, monolito por microsserviços. Isso sim leva trimestres. Sair da AWS é trocar a infra que sustenta a aplicação que você já tem. O código de domínio fica idêntico. Mudam endpoints de banco, credenciais, alguns SDKs e a forma de declarar deploy.",[12,5419,5420],{},"A confusão dura porque o time olha a console da AWS e vê duzentos serviços. Ninguém usa duzentos. A maior parte usa doze. Mapeie esses doze, encontre alternativa pra cada um, e o que resta é trabalho de execução — não de pesquisa.",[19,5422,5424],{"id":5423},"os-doze-servicos-aws-que-sua-stack-provavelmente-usa","Os doze serviços AWS que sua stack provavelmente usa",[12,5426,5427],{},"A planilha de início é essa. Tudo o que está fora dela na sua conta provavelmente é satélite — alarme do CloudWatch que ninguém olha, bucket S3 esquecido, função Lambda morta. Foque nos doze:",[67,5429,5430,5436,5442,5448,5454,5460,5466,5472,5478,5484,5490,5496],{},[70,5431,5432,5435],{},[27,5433,5434],{},"EC2"," — máquinas virtuais que rodam app server e workers",[70,5437,5438,5441],{},[27,5439,5440],{},"RDS"," — banco relacional gerenciado (Postgres ou MySQL)",[70,5443,5444,5447],{},[27,5445,5446],{},"ElastiCache"," — Redis pra cache e sessão",[70,5449,5450,5453],{},[27,5451,5452],{},"S3"," — armazenamento de objetos (uploads, backups, assets)",[70,5455,5456,5459],{},[27,5457,5458],{},"ALB \u002F NLB"," — balanceador de carga na frente das EC2",[70,5461,5462,5465],{},[27,5463,5464],{},"CloudFront"," — CDN pra assets estáticos",[70,5467,5468,5471],{},[27,5469,5470],{},"Route 53"," — DNS autoritativo",[70,5473,5474,5477],{},[27,5475,5476],{},"SES"," — email transacional",[70,5479,5480,5483],{},[27,5481,5482],{},"SQS \u002F SNS"," — fila e pub-sub",[70,5485,5486,5489],{},[27,5487,5488],{},"IAM"," — credenciais e papéis pra serviços conversarem",[70,5491,5492,5495],{},[27,5493,5494],{},"CloudWatch"," — métricas e logs",[70,5497,5498,5501],{},[27,5499,5500],{},"Lambda"," — funções serverless",[12,5503,5504],{},"Se a sua conta tem todos os doze, parabéns: você é a stack mediana. Se tem oito ou nove, melhor — menos coisa pra migrar. Se tem cinco serviços muito específicos (Aurora Global, DynamoDB com Streams, EventBridge complexo), você está num caminho diferente — leia a seção de lock-ins antes de continuar.",[19,5506,5508],{"id":5507},"mapeamento-servico-por-servico-alternativa-custo-e-complexidade","Mapeamento serviço por serviço — alternativa, custo e complexidade",[12,5510,5511],{},"A tabela abaixo é o atalho. Cada linha tem detalhe expandido depois.",[119,5513,5514,5533],{},[122,5515,5516],{},[125,5517,5518,5521,5524,5527,5530],{},[128,5519,5520],{},"Serviço AWS",[128,5522,5523],{},"Alternativa portável",[128,5525,5526],{},"Custo antes (R$\u002Fmês)",[128,5528,5529],{},"Custo depois (R$\u002Fmês)",[128,5531,5532],{},"Complexidade migração",[141,5534,5535,5551,5567,5583,5598,5613,5627,5642,5657,5673,5686,5700],{},[125,5536,5537,5540,5543,5546,5549],{},[146,5538,5539],{},"EC2 t3.medium",[146,5541,5542],{},"VPS Hetzner CPX21",[146,5544,5545],{},"150",[146,5547,5548],{},"44",[146,5550,3155],{},[125,5552,5553,5556,5559,5562,5565],{},[146,5554,5555],{},"RDS db.t4g.large",[146,5557,5558],{},"Postgres self-hosted ou Neon",[146,5560,5561],{},"700",[146,5563,5564],{},"50–250",[146,5566,3160],{},[125,5568,5569,5572,5575,5578,5581],{},[146,5570,5571],{},"ElastiCache cache.t4g.micro",[146,5573,5574],{},"Valkey self-hosted",[146,5576,5577],{},"75",[146,5579,5580],{},"30",[146,5582,3160],{},[125,5584,5585,5588,5591,5594,5596],{},[146,5586,5587],{},"S3 (1TB + egress)",[146,5589,5590],{},"Cloudflare R2",[146,5592,5593],{},"600",[146,5595,5577],{},[146,5597,3155],{},[125,5599,5600,5603,5606,5609,5611],{},[146,5601,5602],{},"ALB",[146,5604,5605],{},"Roteador integrado do orquestrador",[146,5607,5608],{},"110",[146,5610,893],{},[146,5612,3160],{},[125,5614,5615,5617,5620,5623,5625],{},[146,5616,5464],{},[146,5618,5619],{},"Cloudflare grátis",[146,5621,5622],{},"400",[146,5624,893],{},[146,5626,3155],{},[125,5628,5629,5631,5634,5637,5639],{},[146,5630,5470],{},[146,5632,5633],{},"Cloudflare DNS",[146,5635,5636],{},"25",[146,5638,893],{},[146,5640,5641],{},"Trivial",[125,5643,5644,5646,5649,5652,5655],{},[146,5645,5476],{},[146,5647,5648],{},"Resend ou Postmark",[146,5650,5651],{},"50",[146,5653,5654],{},"75–100",[146,5656,5641],{},[125,5658,5659,5661,5664,5667,5670],{},[146,5660,5482],{},[146,5662,5663],{},"Redis Streams ou RabbitMQ",[146,5665,5666],{},"80",[146,5668,5669],{},"0 (mesma VPS)",[146,5671,5672],{},"Média–alta",[125,5674,5675,5677,5680,5682,5684],{},[146,5676,5488],{},[146,5678,5679],{},"Secrets do orquestrador",[146,5681,893],{},[146,5683,893],{},[146,5685,3160],{},[125,5687,5688,5690,5693,5696,5698],{},[146,5689,5494],{},[146,5691,5692],{},"Prometheus + Loki",[146,5694,5695],{},"250",[146,5697,5669],{},[146,5699,3160],{},[125,5701,5702,5704,5707,5709,5712],{},[146,5703,5500],{},[146,5705,5706],{},"App server ou Cloudflare Workers",[146,5708,669],{},[146,5710,5711],{},"0–60",[146,5713,5714],{},"Variável",[12,5716,5717],{},"Câmbio considerado: cinco reais por dólar. Custos de antes assumem stack de SaaS pequeno-médio com cinco a dez aplicações ativas.",[368,5719,5721],{"id":5720},"ec2-vira-vps-em-qualquer-provedor","EC2 vira VPS em qualquer provedor",[12,5723,5724],{},"A migração mais óbvia. EC2 t3.medium custa cerca de trinta dólares mensais — cento e cinquenta reais. Hetzner CPX21 com a mesma classe de CPU e mais RAM custa sete euros e noventa e nove — quarenta e quatro reais. DigitalOcean fica no meio. Magalu Cloud é competitivo pra quem prioriza fatura em real e dado em território nacional.",[12,5726,5727],{},"O caminho técnico é provisionar a VPS, rodar o seu Ansible existente (ou um script de bootstrap simples), copiar o snapshot da EC2 ou subir a imagem do zero. Pra cada servidor, conte de duas a quatro horas. Não é a parte demorada da migração.",[368,5729,5731],{"id":5730},"rds-vira-postgres-self-hosted-ou-neonsupabase","RDS vira Postgres self-hosted ou Neon\u002FSupabase",[12,5733,5734,5735,5738],{},"Aqui há três caminhos honestos. O primeiro é Postgres rodando numa VPS dedicada, com backup automatizado via ",[231,5736,5737],{},"pg_dump"," em cron e replicação física pra um secundário em outra região. Custa o preço da VPS — cinquenta a cem reais mensais — pra substituir um RDS de setecentos.",[12,5740,5741],{},"O segundo é Neon. Postgres serverless com branching, ramp-up automático, plano gratuito generoso, planos pagos a partir de cinco dólares. Útil pra quem quer abandonar AWS sem assumir operação direta de banco.",[12,5743,5744],{},"O terceiro é Supabase, que entrega Postgres com APIs adicionais (auth, realtime, storage) e tier gratuito permanente. Faz sentido pra startups que toleram acoplamento ao Supabase em troca de simplicidade.",[12,5746,5747,5748,5750],{},"A migração em si é ",[231,5749,5737],{}," seguido de restore no destino, com janela de manutenção curta — ou replicação lógica com cutover quase sem downtime se o seu Postgres for versão 13 ou superior. Quatro a oito horas dependendo do tamanho da base.",[368,5752,5754],{"id":5753},"elasticache-vira-valkey-self-hosted","ElastiCache vira Valkey self-hosted",[12,5756,5757],{},"O Redis virou Valkey depois da mudança de licença em 2024 — fork mantido pela Linux Foundation. Roda em qualquer VPS com dois cliques. Trinta reais mensais substituem o ElastiCache de setenta e cinco.",[12,5759,5760],{},"A migração tem duas etapas. Primeiro, levantar o cluster Valkey com Sentinel pra failover automático. Segundo, popular o cache — script que lê da AWS e grava no destino, ou simplesmente deixar a aplicação preencher organicamente após o cutover (cache cold start de poucos minutos). Três a seis horas de trabalho.",[368,5762,5764],{"id":5763},"s3-vira-cloudflare-r2-ou-backblaze-b2","S3 vira Cloudflare R2 (ou Backblaze B2)",[12,5766,5767],{},"Esse é o ganho mais imediato. Cloudflare R2 cobra zero pelo egresso — a fatia mais cara do S3 quando você serve assets pra usuários. Quinze centavos de dólar por GB armazenado, contra vinte e três centavos do S3 padrão. Backblaze B2 é uma alternativa quase idêntica, com integração ainda mais barata pra workloads de backup pesado.",[12,5769,5770,5771,5774],{},"A migração técnica é trivial: ",[231,5772,5773],{},"rclone copy s3:meu-bucket r2:meu-bucket"," em paralelo. Um terabyte transfere em torno de doze horas dependendo da banda. O código da aplicação muda exatamente uma linha — o endpoint do cliente S3. Toda biblioteca AWS SDK aceita configuração de endpoint custom; R2 e B2 implementam o protocolo S3 idêntico.",[12,5776,5777],{},"Volume típico de SaaS médio (cinquenta GB de uploads de usuário): R$75 mensais em R2 contra R$600 em S3 com egresso ativo. A economia paga uma semana de trabalho de migração no primeiro mês.",[368,5779,5781],{"id":5780},"alb-vira-roteador-integrado-do-orquestrador","ALB vira roteador integrado do orquestrador",[12,5783,5784],{},"Se você está usando ALB, é porque tem várias EC2 atrás dele. A alternativa é o roteador embutido no orquestrador escolhido — HeroCtl, Caddy, ou o roteador embutido em outras stacks self-hosted. O orquestrador descobre os contêineres rodando, abre porta, terminam TLS via Let's Encrypt automático, distribui tráfego.",[12,5786,5787],{},"A migração troca a definição de target group AWS por uma definição de ingress no manifesto do orquestrador. Quatro a oito horas pra entender as regras certas. Cento e dez reais mensais economizados por balanceador, e o orquestrador aceita quantos hosts você quiser sem cobrança adicional.",[368,5789,5791],{"id":5790},"cloudfront-vira-cloudflare-gratis","CloudFront vira Cloudflare grátis",[12,5793,5794],{},"Esse merece uma menção em destaque. CloudFront cobra por GB transferido — quem serve vídeo ou downloads pesados sangra. Cloudflare oferece CDN global gratuita no plano free, com cache configurável, mitigação básica de DDoS e WAF rudimentar. Pra a maior parte dos casos de SaaS, é mais que suficiente.",[12,5796,5797],{},"A migração é trocar os nameservers do domínio pra Cloudflare e configurar regras de cache. Duas a quatro horas. A economia pode ser massiva — quatrocentos reais mensais pra quem tem volume médio de tráfego, milhares pra quem tem volume alto.",[368,5799,5801],{"id":5800},"route-53-vira-cloudflare-dns","Route 53 vira Cloudflare DNS",[12,5803,5804],{},"DNS no Cloudflare é grátis e mais rápido que Route 53 na maioria das medições públicas. Migração é exportar a zone file, importar no Cloudflare, validar registros, mudar nameservers no registrar. Trinta minutos. Vinte e cinco reais mensais que voltam pro caixa.",[368,5806,5808],{"id":5807},"ses-vira-resend-postmark-ou-mailgun","SES vira Resend, Postmark ou Mailgun",[12,5810,5811],{},"A AWS é barata pra envio em volume, mas a entregabilidade do SES exige aquecimento de IP e configuração de reputação que tira tempo. Resend cobra vinte dólares por cinquenta mil emails mensais e tem entregabilidade superior fora da caixa. Postmark cobra quinze por dez mil. Mailgun cobre o caso de quem manda muito volume não-transacional.",[12,5813,5814],{},"A migração é trocar credenciais SMTP no app — uma hora de trabalho.",[368,5816,5818],{"id":5817},"sqs-e-sns-viram-redis-streams-ou-rabbitmq","SQS e SNS viram Redis Streams ou RabbitMQ",[12,5820,5821],{},"A migração mais delicada. SQS é um serviço que faz uma coisa e faz bem; substituir exige escolher tecnologia de fila e refatorar produtor e consumidor.",[12,5823,5824],{},"O caminho mais curto é Redis Streams, principalmente se você já está rodando Valkey pra cache. Bibliotecas como Sidekiq (Ruby), BullMQ (Node), RQ (Python) e Asynq (Go) consomem Redis nativamente. RabbitMQ é mais robusto pra cenários de roteamento complexo. NATS é alternativa moderna pra pub-sub.",[12,5826,5827],{},"Pra cada fila, conte um a três dias dependendo da complexidade. Filas simples de jobs background são triviais. Filas com fan-out, dead letter queues e visibility timeout customizado exigem mais cuidado. Oitenta reais mensais economizados, e a fila roda na mesma VPS que o cache — zero adicional na infra.",[368,5829,5831],{"id":5830},"iam-vira-secrets-do-orquestrador","IAM vira secrets do orquestrador",[12,5833,5834],{},"Aqui está a migração não-óbvia que pega muito time desprevenido. Na AWS, a aplicação acessa S3 e RDS sem credenciais explícitas no código — a EC2 herda um IAM role e o SDK busca tokens automaticamente. Fora da AWS, isso desaparece.",[12,5836,5837,5838,5840,5841,5844],{},"A solução é injeção de secrets pelo orquestrador. HeroCtl, k3s e similares aceitam secrets como recursos de primeira classe — você declara ",[231,5839,453],{}," ou ",[231,5842,5843],{},"S3_ACCESS_KEY"," no manifesto do job e o orquestrador injeta como variável de ambiente no contêiner. Pra cenários mais sofisticados, HashiCorp Vault auto-hospedado faz rotação automática.",[12,5846,5847],{},"A migração é refatorar cada papel IAM em um conjunto de credenciais explícitas, criadas no provedor de destino (Cloudflare API token, Postgres user específico, etc), e declaradas como secrets. Quatro a oito horas pra uma stack média.",[368,5849,5851],{"id":5850},"cloudwatch-vira-prometheus-loki","CloudWatch vira Prometheus + Loki",[12,5853,5854],{},"Métricas viram Prometheus + Grafana. Logs viram Loki + Grafana. Tudo roda em containers na mesma cluster. Duzentos e cinquenta reais mensais de CloudWatch viram zero adicional.",[12,5856,5857],{},"A configuração inicial leva cerca de quatro horas pra ficar produtiva: Prometheus com service discovery apontando pros agentes do orquestrador, Loki recebendo via Promtail ou diretamente do runtime de contêiner, Grafana com dashboards básicos. Há posts dedicados a essa migração no blog.",[368,5859,5861],{"id":5860},"lambda-a-parte-mais-dificil","Lambda — a parte mais difícil",[12,5863,5864],{},"Lambda é o serviço com a maior variância de complexidade na migração. Depende totalmente de como você está usando.",[12,5866,5867,5870],{},[27,5868,5869],{},"Lambda HTTP simples"," (API Gateway → Lambda → resposta) é trivial. Vira endpoint no seu app server. O código da função muda pouco — handler do framework no lugar do handler do Lambda. Uma a duas horas por função.",[12,5872,5873,5876],{},[27,5874,5875],{},"Lambda event-driven"," (S3 dispara Lambda, SQS dispara Lambda, EventBridge agenda Lambda) é a parte cara. Pra eventos de S3, R2 oferece eventos via Cloudflare Workers — você reescreve a Lambda como Worker e mantém o padrão. Pra SQS, vira consumer no app server. Pra EventBridge agendado, vira cron no orquestrador.",[12,5878,5879],{},"Cenário pior: Lambda complexa com EventBridge, Step Functions e dead letter queues encadeados. Aqui é redesign. Reserve uma semana ou duas e desenhe um modelo de eventos mais simples — geralmente o sistema fica melhor.",[19,5881,5883],{"id":5882},"cronograma-realista-de-seis-a-oito-semanas","Cronograma realista de seis a oito semanas",[12,5885,5886],{},"A ordem importa. Começar pelo banco é tentação e armadilha — banco é o último a migrar, não o primeiro.",[12,5888,5889,5892],{},[27,5890,5891],{},"Semana 1 — Inventário e decisão."," Lista os doze serviços, anota custo atual, identifica integrações entre eles. Escolhe alternativa pra cada um. Documento de uma página com a tabela de mapeamento. Sem código ainda.",[12,5894,5895,5898],{},[27,5896,5897],{},"Semana 2 — Provisão do destino em paralelo."," Levanta as VPS, instala o orquestrador (HeroCtl ou similar), configura DNS de teste apontando pra um subdomínio. Sobe Postgres, Valkey, Cloudflare R2. Tudo vazio. Smoke test: um \"hello world\" rodando.",[12,5900,5901,5904,5905,5908],{},[27,5902,5903],{},"Semana 3 — Migração de storage."," S3 pra R2 com ",[231,5906,5907],{},"rclone",". Costuma ser lenta (volume) mas baixíssimo risco. Aplicação ainda lê de S3, mas você valida que R2 está sincronizado. No fim da semana, dual-write — aplicação escreve nos dois.",[12,5910,5911,5914],{},[27,5912,5913],{},"Semana 4 — Migração do banco."," Réplica lógica de Postgres do RDS pro destino. Cutover numa janela de manutenção curta — costuma ser minutos, não horas, com replicação lógica funcionando. Aplicação aponta pro novo banco. RDS fica como hot standby por uma semana.",[12,5916,5917,5920],{},[27,5918,5919],{},"Semana 5 — Migração de aplicações web."," Apps que rodam em EC2 viram jobs no orquestrador. Roteador integrado faz o papel do ALB. DNS aponta pro orquestrador (ou pra Cloudflare na frente dele). Cutover gradual usando weighted DNS.",[12,5922,5923,5926],{},[27,5924,5925],{},"Semana 6 — Filas e jobs assíncronos."," SQS sai, Redis Streams ou RabbitMQ entra. Workers rodam no orquestrador. Período de dual-consume pra garantir que nenhuma mensagem cai.",[12,5928,5929,5932],{},[27,5930,5931],{},"Semana 7 — Lambdas e workloads event-driven."," A semana mais variável. Lambdas HTTP migram rapidamente. Lambdas event-driven exigem o redesign discutido acima. Se você tem mais de dez Lambdas complexas, considere estender pra duas semanas.",[12,5934,5935,5938],{},[27,5936,5937],{},"Semana 8 — Cutover final, monitoramento intensivo, decommission."," Cloudflare na frente substitui CloudFront. Route 53 vira Cloudflare DNS. CloudWatch vai pra Prometheus + Loki. Última coisa: desliga as EC2 antigas e fecha a conta AWS — ou deixa um saldo mínimo se você ainda mantém algum serviço residual.",[19,5940,5942],{"id":5941},"os-cinco-lock-ins-que-mais-doem-na-migracao","Os cinco lock-ins que mais doem na migração",[12,5944,5945],{},"Honestidade é importante: nem tudo migra fácil. Cinco coisas exigem trabalho extra e às vezes mudam a viabilidade do projeto:",[67,5947,5948,5954,5960,5966,5972],{},[70,5949,5950,5953],{},[27,5951,5952],{},"DynamoDB com features específicas."," GSI, Streams, scan limits, TTL. Não há equivalente direto. O caminho realista é redesenhar pra Postgres com JSONB, ou pra um NoSQL self-hosted (FoundationDB, ScyllaDB) — re-arquitetura, não migração.",[70,5955,5956,5959],{},[27,5957,5958],{},"Aurora-only features."," Aurora Serverless v2 com auto-scaling de connections, Aurora Global Database, Aurora I\u002FO optimized. Postgres self-hosted faz quase tudo, mas não tem o auto-scaling instantâneo. Pra workloads spiky, considere Neon (que oferece padrão similar).",[70,5961,5962,5965],{},[27,5963,5964],{},"IAM cross-service complexo."," Times que usam IAM roles cross-account, Service Control Policies e organização hierárquica de contas têm controle de acesso embutido na arquitetura. Migrar exige reimplementar a hierarquia em outro lugar — Vault, Cloudflare Access, ou injeção de secrets do orquestrador. Conte dias, não horas.",[70,5967,5968,5971],{},[27,5969,5970],{},"Lambda + EventBridge complexo."," Pipelines de eventos com vários hops, retries, dead letter queues. Não migra como está. Redesigne em torno de filas (RabbitMQ, NATS) e workers persistentes. Geralmente o sistema fica mais simples — mas leva tempo.",[70,5973,5974,5977],{},[27,5975,5976],{},"S3 events disparando Lambda."," Padrão muito comum, e R2 com Cloudflare Workers cobre a maioria dos casos. Pra workloads que precisam de garantia exatamente-uma-vez ou ordering forte, troque pra padrão de fila — produtor escreve evento na fila quando arquivo é confirmado, worker consome.",[19,5979,5981],{"id":5980},"a-conta-de-economia-sem-otimismo","A conta de economia, sem otimismo",[12,5983,5984],{},"Cenário típico de SaaS brasileiro com cinco aplicações:",[12,5986,5987],{},[27,5988,5989],{},"Antes na AWS:",[2735,5991,5992,5995,5998,6001,6004,6007,6010,6013,6016,6019,6022],{},[70,5993,5994],{},"Cinco EC2 t3.medium: R$750",[70,5996,5997],{},"RDS db.t4g.large Multi-AZ: R$1.400",[70,5999,6000],{},"ElastiCache cache.t4g.micro: R$75",[70,6002,6003],{},"S3 com 100GB e egresso médio: R$300",[70,6005,6006],{},"ALB: R$110",[70,6008,6009],{},"CloudFront com volume médio: R$400",[70,6011,6012],{},"Route 53 + SES: R$75",[70,6014,6015],{},"CloudWatch logs\u002Fmétricas: R$250",[70,6017,6018],{},"Lambda com volume médio: R$200",[70,6020,6021],{},"NAT Gateway: R$200",[70,6023,6024],{},[27,6025,6026],{},"Total: R$3.760\u002Fmês = R$45.120\u002Fano",[12,6028,6029],{},[27,6030,6031],{},"Depois auto-hospedado:",[2735,6033,6034,6037,6040,6043,6046,6049,6052,6055,6058],{},[70,6035,6036],{},"Quatro VPS Hetzner CPX21 com orquestrador: R$176",[70,6038,6039],{},"Postgres self-hosted (incluído nas VPS): R$0",[70,6041,6042],{},"Valkey (incluído): R$0",[70,6044,6045],{},"Cloudflare R2 50GB com egresso ilimitado: R$75",[70,6047,6048],{},"Cloudflare CDN + DNS: R$0",[70,6050,6051],{},"Resend pra email: R$100",[70,6053,6054],{},"Prometheus + Loki (incluído): R$0",[70,6056,6057],{},"Workers de fila (incluídos): R$0",[70,6059,6060],{},[27,6061,6062],{},"Total: R$351\u002Fmês = R$4.212\u002Fano",[12,6064,6065],{},"Economia: R$3.409\u002Fmês, R$40.908\u002Fano. Aproximadamente um mês de salário de engenheiro sênior.",[12,6067,6068],{},"A migração consome oitenta a cento e sessenta horas. Em horário de dev sênior interno, são entre dezesseis e trinta e dois mil reais. Retorno em cinco a dez meses, com economia perpétua depois.",[19,6070,6072],{"id":6071},"a-migracao-mais-nao-obvia-secrets-e-credenciais","A migração mais não-óbvia: secrets e credenciais",[12,6074,6075],{},"Vale repetir, porque é o que mais surpreende time experiente em AWS. Na AWS você acessa S3 sem credentials no código — IAM role da EC2 resolve. Acessa RDS via IAM authentication. Acessa parameter store via IAM. O time perde a noção de que essa \"magia\" existe.",[12,6077,6078],{},"Fora da AWS, toda credencial é explícita. Aplicação precisa de:",[2735,6080,6081,6084,6087,6090,6093],{},[70,6082,6083],{},"Access key e secret pra R2 (criada no painel Cloudflare)",[70,6085,6086],{},"Connection string com user e senha pro Postgres",[70,6088,6089],{},"URL do Valkey com senha",[70,6091,6092],{},"API key pra Resend",[70,6094,6095],{},"Token pra Cloudflare API se você automatiza DNS",[12,6097,6098],{},"A solução do orquestrador é declarar tudo isso como secrets injetados no contêiner como variáveis de ambiente. O secret é criptografado em repouso no orquestrador e nunca aparece nos logs. Pra rotação automática e auditoria sofisticada, Vault auto-hospedado entra na jogada — mas a maioria dos times não precisa.",[12,6100,6101],{},"Plano: faça uma planilha com todas as credenciais que cada app precisa, crie cada uma no provedor de destino, declare como secret no orquestrador, injeta no contêiner. Quatro a oito horas pra uma stack média.",[19,6103,6105],{"id":6104},"quando-nao-migrar-perfis-honestos","Quando NÃO migrar (perfis honestos)",[12,6107,6108],{},"Quatro situações em que sair da AWS é decisão errada:",[12,6110,6111,6114],{},[27,6112,6113],{},"Compliance que lista AWS nominalmente."," FedRAMP, ITAR, certos contratos de governo americano e algumas certificações financeiras exigem que a infra rode sobre componentes pré-aprovados — e a maioria das listas inclui AWS, GCP, Azure, e poucos provedores adicionais. Se o seu cliente é uma agência federal americana, AWS resolve uma fatia do compliance que custaria meses replicar em outro lugar.",[12,6116,6117,6120],{},[27,6118,6119],{},"Time único focado em produto."," Se você é o único dev e está construindo o produto, oito semanas redirecionadas pra migração matam roadmap. Faça quando tiver o segundo dev, ou quando os custos AWS passarem a representar fatia significativa do MRR. Antes disso, AWS é caro mas comprável.",[12,6122,6123,6126],{},[27,6124,6125],{},"Custos AWS abaixo de 2% do MRR."," Conta de mil reais mensais pra startup que fatura cem mil. A economia é real mas o esforço não vale o foco. Migre quando a fatura passar de cinco a dez por cento do MRR — aí o ganho cobre a oportunidade perdida.",[12,6128,6129,6132],{},[27,6130,6131],{},"Lock-in profundo em DynamoDB ou Aurora Serverless v2."," Já tratado acima. Se metade da sua arquitetura é DynamoDB com Streams, você não migra — re-arquiteta. Esse é projeto diferente, com escopo diferente, decisão diferente.",[19,6134,6136],{"id":6135},"estrategia-hibrida-alternativa-pra-quem-nao-quer-migrar-tudo","Estratégia híbrida — alternativa pra quem não quer migrar tudo",[12,6138,6139],{},"Times com cinquenta ou mais aplicações em AWS raramente migram em bloco. A estratégia híbrida funciona melhor:",[2735,6141,6142,6145,6148,6151],{},[70,6143,6144],{},"Mantém em AWS o que é caro de mover (Aurora com features específicas, Lambda crítica, DynamoDB)",[70,6146,6147],{},"Move o que é barato de mover e caro de manter (S3 → R2, CloudFront → Cloudflare, EC2 não-críticas → VPS)",[70,6149,6150],{},"Estabelece VPN ou conexão privada entre as duas pontas",[70,6152,6153],{},"Economia parcial mas zero risco de migração radical",[12,6155,6156],{},"Resultado típico: corte de quarenta a sessenta por cento da fatura AWS, sem mexer nas peças críticas. Pra empresa que paga cinquenta mil mensais, isso é vinte a trinta mil de volta — e o restante migra organicamente nos doze meses seguintes, conforme times reescrevem componentes por outras razões.",[19,6158,6160],{"id":6159},"heroctl-como-destino-o-que-muda-na-pratica","HeroCtl como destino — o que muda na prática",[12,6162,6163],{},"HeroCtl é orquestrador de contêineres que roda em qualquer servidor Linux com Docker. Quatro VPS rodando HeroCtl entregam experiência operacional próxima do que você teria com ECS gerenciado — sem cobrança gerenciada, sem lock-in.",[12,6165,6166],{},"O que substitui:",[2735,6168,6169,6174,6180,6186],{},[70,6170,6171,6173],{},[27,6172,5602],{}," vira o roteador integrado do HeroCtl, com TLS Let's Encrypt automático",[70,6175,6176,6179],{},[27,6177,6178],{},"CloudWatch parcial"," vira métricas embutidas e logs centralizados nativos",[70,6181,6182,6185],{},[27,6183,6184],{},"RDS automated backups"," vira backup gerenciado no Business Edition",[70,6187,6188,6191],{},[27,6189,6190],{},"IAM roles em apps"," vira injeção de secrets no manifesto de job",[12,6193,6194],{},"O que continua igual: Docker rodando seu app exatamente como roda em ECS. Variáveis de ambiente, healthchecks, rolling deploys, multi-replicas. A aplicação não percebe a diferença.",[12,6196,6197,6198,6200,6201,6203,6204,6206],{},"Há três planos. ",[27,6199,4351],{}," é gratuito permanente, sem limite de servidores ou jobs — roda toda a stack descrita acima incluindo alta disponibilidade real, roteador, certificados, métricas e logs. ",[27,6202,4355],{}," adiciona SSO, RBAC granular, auditoria detalhada, backup gerenciado e suporte com SLA — útil pra quem já tem requisitos formais de plataforma. ",[27,6205,4359],{}," adiciona escrow de código-fonte, suporte 24×7 e desenvolvimento dedicado. Os preços de Business e Enterprise estão publicados na página de planos, sem \"fale com vendas\" obrigatório.",[12,6208,6209],{},"O cluster público de demonstração roda em quatro servidores e a eleição de coordenador acontece em cerca de sete segundos quando o nó atual cai — número medido, não estimado.",[12,6211,5407],{},[19,6213,6215],{"id":6214},"perguntas-que-a-gente-recebe-sobre-saida-da-aws","Perguntas que a gente recebe sobre saída da AWS",[368,6217,6219],{"id":6218},"quanto-tempo-realmente-leva-migrar-uma-stack-media","Quanto tempo realmente leva migrar uma stack média?",[12,6221,6222],{},"Pra startup com cinco a dez aplicações, sem lock-ins profundos: seis a oito semanas com um dev sênior dedicando meio tempo, ou três a quatro semanas com dedicação total. Stacks maiores ou com Lambdas event-driven complexas: três a quatro meses. Stacks com DynamoDB ou Aurora Serverless v2 críticos: vire projeto de re-arquitetura, prazo de seis meses ou mais.",[368,6224,6226],{"id":6225},"dynamodb-tem-alternativa-boa","DynamoDB tem alternativa boa?",[12,6228,6229],{},"Não há substituto idêntico. As opções honestas são: Postgres com JSONB pra a maioria dos casos (resolve oitenta por cento dos usos de DynamoDB com performance excelente), ScyllaDB ou Cassandra self-hosted pra workloads que realmente precisam de NoSQL distribuído, FoundationDB pra quem precisa de transações distribuídas. Nenhum desses é \"mude a connection string e pronto\" — exigem mudança no modelo de dados.",[368,6231,6233],{"id":6232},"posso-manter-aws-pro-banco-e-mover-compute","Posso manter AWS pro banco e mover compute?",[12,6235,6236],{},"Sim, e é a estratégia híbrida mais comum. Aurora ou RDS continua na AWS, EC2 viram VPS Hetzner ou DigitalOcean, S3 vira R2. Você abre VPN entre as duas pontas e o app continua acessando RDS via endpoint privado. Economia tipicamente de cinquenta a setenta por cento da fatura AWS.",[368,6238,6240],{"id":6239},"s3-r2-quanto-custa-transferir-1tb","S3 → R2: quanto custa transferir 1TB?",[12,6242,6243,6244,6246],{},"R2 cobra zero de ingresso. AWS cobra a saída de S3 — aproximadamente nove centavos de dólar por GB nos primeiros 10 TB. Um terabyte custa cerca de noventa dólares pra sair da AWS, R$450. Tempo de transferência: doze a vinte e quatro horas com ",[231,6245,5907],{}," paralelizado, dependendo da banda. Após a migração, R$75 mensais armazenando 50GB com egresso ilimitado, contra R$600 pelo mesmo no S3 com tráfego ativo.",[368,6248,6250],{"id":6249},"lambda-como-migrar-event-driven","Lambda — como migrar event-driven?",[12,6252,6253,6254,6257,6258,6261,6262,6265,6266,6269],{},"Depende do disparador. ",[27,6255,6256],{},"S3 disparando Lambda"," vira R2 com Cloudflare Workers (mesmo padrão, sem mudança radical). ",[27,6259,6260],{},"SQS disparando Lambda"," vira worker persistente no app server, consumindo da fila — geralmente código mais simples que a Lambda original. ",[27,6263,6264],{},"EventBridge agendado"," vira cron no orquestrador. ",[27,6267,6268],{},"EventBridge com regras complexas e Step Functions encadeados"," exige redesign — desenhe o fluxo em torno de uma fila central com workers consumidores, fica mais auditável.",[368,6271,6273],{"id":6272},"rds-multi-az-postgres-self-hosted-e-confiavel","RDS Multi-AZ → Postgres self-hosted é confiável?",[12,6275,6276],{},"Postgres com replicação física streaming e failover via Patroni atinge confiabilidade próxima do RDS Multi-AZ — desde que o time saiba operar. Se ninguém na equipe domina Postgres em produção, o caminho mais seguro é Neon ou Supabase, que entregam Postgres gerenciado com tier gratuito. Pra times com SRE ou DBA, self-hosted é viável e economiza substancial. Pra times sem essa competência, a economia não compensa o risco — pague pelo gerenciado.",[368,6278,6280],{"id":6279},"email-ses-quem-e-mais-barato","Email SES → quem é mais barato?",[12,6282,6283],{},"Depende do volume. Até 10 mil emails mensais, Postmark a US$15 entrega muito mais (entregabilidade superior, dashboard melhor, suporte responsivo). Entre 50 mil e 100 mil mensais, Resend a US$20 é o melhor custo-benefício. Acima de 500 mil mensais, Mailgun ou Amazon SES competem em preço — e SES talvez faça sentido manter mesmo após migrar o resto. Email é dos poucos serviços AWS que pode ser racional manter.",[368,6285,6287],{"id":6286},"dns-tudo-cloudflare-ou-misturar","DNS — tudo Cloudflare ou misturar?",[12,6289,6290],{},"Cloudflare resolve DNS, CDN, DDoS, WAF e workers no plano grátis. Pra a maioria das stacks, concentrar tudo lá simplifica operação e corta custo. A exceção é compliance que exige separação geográfica de provedor — alguns frameworks de governança pedem que DNS e CDN sejam de fornecedores distintos. Nesse caso, Cloudflare DNS + Bunny CDN (ou Fastly) cumpre a separação.",[368,6292,6294],{"id":6293},"compliance-lgpd-muda-algo","Compliance LGPD muda algo?",[12,6296,6297],{},"LGPD não exige hospedagem em território brasileiro. Exige que você saiba onde os dados estão e que tenha contrato adequado com o operador. Hetzner (Alemanha), DigitalOcean (várias regiões), Cloudflare R2 (multi-região) e Magalu Cloud (Brasil) são todos compatíveis com LGPD desde que o contrato esteja em ordem. Pra quem prefere dado em território nacional por preferência de cliente, Magalu Cloud é a alternativa direta.",[12,6299,5407],{},[19,6301,6303],{"id":6302},"proximo-passo-concreto","Próximo passo concreto",[12,6305,6306],{},"Se você chegou até aqui, o próximo passo é a planilha. Lista os doze serviços, marca quais sua stack usa, anota custo atual de cada um, escolhe alternativa. Em uma tarde você sabe se a migração vale o esforço.",[12,6308,6309],{},"Quando estiver pronto pra provisionar o destino:",[224,6311,6312],{"className":226,"code":5319,"language":228,"meta":229,"style":229},[231,6313,6314],{"__ignoreMap":229},[234,6315,6316,6318,6320,6322,6324],{"class":236,"line":237},[234,6317,1220],{"class":247},[234,6319,2958],{"class":251},[234,6321,5330],{"class":255},[234,6323,2964],{"class":383},[234,6325,2967],{"class":247},[12,6327,6328],{},"Roda em qualquer servidor Linux com Docker. Os primeiros três viram quórum pro plano de controle replicado. Você submete jobs via CLI, API ou painel web embutido. O cluster decide onde rodar, faz health check, gerencia rolling deploys, emite certificados Let's Encrypt automaticamente.",[12,6330,6331,6332,2403,6336,101],{},"Pra contexto adicional sobre custos e arquitetura, leia também ",[3337,6333,6335],{"href":6334},"\u002Fblog\u002Faws-ecs-vs-kubernetes-vs-auto-hospedado","AWS ECS vs Kubernetes vs auto-hospedado",[3337,6337,6339],{"href":6338},"\u002Fblog\u002Fquanto-custa-hospedar-saas-brasileiro-2026","Quanto custa hospedar SaaS brasileiro em 2026",[12,6341,6342],{},"A migração é mais aborrecida do que difícil. O difícil é decidir começar.",[3351,6344,4376],{},{"title":229,"searchDepth":244,"depth":244,"links":6346},[6347,6348,6349,6350,6364,6365,6366,6367,6368,6369,6370,6371,6382],{"id":5391,"depth":244,"text":5392},{"id":5410,"depth":244,"text":5411},{"id":5423,"depth":244,"text":5424},{"id":5507,"depth":244,"text":5508,"children":6351},[6352,6353,6354,6355,6356,6357,6358,6359,6360,6361,6362,6363],{"id":5720,"depth":271,"text":5721},{"id":5730,"depth":271,"text":5731},{"id":5753,"depth":271,"text":5754},{"id":5763,"depth":271,"text":5764},{"id":5780,"depth":271,"text":5781},{"id":5790,"depth":271,"text":5791},{"id":5800,"depth":271,"text":5801},{"id":5807,"depth":271,"text":5808},{"id":5817,"depth":271,"text":5818},{"id":5830,"depth":271,"text":5831},{"id":5850,"depth":271,"text":5851},{"id":5860,"depth":271,"text":5861},{"id":5882,"depth":244,"text":5883},{"id":5941,"depth":244,"text":5942},{"id":5980,"depth":244,"text":5981},{"id":6071,"depth":244,"text":6072},{"id":6104,"depth":244,"text":6105},{"id":6135,"depth":244,"text":6136},{"id":6159,"depth":244,"text":6160},{"id":6214,"depth":244,"text":6215,"children":6372},[6373,6374,6375,6376,6377,6378,6379,6380,6381],{"id":6218,"depth":271,"text":6219},{"id":6225,"depth":271,"text":6226},{"id":6232,"depth":271,"text":6233},{"id":6239,"depth":271,"text":6240},{"id":6249,"depth":271,"text":6250},{"id":6272,"depth":271,"text":6273},{"id":6279,"depth":271,"text":6280},{"id":6286,"depth":271,"text":6287},{"id":6293,"depth":271,"text":6294},{"id":6302,"depth":244,"text":6303},"caso-de-uso","2026-05-26","Migrar de AWS pra cloud mais barato (Hetzner\u002FDO) ou auto-hospedado parece projeto de 1 ano. Na prática, dá pra fazer em 6-8 semanas se você mapear os 12 serviços AWS-only que sua stack usa de verdade.",{},"\u002Fblog\u002Fcomo-sair-da-aws-sem-reescrever-toda-stack","16 min",{"title":5383,"description":6385},{"loc":6387},"blog\u002Fcomo-sair-da-aws-sem-reescrever-toda-stack",[6393,6394,6395,6396,6397],"aws","migracao","custo","saida","guia","kqR2CUbhazjyN_A9HwRFTWxS_-3H7sNRPr0KAS0KlYw",{"id":6400,"title":6401,"author":7,"body":6402,"category":3379,"cover":3380,"date":7504,"description":7505,"draft":3383,"extension":3384,"lastReviewed":3380,"meta":7506,"navigation":411,"path":7507,"readingTime":4401,"seo":7508,"sitemap":7509,"stem":7510,"tags":7511,"__hash__":7515},"blog_pt\u002Fblog\u002Fredis-em-producao-gerenciado-vs-self-hosted-valkey.md","Redis (e Valkey) em produção: gerenciado vs auto-hospedado em 2026",{"type":9,"value":6403,"toc":7476},[6404,6418,6420,6440,6444,6447,6450,6456,6463,6467,6470,6476,6482,6488,6491,6497,6502,6507,6510,6516,6521,6526,6529,6535,6540,6550,6554,6557,6601,6605,6608,6612,6615,6647,6650,6654,6657,6671,6678,6682,6696,6699,6703,6736,6739,6743,6746,6760,6767,6771,6774,6855,6858,6862,6865,6871,6877,6884,6887,6891,6894,6926,6929,6933,6940,6943,6946,7242,7246,7249,7275,7279,7282,7308,7312,7315,7336,7345,7348,7351,7355,7380,7394,7400,7409,7415,7421,7427,7433,7435,7438,7444,7447,7463,7474],[12,6405,6406,6407,6410,6411,2403,6414,6417],{},"A pergunta \"Redis gerenciado ou auto-hospedado?\" virou outra pergunta no fim de março de 2024. Foi quando a empresa por trás do Redis trocou a licença de Apache 2.0 \u002F BSD para uma combinação de RSAL com SSPL — um par de licenças \"source available\" desenhadas pra impedir que provedores de nuvem oferecessem Redis como serviço sem licenciamento comercial. A reação foi rápida: a Linux Foundation lançou o ",[27,6408,6409],{},"Valkey"," como fork direto da última versão BSD, com AWS, Google e Oracle bancando o desenvolvimento. Em paralelo, projetos que já existiam — ",[27,6412,6413],{},"KeyDB",[27,6415,6416],{},"Dragonfly"," — passaram a aparecer com mais frequência em benchmarks de empresas que estavam reavaliando a stack.",[19,6419,22],{"id":21},[12,6421,6422,6423,6426,6427,6429,6430,6432,6433,6435,6436,6439],{},"Em 2026, \"Redis em produção\" virou uma categoria com quatro implementações disputando o mesmo protocolo: ",[27,6424,6425],{},"Redis OSS"," (BSD pré-2024 ou RSAL pós), ",[27,6428,6409],{}," (BSD, drop-in pelo fork), ",[27,6431,6413],{}," (multi-thread, fork antigo) e ",[27,6434,6416],{}," (BSL, reescrita do zero em C++). Auto-hospedar qualquer um dos quatro custa entre R$30 e R$130 por mês em VPS Hetzner. O caminho gerenciado custa de R$75 (ElastiCache micro) até R$1.000\u002Fmês (instância de 13 GB), mais Upstash com cobrança serverless variando US$0–100\u002Fmês. Pra startup brasileira com MRR abaixo de R$200k, ",[27,6437,6438],{},"Valkey auto-hospedado"," num cluster próprio economiza entre R$300 e R$1.500 por mês comparado ao gerenciado, elimina exposição à licença RSAL e mantém compatibilidade total com clientes Redis. Trocar a stack depois de adotar a versão comercial é dor real — começar com a versão amigável a OSS é a aposta com menor custo de saída. Este post compara os quatro produtos, os três caminhos gerenciados (ElastiCache, Upstash, Redis Cloud) e a configuração mínima pra rodar Valkey em produção sem perder noites de sono.",[19,6441,6443],{"id":6442},"a-historia-curta-da-mudanca-de-licenca","A história curta da mudança de licença",[12,6445,6446],{},"Antes de março de 2024, \"Redis\" era o cache OSS dominante: BSD, ecossistema gigante, presente em qualquer stack que tivesse cabido a palavra \"Rails\" ou \"Node\" no currículo. O fornecedor comercial — Redis Inc, antiga Redis Labs — vivia bem do produto gerenciado e dos módulos pagos (Search, JSON, TimeSeries).",[12,6448,6449],{},"Aí veio o anúncio: a versão 7.4 em diante sairia sob RSAL + SSPL, não mais BSD. Em termos práticos, a mudança visou diretamente AWS, Google e Azure. A leitura interna de quem produz software open source foi outra: \"se aconteceu com Redis, pode acontecer com qualquer projeto VC-funded\". Foi o terceiro caso recente — depois de Elastic em 2021 e MongoDB em 2018 — em que um projeto que parecia consolidado mudou as regras.",[12,6451,6452,6453,6455],{},"A Linux Foundation foi rápida. Cinco dias após o anúncio, o ",[27,6454,6409],{}," foi formado como fork da última versão BSD (7.2.4), com governança independente e backers de peso: AWS, Google Cloud, Oracle, Ericsson, Snap. Em pouco mais de um ano, a AWS já tinha migrado o motor padrão do ElastiCache pra Valkey. O Google Memorystore seguiu. Em 2026, Valkey deixou de ser \"fork experimental\" pra virar referência crescente — com versões 7.x e 8.x já incorporando otimizações próprias que nem foram ofertadas ao Redis OSS.",[12,6457,6458,6459,6462],{},"A lição operacional pra quem está escolhendo cache hoje: ",[27,6460,6461],{},"o mainstream se moveu",". Não há mais a inércia de \"ninguém foi demitido por escolher Redis\" — a pergunta na entrevista de arquitetura virou \"por que Redis e não Valkey?\". E a resposta honesta, na maioria dos casos, é \"costume\".",[19,6464,6466],{"id":6465},"quais-sao-os-quatro-produtos-disputando-esse-mercado","Quais são os quatro produtos disputando esse mercado?",[368,6468,6425],{"id":6469},"redis-oss",[12,6471,6472,6475],{},[27,6473,6474],{},"O original."," Versões anteriores a 7.4 ainda estão sob BSD e seguem usáveis indefinidamente — ninguém revoga licença retroativa. Versões 7.4 em diante saem sob RSAL\u002FSSPL.",[12,6477,6478,6481],{},[27,6479,6480],{},"Pros",": comunidade ainda enorme, batter-tested em produção há mais de uma década, ecossistema mais rico (módulos, integrações, livros, talks). Quase todo client library testou primeiro contra Redis OSS.",[12,6483,6484,6487],{},[27,6485,6486],{},"Cons",": a RSAL impede oferecer-as-a-service sem licenciamento comercial. Pra quem opera Redis pra uso interno, isso é irrelevante — a restrição é sobre revenda. O risco real é estratégico: se o fornecedor mudou a licença uma vez, pode mudar de novo. Adotar Redis OSS em 2026 significa apostar que a próxima feature crítica vai descer pra ramo aberto, e não ficar no produto comercial.",[368,6489,6409],{"id":6490},"valkey",[12,6492,6493,6496],{},[27,6494,6495],{},"O fork da Linux Foundation."," Pegou o código da 7.2.4 BSD e seguiu desenvolvendo. Drop-in replacement no nível de protocolo: nenhum cliente precisa mudar uma linha de código pra trocar Redis por Valkey.",[12,6498,6499,6501],{},[27,6500,6480],{},": BSD permanente garantido por governança neutra (não é uma empresa, é fundação). Backers grandes alinham incentivos pra manter o projeto saudável. Paridade técnica com Redis 7.x e velocidade de desenvolvimento crescente.",[12,6503,6504,6506],{},[27,6505,6486],{},": a marca ainda está sendo construída — alguns plugins de terceiros e SDKs muito específicos ainda só listam \"Redis\" no README. Em 2026 isso é cada vez mais cosmético, mas pode aparecer em integrações antigas que precisam de pequena adaptação.",[368,6508,6413],{"id":6509},"keydb",[12,6511,6512,6515],{},[27,6513,6514],{},"O fork multi-thread."," Existe desde 2019, foi adquirido pela Snap em 2022, hoje vive como projeto Snap-Telemetry. A diferença arquitetural é fundamental: Redis OSS e Valkey são single-thread por design (uma thread principal processa todos os comandos). KeyDB roda multi-thread por padrão.",[12,6517,6518,6520],{},[27,6519,6480],{},": em CPUs com 4+ núcleos, KeyDB entrega 2 a 3 vezes mais throughput que Redis single-thread no mesmo hardware. API é compatível, então o cliente não muda. Pra workloads CPU-bound com volume alto, é a escolha óbvia.",[12,6522,6523,6525],{},[27,6524,6486],{},": comunidade menor, ritmo de adoção de novas features Redis costuma ficar trimestres atrás. Algumas features novas do Redis (Functions, certas extensões) demoram a aparecer no KeyDB.",[368,6527,6416],{"id":6528},"dragonfly",[12,6530,6531,6534],{},[27,6532,6533],{},"A reescrita."," Não é fork — é implementação nova em C++ moderno, com hash table desenhada pra cache (não a estrutura genérica do Redis), usando io_uring no Linux pra I\u002FO assíncrono. Compatibilidade no nível do protocolo, não no nível do código.",[12,6536,6537,6539],{},[27,6538,6480],{},": claims de 25× throughput em benchmarks específicos (pipelines pesadas em hardware moderno). Memory efficiency real — 2 a 3 vezes mais dados no mesmo RAM que Redis. Sem GIL implícito de single-thread; escala vertical em uma máquina com 32+ cores.",[12,6541,6542,6544,6545,6549],{},[27,6543,6486],{},": licença BSL (Business Source License) — fica fechada por 4 anos antes de virar Apache 2.0. É exatamente o mesmo padrão de licença que pegou outros projetos da indústria de orquestração de surpresa, e que tratamos no nosso post sobre ",[3337,6546,6548],{"href":6547},"\u002Fblog\u002Fpor-que-criamos-o-heroctl","por que criamos o HeroCtl",". Alguns commands ainda incompatíveis com Redis em casos de borda (scripts Lua complexos, certas operações de cluster).",[19,6551,6553],{"id":6552},"qual-escolher-pra-novo-projeto-em-2026","Qual escolher pra novo projeto em 2026?",[12,6555,6556],{},"A árvore de decisão curta:",[2735,6558,6559,6568,6576,6584,6593],{},[70,6560,6561,6564,6565,6567],{},[27,6562,6563],{},"Default sensato",": ",[27,6566,6409],{},". BSD permanente, paridade Redis, cliente não precisa mudar, futuro garantido por backers grandes. Não há razão técnica pra preferir Redis OSS pra projeto novo em 2026.",[70,6569,6570,6564,6573,6575],{},[27,6571,6572],{},"Performance crítica",[27,6574,6416],{},", se a aplicação sustenta acima de 100 mil operações por segundo e o time aceita o risco de licença BSL.",[70,6577,6578,6564,6581,6583],{},[27,6579,6580],{},"Multi-thread sem reescrita",[27,6582,6413],{},", se o gargalo é CPU em hardware grande e o time prefere não migrar pra Dragonfly.",[70,6585,6586,6564,6589,6592],{},[27,6587,6588],{},"Simplicidade extrema (1 VPS, baixo volume)",[27,6590,6591],{},"Redis OSS 7.2.4 BSD"," ainda funciona perfeitamente. Cristalizou como versão estável; vai rodar em qualquer Debian\u002FAlpine pelos próximos cinco anos sem reclamar.",[70,6594,6595,6564,6598,6600],{},[27,6596,6597],{},"Migrando de Redis Labs gerenciado",[27,6599,6409],{}," é drop-in. Zero código mudando. A migração é apenas operacional — replicação, swap de DNS, rollback se necessário.",[19,6602,6604],{"id":6603},"gerenciado-vs-auto-hospedado-a-conta-sem-floreio","Gerenciado vs auto-hospedado: a conta sem floreio",[12,6606,6607],{},"Os números abaixo são preço de tabela em maio de 2026, câmbio R$5\u002FUSD.",[368,6609,6611],{"id":6610},"aws-elasticache","AWS ElastiCache",[12,6613,6614],{},"Cresce em degraus por instância:",[2735,6616,6617,6626,6632,6641],{},[70,6618,6619,6622,6623],{},[231,6620,6621],{},"cache.t4g.micro"," (1 GB): cerca de US$15\u002Fmês = ",[27,6624,6625],{},"R$75\u002Fmês",[70,6627,6628,6631],{},[231,6629,6630],{},"cache.t4g.small"," (2 GB): US$30\u002Fmês = R$150\u002Fmês",[70,6633,6634,6637,6638],{},[231,6635,6636],{},"cache.r6g.large"," (13 GB): cerca de US$200\u002Fmês = ",[27,6639,6640],{},"R$1.000\u002Fmês",[70,6642,6643,6646],{},[231,6644,6645],{},"cache.r6g.xlarge"," (26 GB): cerca de US$400\u002Fmês = R$2.000\u002Fmês",[12,6648,6649],{},"Multi-AZ dobra o preço (réplica em outra zona). Backup automático é incluso. Multi-AZ failover real é o argumento principal — você paga pra não ter que pensar nisso.",[368,6651,6653],{"id":6652},"upstash","Upstash",[12,6655,6656],{},"Cobrança serverless por comando:",[2735,6658,6659,6662,6665,6668],{},[70,6660,6661],{},"Free tier: 256 MB, 500k commands\u002Fdia",[70,6663,6664],{},"Pay-as-you-go: US$0,2 por 100k commands",[70,6666,6667],{},"Pra startup com volume médio (10M commands\u002Fdia): cerca de US$60\u002Fmês = R$300\u002Fmês",[70,6669,6670],{},"Pra app com pico baixo: pode ficar entre US$0 e US$10\u002Fmês",[12,6672,6673,6674,6677],{},"A vantagem operacional é única: ",[27,6675,6676],{},"zero capacidade pré-alocada",". Se o app dorme, a conta dorme. Pra Vercel\u002FCloudflare Workers, é o complemento natural. Pra carga sustentada e previsível, fica mais caro que ElastiCache.",[368,6679,6681],{"id":6680},"redis-cloud-oferta-direta-da-redis-inc","Redis Cloud (oferta direta da Redis Inc)",[2735,6683,6684,6687,6693],{},[70,6685,6686],{},"Plano Essentials 30MB: free",[70,6688,6689,6690],{},"Plano Pro 5GB single-region: cerca de US$50\u002Fmês = ",[27,6691,6692],{},"R$250\u002Fmês",[70,6694,6695],{},"Plano Pro 10GB multi-AZ: cerca de US$120\u002Fmês = R$600\u002Fmês",[12,6697,6698],{},"Inclui módulos comerciais (Search, JSON, TimeSeries) que não existem no Valkey nem no Redis OSS. Se você usa esses módulos, não há alternativa direta — é Redis Cloud ou compra licença comercial e auto-hospeda.",[368,6700,6702],{"id":6701},"auto-hospedado-em-hetzner","Auto-hospedado em Hetzner",[2735,6704,6705,6715,6721,6730],{},[70,6706,6707,6710,6711,6714],{},[27,6708,6709],{},"CPX21"," (3 vCPU, 4 GB RAM, 80 GB SSD): €7,99 = ",[27,6712,6713],{},"R$44\u002Fmês",". Cabe Valkey de 2 GB com folga.",[70,6716,6717,6720],{},[27,6718,6719],{},"CPX31"," (4 vCPU, 8 GB RAM, 160 GB SSD): €13,99 = R$78\u002Fmês.",[70,6722,6723,6726,6727,101],{},[27,6724,6725],{},"Cluster de 3 CPX21 pra Valkey + Sentinel HA",": 3 × €7,99 = €24\u002Fmês = ",[27,6728,6729],{},"R$130\u002Fmês",[70,6731,6732,6735],{},[27,6733,6734],{},"Cluster de 3 CPX31 pra dados sérios",": €42\u002Fmês = R$230\u002Fmês.",[12,6737,6738],{},"Pra DigitalOcean, Linode, Vultr, multiplique por aproximadamente 1,5×. Pra AWS EC2, multiplique por 2×. Mas em qualquer caso fica mais barato que o gerenciado equivalente.",[368,6740,6742],{"id":6741},"diferenca-pratica","Diferença prática",[12,6744,6745],{},"Pra workload de cache de 8 GB com replicação:",[2735,6747,6748,6751,6754,6757],{},[70,6749,6750],{},"ElastiCache Multi-AZ: ~R$1.000\u002Fmês",[70,6752,6753],{},"Redis Cloud Pro Multi-AZ: ~R$600\u002Fmês",[70,6755,6756],{},"Valkey self-hosted em 3× Hetzner CPX31: R$230\u002Fmês",[70,6758,6759],{},"Valkey single-node em 1× Hetzner CPX31 + backup S3: R$80\u002Fmês",[12,6761,6762,6763,6766],{},"Quem escolhe o caminho gerenciado paga ",[27,6764,6765],{},"3 a 10 vezes mais"," pelo mesmo throughput. A diferença é o que você compra com isso: SLA contratual, multi-AZ failover automático, ausência de pager às 3 da manhã. Pra time pequeno, isso pode valer o preço. Pra time que já opera servidores Linux em produção, geralmente não vale.",[19,6768,6770],{"id":6769},"stack-minima-de-valkey-production-grade","Stack mínima de Valkey production-grade",[12,6772,6773],{},"Configuração que aguenta produção real sem teatro:",[2735,6775,6776,6782,6791,6806,6812,6818,6824,6830],{},[70,6777,6778,6781],{},[27,6779,6780],{},"Container ou systemd service em VPS dedicado."," Não compartilha máquina com a aplicação — cache e app concorrem por RAM, e quando dá errado dá errado pros dois ao mesmo tempo.",[70,6783,6784,6790],{},[27,6785,6786,6789],{},[231,6787,6788],{},"maxmemory"," configurado"," entre 50 e 70% do RAM disponível. Sobrar memória pro sistema e pros buffers de rede é mais importante que ter os últimos megabytes pra cache.",[70,6792,6793,6564,6798,6801,6802,6805],{},[27,6794,6795],{},[231,6796,6797],{},"maxmemory-policy",[231,6799,6800],{},"allkeys-lru"," se modo cache puro (jogar fora chaves antigas quando encher). ",[231,6803,6804],{},"noeviction"," se modo storage (queue, sessões) — aí prefira erro de write a perder dados silenciosamente.",[70,6807,6808,6811],{},[27,6809,6810],{},"AOF persistence"," se a carga é fila de jobs (Sidekiq, BullMQ, Resque). Sem AOF, um restart perde qualquer job que estava enfileirado mas não processado. RDB é insuficiente nesse cenário porque snapshot é periódico.",[70,6813,6814,6817],{},[27,6815,6816],{},"RDB suficiente"," se a carga é cache puro (Rails cache, Django cache). Se reiniciar perdendo cache só significa \"request lento por alguns segundos enquanto reaquece\", AOF é overhead desnecessário.",[70,6819,6820,6823],{},[27,6821,6822],{},"Replicação async pra standby"," num segundo nó. Failover manual com swap de DNS interno é aceitável pra muito caso. Failover automático custa Sentinel ou Cluster.",[70,6825,6826,6829],{},[27,6827,6828],{},"Backup AOF + RDB pra S3"," ou compatível, diariamente. Restic ou rclone resolvem bem.",[70,6831,6832,6835,6836,6839,6840,571,6843,571,6846,571,6849,571,6852,101],{},[27,6833,6834],{},"Monitoring"," com ",[231,6837,6838],{},"redis_exporter"," exportando pra Prometheus + alertas no Grafana ou similar. Métricas críticas: ",[231,6841,6842],{},"connected_clients",[231,6844,6845],{},"used_memory",[231,6847,6848],{},"evicted_keys",[231,6850,6851],{},"keyspace_hits\u002Fmisses",[231,6853,6854],{},"latency_percentiles",[12,6856,6857],{},"Esse setup roda confortável em CPX21 (R$44\u002Fmês) servindo 50k+ ops\u002Fs sustentadas pra app brasileira média.",[19,6859,6861],{"id":6860},"sentinel-ou-cluster","Sentinel ou Cluster?",[12,6863,6864],{},"Pergunta que confunde muito time vindo de Redis pela primeira vez.",[12,6866,6867,6870],{},[27,6868,6869],{},"Sentinel",": 1 master + N réplicas + 3+ processos sentinel monitorando. Failover automático quando o master cai — um sentinel detecta, os sentinels votam, uma réplica vira master, clientes recebem novo endpoint via discovery. Tudo num único shard — todo o dataset cabe num nó.",[12,6872,6873,6876],{},[27,6874,6875],{},"Cluster",": dataset particionado em 16384 slots distribuídos em 3+ masters. Cada master tem suas próprias réplicas. Multi-shard, escala horizontal de capacidade — você pode ter 100 GB total com nenhum nó individual segurando mais de 20 GB.",[12,6878,6879,6880,6883],{},"A regra prática: ",[27,6881,6882],{},"Sentinel basta até dataset de ~100 GB",". Acima disso, Cluster é necessário. Pra maioria das startups brasileiras, Sentinel é a escolha certa por simplicidade — Cluster adiciona complexidade real (chave precisa de hashtag pra operações multi-key, scripts Lua ficam restritos a um slot, alguns clientes têm bugs em modo cluster).",[12,6885,6886],{},"Não use Cluster por status. Use Sentinel até a métrica forçar.",[19,6888,6890],{"id":6889},"padroes-de-sidekiq-bullmq-e-companhia","Padrões de Sidekiq, BullMQ e companhia",[12,6892,6893],{},"Use real, não diagrama de marketing:",[2735,6895,6896,6902,6908,6914,6920],{},[70,6897,6898,6901],{},[27,6899,6900],{},"Sidekiq Ruby",": Redis precisa de AOF. Sem AOF, qualquer crash perde os jobs enfileirados que ainda não foram retirados. Sidekiq Pro adiciona \"reliable fetch\" que melhora — mas o backstop ainda é AOF.",[70,6903,6904,6907],{},[27,6905,6906],{},"BullMQ Node",": similar. AOF essential pra durability. BullMQ usa estruturas de dados que dependem de atomicidade transacional do Redis — restart sem AOF pode deixar fila num estado inconsistente.",[70,6909,6910,6913],{},[27,6911,6912],{},"Resque Ruby",": o pai de todos. AOF necessário pelas mesmas razões.",[70,6915,6916,6919],{},[27,6917,6918],{},"Cache puro (Rails.cache, Django cache, Laravel cache)",": pode rodar sem AOF, RDB suficiente. Perder cache num restart é aceitável.",[70,6921,6922,6925],{},[27,6923,6924],{},"Pub\u002Fsub puro",": nem precisa de persistência. Pub\u002Fsub é fire-and-forget por design.",[12,6927,6928],{},"Misturar uso de cache e queue no mesmo Redis funciona — basta configurar AOF (a carga \"pior caso\" determina). Mas pra workload sério, separar em duas instâncias (uma pra cache sem AOF, outra pra queue com AOF) é mais limpo. Operacionalmente baratíssimo se já há orquestrador rodando.",[19,6930,6932],{"id":6931},"elasticache-sao-paulo-e-confiavel","ElastiCache São Paulo é confiável?",[12,6934,6935,6936,6939],{},"Sim — 99.99% uptime SLA contratual, multi-AZ na região São Paulo (",[231,6937,6938],{},"sa-east-1","), backup automático, failover testado. Latência da app brasileira pra ElastiCache São Paulo fica em 1-3ms, indistinguível de Redis local pra maioria dos workloads.",[12,6941,6942],{},"O ponto fraco não é confiabilidade técnica, é custo e lock-in. AWS Brazil cobra cerca de 30% mais caro que regiões norte-americanas pelo mesmo recurso. E migrar de ElastiCache pra outro provedor depois envolve dump\u002Frestore + cutover coordenado — não é apocalipse, mas é trabalho de fim de semana.",[19,6944,6945],{"id":3836},"Tabela comparativa: 12 critérios",[119,6947,6948,6969],{},[122,6949,6950],{},[125,6951,6952,6954,6956,6958,6960,6962,6964,6966],{},[128,6953,2983],{},[128,6955,6425],{},[128,6957,6409],{},[128,6959,6413],{},[128,6961,6416],{},[128,6963,5446],{},[128,6965,6653],{},[128,6967,6968],{},"Self-hosted Valkey",[141,6970,6971,6996,7020,7042,7067,7088,7110,7131,7154,7178,7199,7220],{},[125,6972,6973,6976,6979,6982,6984,6987,6990,6993],{},[146,6974,6975],{},"Licença",[146,6977,6978],{},"RSAL\u002FSSPL (7.4+)",[146,6980,6981],{},"BSD",[146,6983,6981],{},[146,6985,6986],{},"BSL → Apache 4 anos",[146,6988,6989],{},"Comercial AWS",[146,6991,6992],{},"Comercial Upstash",[146,6994,6995],{},"BSD permanente",[125,6997,6998,7001,7004,7006,7009,7011,7014,7017],{},[146,6999,7000],{},"Threading",[146,7002,7003],{},"Single",[146,7005,7003],{},[146,7007,7008],{},"Multi",[146,7010,7008],{},[146,7012,7013],{},"Single (engine 7)",[146,7015,7016],{},"Serverless",[146,7018,7019],{},"Configurável",[125,7021,7022,7025,7028,7030,7032,7035,7037,7040],{},[146,7023,7024],{},"Compat. cliente Redis",[146,7026,7027],{},"100%",[146,7029,7027],{},[146,7031,7027],{},[146,7033,7034],{},"95%+",[146,7036,7027],{},[146,7038,7039],{},"100% (subset comandos)",[146,7041,7027],{},[125,7043,7044,7047,7050,7052,7055,7058,7061,7064],{},[146,7045,7046],{},"Throughput baseline",[146,7048,7049],{},"100k ops\u002Fs",[146,7051,7049],{},[146,7053,7054],{},"250k ops\u002Fs",[146,7056,7057],{},"1M+ ops\u002Fs",[146,7059,7060],{},"depende inst.",[146,7062,7063],{},"depende plano",[146,7065,7066],{},"100-250k ops\u002Fs",[125,7068,7069,7072,7074,7076,7078,7080,7083,7086],{},[146,7070,7071],{},"Persistência AOF",[146,7073,3065],{},[146,7075,3065],{},[146,7077,3065],{},[146,7079,3065],{},[146,7081,7082],{},"Sim (snapshot)",[146,7084,7085],{},"Gerenciada",[146,7087,3065],{},[125,7089,7090,7093,7095,7097,7099,7101,7104,7107],{},[146,7091,7092],{},"Replicação",[146,7094,3065],{},[146,7096,3065],{},[146,7098,3065],{},[146,7100,3065],{},[146,7102,7103],{},"Multi-AZ",[146,7105,7106],{},"Multi-region",[146,7108,7109],{},"Sim (manual config)",[125,7111,7112,7115,7118,7120,7122,7124,7127,7129],{},[146,7113,7114],{},"Failover automático",[146,7116,7117],{},"Sentinel\u002FCluster",[146,7119,7117],{},[146,7121,7117],{},[146,7123,6875],{},[146,7125,7126],{},"Built-in",[146,7128,7126],{},[146,7130,7117],{},[125,7132,7133,7136,7139,7141,7143,7145,7148,7151],{},[146,7134,7135],{},"Custo 8GB\u002Fmês (R$)",[146,7137,7138],{},"80 (VPS)",[146,7140,7138],{},[146,7142,7138],{},[146,7144,7138],{},[146,7146,7147],{},"1000 (Multi-AZ)",[146,7149,7150],{},"300-500",[146,7152,7153],{},"80-230",[125,7155,7156,7159,7162,7165,7167,7170,7173,7176],{},[146,7157,7158],{},"Lock-in",[146,7160,7161],{},"Médio (licença)",[146,7163,7164],{},"Baixo",[146,7166,7164],{},[146,7168,7169],{},"Médio (BSL)",[146,7171,7172],{},"Alto (AWS)",[146,7174,7175],{},"Alto (Upstash API)",[146,7177,7164],{},[125,7179,7180,7183,7186,7188,7190,7192,7195,7197],{},[146,7181,7182],{},"Módulos premium",[146,7184,7185],{},"Pagos",[146,7187,3056],{},[146,7189,3056],{},[146,7191,3056],{},[146,7193,7194],{},"Add-on $$",[146,7196,3062],{},[146,7198,3056],{},[125,7200,7201,7204,7207,7209,7211,7213,7216,7218],{},[146,7202,7203],{},"Operacional",[146,7205,7206],{},"Você",[146,7208,7206],{},[146,7210,7206],{},[146,7212,7206],{},[146,7214,7215],{},"AWS",[146,7217,6653],{},[146,7219,7206],{},[125,7221,7222,7225,7228,7231,7233,7235,7238,7240],{},[146,7223,7224],{},"Suporte SLA",[146,7226,7227],{},"Pago",[146,7229,7230],{},"Comunidade",[146,7232,7230],{},[146,7234,7227],{},[146,7236,7237],{},"Incluso",[146,7239,7237],{},[146,7241,7206],{},[19,7243,7245],{"id":7244},"quando-ainda-gerenciado-faz-sentido","Quando ainda gerenciado faz sentido",[12,7247,7248],{},"A honestidade é mecanismo de defesa de qualquer recomendação técnica. Há quatro perfis em que pagar pelo gerenciado é a escolha certa:",[2735,7250,7251,7257,7263,7269],{},[70,7252,7253,7256],{},[27,7254,7255],{},"Time sem capacidade operacional pra Redis cluster."," Se ninguém na empresa sabe debugar um master que não responde mais, ou interpretar latência de fork RDB, ou cuidar de backup AOF — pagar AWS pra fazer isso é racional. Não é desculpa, é divisão de trabalho.",[70,7258,7259,7262],{},[27,7260,7261],{},"Compliance que exige fornecedor SOC2\u002FISO certificado."," Auditoria que pede \"fornecedor certificado X\" não aceita \"rodamos Valkey num VPS Hetzner\". O caminho é ElastiCache, Redis Cloud ou similar com certificações no contrato.",[70,7264,7265,7268],{},[27,7266,7267],{},"Volume que precisa escalar instantâneo."," Aplicação que vai de 100 req\u002Fs pra 100k req\u002Fs em 5 minutos por causa de campanha viral — o caminho serverless do Upstash é onde brilha. Auto-hospedado precisa de capacidade reservada antes; serverless cresce na hora.",[70,7270,7271,7274],{},[27,7272,7273],{},"Aplicação totalmente serverless."," Se a app roda em Vercel ou Cloudflare Workers e o Redis também precisa ser serverless por modelo de cobrança, Upstash é praticamente a única opção sã. Conectar funções edge a um Redis em VPS implica cold start ruim.",[19,7276,7278],{"id":7277},"quando-auto-hospedar-e-obvio","Quando auto-hospedar é óbvio",[12,7280,7281],{},"E quatro perfis em que pagar gerenciado é desperdício:",[2735,7283,7284,7290,7296,7302],{},[70,7285,7286,7289],{},[27,7287,7288],{},"Startup com R$10k–R$200k MRR otimizando custo."," A diferença entre R$80\u002Fmês e R$1.000\u002Fmês de cache é 1% do custo total dum SaaS pequeno; também é 11 horas de salário pessoa-hora de dev. Vale a pena fazer a conta.",[70,7291,7292,7295],{},[27,7293,7294],{},"Workload predicível."," Se o volume de cache cresce 10% ao mês, não há vantagem em escalar serverless. Capacidade reservada em VPS é mais barata e mais previsível.",[70,7297,7298,7301],{},[27,7299,7300],{},"Time tem 1+ pessoa confortável com Linux\u002FDocker."," Se já tem alguém que opera Postgres, nginx, Docker — Redis\u002FValkey é mais fácil que qualquer um deles. Curva de aprendizado é dias, não semanas.",[70,7303,7304,7307],{},[27,7305,7306],{},"Já existe cluster próprio."," Se a empresa roda orquestrador (HeroCtl, Coolify, plataforma similar) com nós sobrando, Valkey vira só mais um job. Custo marginal próximo de zero — você já paga pelos nós.",[19,7309,7311],{"id":7310},"heroctl-como-infraestrutura-pra-valkey","HeroCtl como infraestrutura pra Valkey",[12,7313,7314],{},"Pra quem opera HeroCtl, rodar Valkey em produção é exercício de configuração curta. Um arquivo de ~30 linhas descreve job com:",[2735,7316,7317,7320,7323,7326,7329],{},[70,7318,7319],{},"Container Valkey 8.x oficial",[70,7321,7322],{},"Volume nomeado replicado entre nós (dados sobrevivem a kill -9 de servidor)",[70,7324,7325],{},"Recursos reservados (RAM e CPU) com limites duros",[70,7327,7328],{},"Health check no ping Valkey",[70,7330,7331,7332,7335],{},"Roteamento interno entre serviços (a app fala com ",[231,7333,7334],{},"valkey.servico.local"," sem expor porta pra internet)",[12,7337,7338,7339,7341,7342,7344],{},"Backup automatizado AOF + RDB pra S3-compatible está disponível no plano ",[27,7340,4355],{}," — sem montar restic externo, sem cron manual no host. Métricas Valkey saem pelo ",[231,7343,6838],{}," rodando como sidecar e aparecem no Prometheus interno (já incluído como job do próprio cluster, sem stack externa).",[12,7346,7347],{},"Failover Sentinel é integrado ao plano de controle do orquestrador: se o nó do master Valkey cai, o cluster detecta em torno de 7 segundos e a réplica é promovida. A configuração da app é atualizada via descoberta de serviço — nenhum redeploy manual.",[12,7349,7350],{},"Pra startup com 4 servidores rodando o orquestrador, esse setup substitui ElastiCache Multi-AZ inteiro a custo marginal zero (os servidores já estão lá). A diferença mensal real é o salário-equivalente de uma pessoa, dependendo do tamanho da operação.",[19,7352,7354],{"id":7353},"perguntas-que-recebemos","Perguntas que recebemos",[12,7356,7357,7360,7361,571,7364,571,7367,571,7370,571,7373,571,7376,7379],{},[27,7358,7359],{},"Valkey é compatível com client libraries Redis?","\nSim, em 100% dos casos práticos. O protocolo é idêntico — ",[231,7362,7363],{},"redis-cli",[231,7365,7366],{},"node-redis",[231,7368,7369],{},"ioredis",[231,7371,7372],{},"redis-rb",[231,7374,7375],{},"redis-py",[231,7377,7378],{},"go-redis",", todos funcionam sem mudar uma linha. A trocar é apenas o endpoint. Em 2026, várias bibliotecas já anunciam suporte explícito ao Valkey no README, mas isso é cosmético — o protocolo é o mesmo.",[12,7381,7382,7385,7386,7389,7390,7393],{},[27,7383,7384],{},"Posso migrar de Redis Labs gerenciado pra Valkey self-hosted sem downtime?","\nSim, com replicação. Configura Valkey como réplica do Redis Labs (",[231,7387,7388],{},"REPLICAOF host port","), espera sincronizar (alguns minutos a horas dependendo do dataset), promove Valkey a master (",[231,7391,7392],{},"REPLICAOF NO ONE","), faz cutover de DNS interno, decomissiona Redis Labs depois de período de observação. Janela de erro real é de segundos durante o swap.",[12,7395,7396,7399],{},[27,7397,7398],{},"Dragonfly vale o risco de BSL?","\nDepende do horizonte da empresa. BSL converte pra Apache 2.0 após 4 anos pelo modelo padrão — então código de hoje será aberto até 2030. O risco é a empresa por trás (DragonflyDB Inc) seguir o caminho da Redis Inc e tornar a conversão menos amigável. Pra workloads que exigem performance que Valkey não entrega (acima de 500k ops\u002Fs sustentado), Dragonfly pode ser a escolha certa apesar do risco. Pra resto, Valkey é mais conservador.",[12,7401,7402,7405,7406,7408],{},[27,7403,7404],{},"Quanto de RAM consome um Redis com 1 GB de dados úteis?","\nConta pratica: dataset de 1 GB ocupa entre 1,3 e 2 GB reais de RAM (overhead de estrutura, fragmentação, buffers de cliente, replication backlog). Configurar ",[231,7407,6788],{}," em 60% do RAM disponível é regra segura — instância de 4 GB cabe ~2,5 GB de dados úteis com folga.",[12,7410,7411,7414],{},[27,7412,7413],{},"Sidekiq precisa AOF mesmo? Os docs do Sidekiq dizem que dá pra rodar sem.","\nOs docs falam que tecnicamente roda. Em produção, sem AOF, qualquer restart inesperado perde jobs enfileirados que estavam no buffer. Pra fila de \"envio de e-mail bem-vindo\", você descobre quando cliente reclama. Pra fila de \"cobrança recorrente\", descobre quando contador reclama. AOF é barato (incremento de 5-10% de I\u002FO), o custo de não ter é grande.",[12,7416,7417,7420],{},[27,7418,7419],{},"Cluster vs Sentinel pra app processando 50k jobs\u002Fdia?","\nSentinel. 50k jobs\u002Fdia é 0,6 ops\u002Fs média — cabem em 100 MB de RAM Redis. Cluster é overkill por ordem de magnitude. Sentinel resolve failover automático com 1 master + 1 réplica + 3 sentinels (3 processos sentinel em VPSs separados, podem coabitar com outras coisas).",[12,7422,7423,7426],{},[27,7424,7425],{},"ElastiCache São Paulo tem latência boa pra app rodando em São Paulo?","\nSim, 1-3ms p99 dentro da mesma AZ. O problema não é latência — é custo e lock-in. Latência só vira tema se a app está em outro provedor (Hetzner FSN, DigitalOcean NYC) tentando falar com ElastiCache São Paulo — aí sobe pra 130-200ms e o argumento desaparece.",[12,7428,7429,7432],{},[27,7430,7431],{},"Como fazer backup de Valkey self-hosted que sobreviva a desastre?","\nTrês camadas. Primeira: AOF persistente em disco local (sobrevive a restart). Segunda: snapshot RDB diário copiado pra storage S3-compatible (Wasabi, Backblaze B2, Cloudflare R2 — todos mais baratos que S3 da AWS pra esse caso). Terceira: snapshot semanal copiado pra outro provedor de storage (segunda região, segundo fornecedor). Restic ou rclone fazem o trabalho. Custo total de armazenagem pra backup de Valkey de 4 GB: cerca de US$1\u002Fmês.",[19,7434,3310],{"id":3309},[12,7436,7437],{},"Em 2026, \"Redis em produção\" virou pergunta com mais nuance do que tinha em 2023. A licença do produto original mudou, o fork Linux Foundation maturou, alternativas multi-thread estão de pé, a oferta serverless tem caso de uso real. Escolher entre os quatro implementações e os três caminhos gerenciados é exercício honesto — não há resposta única.",[12,7439,7440,7441,7443],{},"A nossa recomendação default pra startup brasileira em 2026: ",[27,7442,6438],{}," num cluster próprio, modo Sentinel, AOF ligado se há queue, monitoring com Prometheus. Custo na faixa de R$80–R$230\u002Fmês, contra R$600–R$2.000\u002Fmês das alternativas gerenciadas equivalentes. Compatibilidade total com qualquer biblioteca Redis. Sem exposição à licença RSAL. Migração reversível se virar um problema.",[12,7445,7446],{},"Pra colocar essa stack de pé:",[224,7448,7449],{"className":226,"code":2949,"language":228,"meta":229,"style":229},[231,7450,7451],{"__ignoreMap":229},[234,7452,7453,7455,7457,7459,7461],{"class":236,"line":237},[234,7454,1220],{"class":247},[234,7456,2958],{"class":251},[234,7458,2961],{"class":255},[234,7460,2964],{"class":383},[234,7462,2967],{"class":247},[12,7464,7465,7466,7470,7471,7473],{},"E ler em paralelo: ",[3337,7467,7469],{"href":7468},"\u002Fblog\u002Fpostgres-em-producao-gerenciado-vs-self-hosted","Postgres em produção: gerenciado vs auto-hospedado"," (mesma análise pro banco de dados) e ",[3337,7472,6339],{"href":6338}," (a conta consolidada de toda a stack).",[3351,7475,4376],{},{"title":229,"searchDepth":244,"depth":244,"links":7477},[7478,7479,7480,7486,7487,7494,7495,7496,7497,7498,7499,7500,7501,7502,7503],{"id":21,"depth":244,"text":22},{"id":6442,"depth":244,"text":6443},{"id":6465,"depth":244,"text":6466,"children":7481},[7482,7483,7484,7485],{"id":6469,"depth":271,"text":6425},{"id":6490,"depth":271,"text":6409},{"id":6509,"depth":271,"text":6413},{"id":6528,"depth":271,"text":6416},{"id":6552,"depth":244,"text":6553},{"id":6603,"depth":244,"text":6604,"children":7488},[7489,7490,7491,7492,7493],{"id":6610,"depth":271,"text":6611},{"id":6652,"depth":271,"text":6653},{"id":6680,"depth":271,"text":6681},{"id":6701,"depth":271,"text":6702},{"id":6741,"depth":271,"text":6742},{"id":6769,"depth":244,"text":6770},{"id":6860,"depth":244,"text":6861},{"id":6889,"depth":244,"text":6890},{"id":6931,"depth":244,"text":6932},{"id":3836,"depth":244,"text":6945},{"id":7244,"depth":244,"text":7245},{"id":7277,"depth":244,"text":7278},{"id":7310,"depth":244,"text":7311},{"id":7353,"depth":244,"text":7354},{"id":3309,"depth":244,"text":3310},"2026-05-20","Redis mudou licença em 2024, Valkey nasceu como fork OSS, Dragonfly bate benchmarks. Em 2026, escolher cache não é mais escolher Redis — é escolher entre 4 produtos. Análise honesta com custos.",{},"\u002Fblog\u002Fredis-em-producao-gerenciado-vs-self-hosted-valkey",{"title":6401,"description":7505},{"loc":7507},"blog\u002Fredis-em-producao-gerenciado-vs-self-hosted-valkey",[7512,6490,7513,7514,3379],"redis","cache","self-hosted","ez2Cr-Q_mpvXUsLHY2ePuetIE1kwbfa3rSyoZcJ0VWY",{"id":7517,"title":7518,"author":7,"body":7519,"category":8764,"cover":3380,"date":8765,"description":8766,"draft":3383,"extension":3384,"lastReviewed":3380,"meta":8767,"navigation":411,"path":8768,"readingTime":8769,"seo":8770,"sitemap":8771,"stem":8772,"tags":8773,"__hash__":8778},"blog_pt\u002Fblog\u002Fgithub-actions-vs-gitlab-ci-vs-drone-self-hosted.md","GitHub Actions vs GitLab CI vs Drone: qual CI\u002FCD escolher pra startup brasileira",{"type":9,"value":7520,"toc":8727},[7521,7528,7531,7535,7550,7556,7562,7568,7571,7574,7578,7581,7584,7608,7611,7615,7622,7626,7658,7662,7665,7710,7713,7716,7719,7723,7726,7729,7749,7752,7756,7759,7763,7799,7803,7806,7811,7860,7863,7869,7883,7886,7890,7893,7897,7940,7944,7970,7974,7977,7984,7988,7991,8111,8118,8121,8125,8128,8189,8192,8194,8198,8378,8381,8385,8388,8392,8398,8402,8408,8411,8415,8421,8424,8428,8434,8438,8444,8448,8451,8454,8480,8490,8493,8497,8501,8504,8520,8524,8527,8538,8541,8545,8548,8551,8555,8558,8576,8579,8581,8586,8589,8594,8597,8602,8605,8610,8613,8618,8621,8626,8629,8634,8641,8646,8649,8654,8657,8659,8663,8666,8698,8701,8706,8709,8720],[12,7522,7523,7524,7527],{},"A escolha de CI\u002FCD em 2026 não é mais sobre \"qual ferramenta tem mais features\". Todas as três sérias — GitHub Actions, GitLab CI, Drone (e seu fork Woodpecker) — fazem o básico bem. A escolha real é sobre ",[27,7525,7526],{},"onde a sua dor vai aparecer primeiro",": na fatura no fim do mês, na complexidade do workflow quando o monorepo crescer, ou na hora de subir um runner que ninguém entende quando o dev sênior sai de férias.",[12,7529,7530],{},"Este post é uma comparação honesta pra tech leads brasileiros decidindo CI\u002FCD em 2026. Sem ranking artificial, sem coluna em que uma ferramenta é \"campeã\" em tudo. Tradeoffs explícitos, números em reais, e uma recomendação por perfil no final.",[19,7532,7534],{"id":7533},"tldr-200-palavras","TL;DR (200 palavras)",[12,7536,7537,7538,571,7541,571,7544,2403,7547,101],{},"A decisão de CI\u002FCD em 2026 segue quatro forças: ",[27,7539,7540],{},"onde o código está hospedado",[27,7542,7543],{},"custo de minutos",[27,7545,7546],{},"complexidade de workflow",[27,7548,7549],{},"disposição pra operar self-hosted",[12,7551,7552,7555],{},[27,7553,7554],{},"GitHub Actions"," ganhou mindshare absoluto pra projetos no GitHub. É grátis até 2000 minutos\u002Fmês em repos públicos; depois custa US$0,008\u002Fmin em runner Linux — entre US$5 e US$30\u002Fmês pra startup típica (R$25 a R$150). Marketplace tem 10 mil ações prontas. O calcanhar é o pricing de minutos quando o volume cresce.",[12,7557,7558,7561],{},[27,7559,7560],{},"GitLab CI"," é mais completo: grafo de dependências entre jobs nativo, parent-child pipelines, monorepo handling melhor, registro de imagens incluído, scanning de segurança embutido. Self-hosted (Community Edition) é gratuito mas exige 4 a 8 GB de RAM e operação ativa. SaaS Premium é US$29\u002Fusuário\u002Fmês — caro pra time grande.",[12,7563,7564,7567],{},[27,7565,7566],{},"Drone\u002FWoodpecker"," auto-hospedado é a opção pra reduzir custo a zero variável. Um servidor de R$30 a R$80\u002Fmês roda CI pra cinco a dez projetos. Custa em ops: você opera os runners.",[12,7569,7570],{},"Pra startup BR pequena no GitHub, comece no plano gratuito do Actions. Quando passar de US$30\u002Fmês, considere Woodpecker self-hosted. Pra empresa que valoriza CI + issue tracker + registro num produto só, GitLab self-hosted.",[12,7572,7573],{},"━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━",[19,7575,7577],{"id":7576},"por-que-essa-decisao-importa-mais-do-que-parece","Por que essa decisão importa mais do que parece?",[12,7579,7580],{},"CI\u002FCD é a infraestrutura mais usada de qualquer time de produto: cada commit toca o sistema, cada PR depende dele, cada deploy passa por ele. Uma escolha errada não te quebra no primeiro mês — te quebra no terceiro ano, quando migrar custa quatro semanas de duas pessoas e você paga isso enquanto o roadmap fica parado.",[12,7582,7583],{},"Os três sintomas que indicam que a escolha foi errada:",[67,7585,7586,7592,7602],{},[70,7587,7588,7591],{},[27,7589,7590],{},"A fatura cresce mais rápido do que o time."," Se o custo de CI dobra a cada seis meses sem que o volume de deploys justifique, o pricing model não é o seu.",[70,7593,7594,7597,7598,7601],{},[27,7595,7596],{},"Os workflows viram cópias-cola."," Se cada novo projeto começa com ",[231,7599,7600],{},"cp -r .github\u002Fworkflows\u002F",", a ferramenta não tem composição decente.",[70,7603,7604,7607],{},[27,7605,7606],{},"Falhas no CI levam mais de uma hora pra debugar."," Se reproduzir o erro local exige rodar uma imagem Docker que ninguém sabe montar, o build não é portável.",[12,7609,7610],{},"Os três competidores principais resolvem esses sintomas de formas diferentes. Vamos por partes.",[19,7612,7614],{"id":7613},"github-actions-o-padrao-de-fato-vale-o-preco","GitHub Actions: o padrão de fato — vale o preço?",[12,7616,7617,7618,7621],{},"Se o seu código está no GitHub, Actions tem uma vantagem estrutural que não dá pra ignorar: zero atrito de integração. Você cria ",[231,7619,7620],{},".github\u002Fworkflows\u002Fci.yml",", faz push, e está rodando. Sem cadastro separado, sem token cruzado, sem webhook pra configurar.",[368,7623,7625],{"id":7624},"o-que-actions-faz-bem","O que Actions faz bem",[2735,7627,7628,7634,7640,7646,7652],{},[70,7629,7630,7633],{},[27,7631,7632],{},"Marketplace gigante."," Mais de dez mil ações prontas para tarefas comuns: setup de Node, Python, Go; deploy pra AWS, GCP, Azure; assinatura de imagens; scanning de segurança. A maioria é mantida pelo próprio fornecedor da tecnologia (HashiCorp publica a sua, AWS publica a sua, etc).",[70,7635,7636,7639],{},[27,7637,7638],{},"Matrix builds."," Rodar a mesma suite contra cinco versões de Node ou três sistemas operacionais é uma chave em três linhas.",[70,7641,7642,7645],{},[27,7643,7644],{},"Reusable workflows."," Desde 2021 dá pra extrair workflows compartilhados entre repos da mesma organização — soluciona o problema de \"cópia-cola entre projetos\" pra times médios.",[70,7647,7648,7651],{},[27,7649,7650],{},"Deployment protection rules."," Approvals manuais, janelas de horário, restrição por branch — tudo configurável sem plugin.",[70,7653,7654,7657],{},[27,7655,7656],{},"Self-hosted runners."," Você pode rodar o agente em sua própria infra e usar a UI do Actions só como orquestrador. Resolve o problema de minutos pra times com volume alto.",[368,7659,7661],{"id":7660},"o-que-actions-cobra-caro","O que Actions cobra caro",[12,7663,7664],{},"O modelo de cobrança é por minuto, e os números importam:",[119,7666,7667,7676],{},[122,7668,7669],{},[125,7670,7671,7674],{},[128,7672,7673],{},"Tipo de runner",[128,7675,136],{},[141,7677,7678,7686,7694,7702],{},[125,7679,7680,7683],{},[146,7681,7682],{},"Linux 2 vCPU (padrão)",[146,7684,7685],{},"US$0,008\u002Fmin (R$0,04)",[125,7687,7688,7691],{},[146,7689,7690],{},"Windows 2 vCPU",[146,7692,7693],{},"US$0,016\u002Fmin (R$0,08)",[125,7695,7696,7699],{},[146,7697,7698],{},"macOS (necessário pra builds iOS)",[146,7700,7701],{},"US$0,08\u002Fmin (R$0,40)",[125,7703,7704,7707],{},[146,7705,7706],{},"Larger Linux (4 vCPU+)",[146,7708,7709],{},"US$0,016\u002Fmin e acima",[12,7711,7712],{},"Pra uma startup no GitHub com cinco devs e workflow razoável (build + teste + lint em cada PR), o consumo típico é 800 a 2500 minutos\u002Fmês em Linux. Isso dá US$6 a US$20\u002Fmês — ou seja, entre R$30 e R$100. Cabe na linha de \"ferramentas de dev\" sem dor.",[12,7714,7715],{},"Quando dói: workflows pesados (E2E com Playwright, builds Rust, testes que sobem Postgres + Redis em cada job) facilmente passam de 10 mil minutos\u002Fmês. A US$0,008\u002Fmin isso vira US$80\u002Fmês — R$400. Multiplique por 12 e você está pagando R$5 mil\u002Fano em CI.",[12,7717,7718],{},"Builds macOS são o pior caso: US$0,40\u002Fmin é dez vezes mais que Linux. Times que mantém apps iOS gastam três a quatro vezes mais em CI do que em infra de produção.",[368,7720,7722],{"id":7721},"self-hosted-runners-do-actions-resolvem","Self-hosted runners do Actions resolvem?",[12,7724,7725],{},"Parcialmente. Você roda o binário do runner em uma máquina sua, registra no repo ou na organização, e os jobs vão pra lá em vez do pool gerenciado. Custo de minuto vai pra zero — você paga só a máquina.",[12,7727,7728],{},"Mas três pegadinhas:",[67,7730,7731,7737,7743],{},[70,7732,7733,7736],{},[27,7734,7735],{},"Manutenção do runner."," A versão atualiza com frequência; runners desatualizados começam a falhar silenciosamente. Sem automação, vira tarefa de operação manual.",[70,7738,7739,7742],{},[27,7740,7741],{},"Scaling manual."," Se o time tem cinco devs abrindo 20 PRs simultâneos, um runner serializa tudo. Você precisa de N runners — e provisionar\u002Fdesprovisionar conforme demanda exige tooling adicional.",[70,7744,7745,7748],{},[27,7746,7747],{},"Segurança em repos públicos."," Self-hosted runners em repo público são uma porta aberta pra qualquer fork malicioso rodar código arbitrário na sua máquina. Sempre restrito a repos privados ou organização confiável.",[12,7750,7751],{},"A solução madura é Actions Runner Controller (ARC): um operador que sobe runners on-demand num cluster Kubernetes ou similar. Resolve scaling, mas adiciona uma camada inteira de infraestrutura — não é trivial.",[19,7753,7755],{"id":7754},"gitlab-ci-o-competidor-pesado-ainda-faz-sentido","GitLab CI: o competidor \"pesado\" ainda faz sentido?",[12,7757,7758],{},"GitLab CI é mais antigo do que Actions, mais completo em features, e menos popular fora dos times que já estão na plataforma GitLab. A pergunta correta não é \"GitLab CI é melhor que Actions?\", é \"vale a pena migrar pra GitLab pra usar GitLab CI?\"",[368,7760,7762],{"id":7761},"o-que-gitlab-ci-faz-melhor","O que GitLab CI faz melhor",[2735,7764,7765,7775,7781,7787,7793],{},[70,7766,7767,7770,7771,7774],{},[27,7768,7769],{},"Grafo de dependências (DAG)."," Nativo, sem tooling externo. Você declara ",[231,7772,7773],{},"needs: [job_a, job_b]"," e os jobs rodam em paralelo respeitando dependências. Pra workflows com 30+ jobs (monorepo grande, várias linguagens, deploy multi-ambiente), isso é a diferença entre 8 minutos e 25 minutos por pipeline.",[70,7776,7777,7780],{},[27,7778,7779],{},"Parent-child pipelines."," Pipeline grande pode disparar pipelines filhos com lógica condicional — útil pra monorepo onde só serviços alterados precisam buildar.",[70,7782,7783,7786],{},[27,7784,7785],{},"Registro de imagens incluído."," Cada projeto vem com um registro de container privado nativo. Sem configurar segredos pra Amazon ECR, Docker Hub ou similar.",[70,7788,7789,7792],{},[27,7790,7791],{},"Pages, security scanning, code quality, dependency scanning"," — tudo embutido na plataforma. Em Actions cada um é uma ação separada do marketplace.",[70,7794,7795,7798],{},[27,7796,7797],{},"Merge request integration profundo."," Pipelines aparecem dentro do MR com diff de cobertura, comparação de bundle size, comparação de tempo de build. Em Actions os checks aparecem como links — em GitLab são dados estruturados.",[368,7800,7802],{"id":7801},"onde-gitlab-ci-cobra-caro","Onde GitLab CI cobra caro",[12,7804,7805],{},"Duas dimensões.",[12,7807,7808],{},[27,7809,7810],{},"Pricing SaaS:",[119,7812,7813,7825],{},[122,7814,7815],{},[125,7816,7817,7820,7822],{},[128,7818,7819],{},"Plano",[128,7821,136],{},[128,7823,7824],{},"Limite mensal de minutos",[141,7826,7827,7838,7849],{},[125,7828,7829,7832,7835],{},[146,7830,7831],{},"Free",[146,7833,7834],{},"US$0\u002Fusuário",[146,7836,7837],{},"400 minutos",[125,7839,7840,7843,7846],{},[146,7841,7842],{},"Premium",[146,7844,7845],{},"US$29\u002Fusuário\u002Fmês (R$145)",[146,7847,7848],{},"10.000 minutos",[125,7850,7851,7854,7857],{},[146,7852,7853],{},"Ultimate",[146,7855,7856],{},"US$99\u002Fusuário\u002Fmês (R$495)",[146,7858,7859],{},"50.000 minutos",[12,7861,7862],{},"Pra time de cinco devs no Premium, são US$145\u002Fmês — R$725. Esse é só o ticket de entrada; minutos extras custam à parte. Pra time de 20, US$580\u002Fmês = R$2.900 só em assinatura.",[12,7864,7865,7868],{},[27,7866,7867],{},"Self-hosted Community Edition"," é gratuito e remove esse custo de licença — mas:",[2735,7870,7871,7874,7877,7880],{},[70,7872,7873],{},"Mínimo realista: 4 vCPU, 8 GB RAM (16 GB se vai usar registro + pages + scanning).",[70,7875,7876],{},"VPS adequado no Brasil: R$120 a R$250\u002Fmês.",[70,7878,7879],{},"Ops: 2 a 4 horas\u002Fmês em atualização, backup, monitoração.",[70,7881,7882],{},"Atualizações mensais. GitLab tem cadência rígida; ficar três versões atrás abre brechas de segurança documentadas.",[12,7884,7885],{},"Em produção real, self-hosted GitLab dá menos trabalho do que Kubernetes mas mais do que Actions SaaS. É um servidor real que você opera.",[19,7887,7889],{"id":7888},"drone-ci-e-woodpecker-a-alternativa-minimalista","Drone CI e Woodpecker: a alternativa minimalista",[12,7891,7892],{},"Drone CI nasceu em 2014 como o \"CI nativo de container\": cada step do pipeline é um container, sem mágica. Em 2020 a empresa por trás (Drone Inc.) foi adquirida pela Harness, e o produto ganhou uma versão Cloud comercial. O fork comunitário Woodpecker continua 100% open-source, com API compatível com Drone.",[368,7894,7896],{"id":7895},"o-que-dronewoodpecker-fazem-bem","O que Drone\u002FWoodpecker fazem bem",[2735,7898,7899,7908,7922,7928,7934],{},[70,7900,7901,7904,7905,7907],{},[27,7902,7903],{},"YAML simples."," Cada step declara uma imagem e um comando. Sem DSL, sem actions reutilizáveis com semântica própria. O que você executa localmente com ",[231,7906,2406],{}," é o que executa no CI.",[70,7909,7910,7913,7914,7917,7918,7921],{},[27,7911,7912],{},"Container-native."," Não há \"executor\" Java, não há agente Python rodando steps. Cada step é um container isolado. Reproduzir o erro localmente é literal: copia o ",[231,7915,7916],{},"image:"," e o ",[231,7919,7920],{},"commands:"," do YAML e roda no terminal.",[70,7923,7924,7927],{},[27,7925,7926],{},"Self-hosted desde o dia um."," Não há \"Drone Cloud grátis\" pulling features pra versão paga. O servidor + os runners são o produto inteiro.",[70,7929,7930,7933],{},[27,7931,7932],{},"Plugins via container."," Cada plugin (deploy SSH, Slack, Docker push, AWS) é uma imagem publicada. Versionado igual a qualquer outra dependência.",[70,7935,7936,7939],{},[27,7937,7938],{},"Suporta múltiplos hosts de código."," GitHub, GitLab, Bitbucket, Gitea, Forgejo — tudo no mesmo servidor de Drone.",[368,7941,7943],{"id":7942},"onde-dronewoodpecker-cobram","Onde Drone\u002FWoodpecker cobram",[2735,7945,7946,7952,7958,7964],{},[70,7947,7948,7951],{},[27,7949,7950],{},"Comunidade menor."," Quando você bate em um bug obscuro, o Stack Overflow tem cinco respostas, não cinquenta. Github issues são sua principal fonte.",[70,7953,7954,7957],{},[27,7955,7956],{},"Operação não trivial em escala."," Um servidor + um runner é fácil. Cinco runners autoescalando atrás de uma fila é tooling que você monta — auto-scaling não é built-in.",[70,7959,7960,7963],{},[27,7961,7962],{},"Drone Cloud é pago."," Se você quer SaaS, vai pra Harness; o tier grátis é limitado. Por isso a recomendação é sempre self-hosted.",[70,7965,7966,7969],{},[27,7967,7968],{},"Documentação modesta."," Cobre o caminho feliz; casos de borda você descobre lendo código.",[368,7971,7973],{"id":7972},"por-que-woodpecker-em-vez-de-drone-em-2026","Por que Woodpecker em vez de Drone em 2026",[12,7975,7976],{},"Drone vanilla ainda funciona, mas a Harness tem priorizado a versão cloud comercial. Woodpecker é o fork comunitário do Drone original — 100% open-source, sem versão paga puxando features, releases mensais ativas, comunidade engajada. API e YAML compatíveis com Drone, então migração é trivial: troca a URL do servidor.",[12,7978,7979,7980,7983],{},"Pra qualquer time pequeno auto-hospedando em 2026, ",[27,7981,7982],{},"Woodpecker é a escolha melhor que Drone vanilla",". Mesma arquitetura, sem o overhead de uma empresa controlando o roadmap.",[19,7985,7987],{"id":7986},"qual-mais-barato-em-2026","Qual mais barato em 2026?",[12,7989,7990],{},"Custo total mensal real, considerando time de cinco devs com volume médio (300 builds\u002Fmês, builds de 8 minutos médios em Linux):",[119,7992,7993,8009],{},[122,7994,7995],{},[125,7996,7997,8000,8003,8006],{},[128,7998,7999],{},"Opção",[128,8001,8002],{},"Custo fixo",[128,8004,8005],{},"Custo variável",[128,8007,8008],{},"Total estimado\u002Fmês",[141,8010,8011,8026,8039,8053,8067,8081,8095],{},[125,8012,8013,8016,8019,8022],{},[146,8014,8015],{},"Woodpecker self-hosted (VPS R$80)",[146,8017,8018],{},"R$80",[146,8020,8021],{},"R$0",[146,8023,8024],{},[27,8025,8018],{},[125,8027,8028,8031,8033,8035],{},[146,8029,8030],{},"Actions repos públicos (open-source)",[146,8032,8021],{},[146,8034,8021],{},[146,8036,8037],{},[27,8038,8021],{},[125,8040,8041,8044,8046,8049],{},[146,8042,8043],{},"Actions repos privados (free tier 2000 min)",[146,8045,8021],{},[146,8047,8048],{},"R$0 a R$50",[146,8050,8051],{},[27,8052,8048],{},[125,8054,8055,8058,8060,8063],{},[146,8056,8057],{},"Actions Linux pago (volume médio)",[146,8059,8021],{},[146,8061,8062],{},"R$50 a R$150",[146,8064,8065],{},[27,8066,8062],{},[125,8068,8069,8072,8075,8077],{},[146,8070,8071],{},"GitLab CI self-hosted (VPS R$200)",[146,8073,8074],{},"R$200",[146,8076,8021],{},[146,8078,8079],{},[27,8080,8074],{},[125,8082,8083,8086,8088,8091],{},[146,8084,8085],{},"Actions com builds macOS pesados",[146,8087,8021],{},[146,8089,8090],{},"R$300 a R$1.500",[146,8092,8093],{},[27,8094,8090],{},[125,8096,8097,8100,8103,8106],{},[146,8098,8099],{},"GitLab CI SaaS Premium (5 devs)",[146,8101,8102],{},"R$725",[146,8104,8105],{},"R$0 a R$200",[146,8107,8108],{},[27,8109,8110],{},"R$725 a R$925",[12,8112,8113,8114,8117],{},"Vencedor absoluto em custo: ",[27,8115,8116],{},"Woodpecker self-hosted"," pra time disposto a operar uma VPS. Custa o mesmo que um almoço por mês e roda CI pra dez projetos sem sentir.",[12,8119,8120],{},"Se ops não está disponível, Actions plano gratuito é a próxima opção. Ele cabe num time pequeno com workflows leves; quando passa de US$30\u002Fmês variável, vale a pena pelo menos avaliar runners self-hosted.",[19,8122,8124],{"id":8123},"qual-tem-melhor-experiencia-de-desenvolvedor","Qual tem melhor experiência de desenvolvedor?",[12,8126,8127],{},"DX em CI\u002FCD se mede em três dimensões: tempo do \"yml em branco\" até o \"primeiro build passando\", capacidade de debug quando dá errado, e capacidade de evoluir o workflow quando ele cresce.",[119,8129,8130,8140],{},[122,8131,8132],{},[125,8133,8134,8137],{},[128,8135,8136],{},"Dimensão",[128,8138,8139],{},"Vencedor",[141,8141,8142,8150,8158,8166,8174,8182],{},[125,8143,8144,8147],{},[146,8145,8146],{},"Templates prontos \u002F acessibilidade",[146,8148,8149],{},"GitHub Actions (marketplace + onboarding)",[125,8151,8152,8155],{},[146,8153,8154],{},"Workflows complexos \u002F DAG \u002F monorepo",[146,8156,8157],{},"GitLab CI (parent-child + needs nativo)",[125,8159,8160,8163],{},[146,8161,8162],{},"Reprodução local \u002F simplicidade conceitual",[146,8164,8165],{},"Drone\u002FWoodpecker (cada step = container)",[125,8167,8168,8171],{},[146,8169,8170],{},"Debug de falha intermitente",[146,8172,8173],{},"Drone\u002FWoodpecker (re-rodar step isolado é trivial)",[125,8175,8176,8179],{},[146,8177,8178],{},"Composição entre projetos",[146,8180,8181],{},"GitHub Actions (reusable workflows + composite actions)",[125,8183,8184,8187],{},[146,8185,8186],{},"Time-to-first-pipeline (zero pra hello world)",[146,8188,7554],{},[12,8190,8191],{},"Não há vencedor absoluto. Pra time que valoriza começar rápido, Actions. Pra time que tem workflow complexo desde o dia um (monorepo, várias linguagens), GitLab CI. Pra time que quer entender exatamente o que está acontecendo, Drone\u002FWoodpecker.",[12,8193,7573],{},[19,8195,8197],{"id":8196},"tabela-comparativa-12-criterios-honestos","Tabela comparativa: 12 critérios honestos",[119,8199,8200,8212],{},[122,8201,8202],{},[125,8203,8204,8206,8208,8210],{},[128,8205,2983],{},[128,8207,7554],{},[128,8209,7560],{},[128,8211,7566],{},[141,8213,8214,8227,8241,8255,8269,8283,8297,8311,8325,8338,8350,8364],{},[125,8215,8216,8219,8222,8225],{},[146,8217,8218],{},"Custo mensal startup BR (5 devs, volume médio)",[146,8220,8221],{},"R$0 a R$150",[146,8223,8224],{},"R$80 a R$925",[146,8226,8018],{},[125,8228,8229,8232,8235,8238],{},[146,8230,8231],{},"Free tier real (2026)",[146,8233,8234],{},"2000 min\u002Fmês privados, ilimitado público",[146,8236,8237],{},"400 min\u002Fmês SaaS",[146,8239,8240],{},"Ilimitado self-hosted",[125,8242,8243,8246,8249,8252],{},[146,8244,8245],{},"Self-hosted disponível",[146,8247,8248],{},"Sim (runners), SaaS UI",[146,8250,8251],{},"Sim (CE completa)",[146,8253,8254],{},"Sim (é a única forma sensata)",[125,8256,8257,8260,8263,8266],{},[146,8258,8259],{},"Complexidade de workflow grande",[146,8261,8262],{},"Boa (reusable workflows)",[146,8264,8265],{},"Excelente (DAG + parent-child)",[146,8267,8268],{},"Modesta (linear + matrix)",[125,8270,8271,8274,8277,8280],{},[146,8272,8273],{},"Suporte a monorepo",[146,8275,8276],{},"Médio (paths filter)",[146,8278,8279],{},"Excelente (rules + parent-child)",[146,8281,8282],{},"Médio (when filter)",[125,8284,8285,8288,8291,8294],{},[146,8286,8287],{},"Registro de containers integrado",[146,8289,8290],{},"Não (precisa GHCR à parte)",[146,8292,8293],{},"Sim, nativo",[146,8295,8296],{},"Não (use registro externo)",[125,8298,8299,8302,8305,8308],{},[146,8300,8301],{},"Gestão de segredos",[146,8303,8304],{},"Repo + org + environment",[146,8306,8307],{},"Project + group + instance",[146,8309,8310],{},"Server + repo",[125,8312,8313,8316,8319,8322],{},[146,8314,8315],{},"Jobs paralelos out-of-box",[146,8317,8318],{},"Sim (matrix)",[146,8320,8321],{},"Sim (parallel + DAG)",[146,8323,8324],{},"Sim (depends_on)",[125,8326,8327,8330,8332,8335],{},[146,8328,8329],{},"Comunidade BR \u002F material em português",[146,8331,4916],{},[146,8333,8334],{},"Médio",[146,8336,8337],{},"Pequeno",[125,8339,8340,8342,8345,8347],{},[146,8341,4896],{},[146,8343,8344],{},"Parcial (oficial em inglês)",[146,8346,3140],{},[146,8348,8349],{},"Praticamente zero",[125,8351,8352,8355,8358,8361],{},[146,8353,8354],{},"Integração com GitHub\u002FGitLab\u002FGitea",[146,8356,8357],{},"Só GitHub",[146,8359,8360],{},"Só GitLab (mirror externo é workaround)",[146,8362,8363],{},"Todos os três + Bitbucket",[125,8365,8366,8369,8372,8375],{},[146,8367,8368],{},"Faixa ideal de uso",[146,8370,8371],{},"1 a 50 devs no GitHub",[146,8373,8374],{},"5 a 500 devs em uma plataforma só",[146,8376,8377],{},"1 a 30 devs com ops disponível",[12,8379,8380],{},"Nenhum competidor tem coluna sem ressalva. A ferramenta certa depende do perfil do time.",[19,8382,8384],{"id":8383},"decisao-por-perfil-de-time","Decisão por perfil de time",[12,8386,8387],{},"Quatro recomendações concretas, sem \"depende\".",[368,8389,8391],{"id":8390},"indie-hacker-ou-repo-publico-no-github","Indie hacker ou repo público no GitHub",[12,8393,8394,8397],{},[27,8395,8396],{},"Use GitHub Actions plano gratuito."," Repos públicos têm minutos ilimitados. Você não tem motivo pra procurar alternativa. Se daqui a um ano o projeto crescer, você reavalia.",[368,8399,8401],{"id":8400},"startup-early-no-github-repos-privados-r10k-a-r50k-mrr","Startup early no GitHub, repos privados, R$10k a R$50k MRR",[12,8403,8404,8407],{},[27,8405,8406],{},"Continue no Actions plano gratuito."," O free tier de 2000 minutos cabe em um time de dois a três devs com workflows razoáveis. Quando começar a passar disso, primeiro reduza desperdício (paths filter pra não rodar tudo em todo PR, cache de dependências decente) antes de migrar.",[12,8409,8410],{},"Se passar consistentemente de US$30\u002Fmês variável, considere migrar pra runners self-hosted ou Woodpecker em paralelo.",[368,8412,8414],{"id":8413},"startup-com-r50k-a-r200k-mrr-no-github-volume-ci-alto","Startup com R$50k a R$200k MRR no GitHub, volume CI alto",[12,8416,8417,8420],{},[27,8418,8419],{},"Híbrido."," Use Actions pra workflows leves (lint, testes unitários) e self-hosted runners (via ARC) ou Woodpecker pra workflows pesados (E2E, builds longos, deploys). Você paga por minuto onde compensa e zero onde dói.",[12,8422,8423],{},"Pra times com builds macOS regulares, considere uma máquina Mac mini dedicada como self-hosted runner. Investimento de R$10 mil paga em três meses se você gasta US$200\u002Fmês em macOS Actions hoje.",[368,8425,8427],{"id":8426},"empresa-br-no-gitlab-self-hosted","Empresa BR no GitLab self-hosted",[12,8429,8430,8433],{},[27,8431,8432],{},"Use GitLab CI nativo."," Você já está pagando o custo de operar GitLab; CI vem junto sem custo adicional. Migrar pra outra ferramenta significaria operar dois sistemas em paralelo — não vale.",[368,8435,8437],{"id":8436},"time-pequeno-controlando-custo-agressivamente","Time pequeno controlando custo agressivamente",[12,8439,8440,8443],{},[27,8441,8442],{},"Woodpecker self-hosted em VPS R$80."," Roda CI pra dez projetos sem suar. Custa em ops 1 a 2 horas\u002Fmês. Se o time tem alguém com afinidade por ferramentas Unix, é a opção mais econômica e mais previsível em conta — você sabe exatamente o custo todo mês.",[19,8445,8447],{"id":8446},"onde-heroctl-entra-como-infraestrutura-pra-runners","Onde HeroCtl entra como infraestrutura pra runners",[12,8449,8450],{},"Self-hosted CI\u002FCD é exatamente o tipo de carga de trabalho que o HeroCtl orquestra bem: serviços longos (servidor de CI, banco que mantém histórico de builds), serviços que escalam horizontalmente (runners que sobem e descem com a fila), serviços com necessidade de persistência (cache de artefatos).",[12,8452,8453],{},"Em vez de operar Docker Compose num servidor único — single point of failure — você descreve o setup como uma configuração de jobs:",[2735,8455,8456,8462,8468,8474],{},[70,8457,8458,8461],{},[27,8459,8460],{},"Servidor de Drone\u002FWoodpecker como job longo",", com replica única e volume persistente pro banco de histórico.",[70,8463,8464,8467],{},[27,8465,8466],{},"N runners como job replicável",", escalando horizontalmente. O orquestrador distribui os runners entre nós; se um servidor morre, os runners migram pros outros.",[70,8469,8470,8473],{},[27,8471,8472],{},"Backup integrado"," pro estado do CI (banco do servidor + cache de artefatos), sem montar tooling externo.",[70,8475,8476,8479],{},[27,8477,8478],{},"Métricas e logs integrados"," — você vê uso de CPU, memória, tempo de build sem subir um stack de observabilidade separado.",[12,8481,8482,8483,2630,8486,8489],{},"A diferença prática: em vez de operar um stack de CI em paralelo ao seu cluster de produção, ele vira parte do mesmo cluster, com as mesmas garantias de alta disponibilidade. Se um servidor cai, os runners migram. Se você quer dobrar capacidade pra uma sprint pesada, é mudar ",[231,8484,8485],{},"replicas: 4",[231,8487,8488],{},"replicas: 8"," no arquivo de configuração.",[12,8491,8492],{},"Pra quem está na fronteira \"começo simples mas vou crescer\", isso resolve a transição sem precisar trocar de ferramenta no meio do caminho.",[19,8494,8496],{"id":8495},"os-4-erros-caros-em-cicd-self-hosted-e-como-evita-los","Os 4 erros caros em CI\u002FCD self-hosted (e como evitá-los)",[368,8498,8500],{"id":8499},"erro-1-cache-stale-silencioso","Erro 1: cache stale silencioso",[12,8502,8503],{},"O sintoma: build passa local, falha no CI por causa de uma dependência que existe na máquina do dev mas não na imagem fresca. Pior caso: passa no CI também porque cache anterior contém a dependência, mas falha em produção quando a imagem é construída sem cache.",[12,8505,8506,8507,571,8510,571,8513,571,8516,8519],{},"A correção: cache decente assume que pode ser invalidado a qualquer momento. Sempre que mudar arquivos de manifesto de dependências (",[231,8508,8509],{},"package.json",[231,8511,8512],{},"go.mod",[231,8514,8515],{},"requirements.txt",[231,8517,8518],{},"Cargo.toml","), incluí-los na chave de cache. Periodicamente (semanal), forçar build sem cache pra detectar drift.",[368,8521,8523],{"id":8522},"erro-2-secret-commitado-por-acidente","Erro 2: secret commitado por acidente",[12,8525,8526],{},"O sintoma: alguém colou um token na configuração de CI \"só pra testar\", commitou, esqueceu. O repo é público; em 12 horas o token está em uso por quem não devia.",[12,8528,8529,8530,8533,8534,8537],{},"A correção: dois mecanismos em camadas. ",[27,8531,8532],{},"Pre-commit hook"," que escaneia padrões de chaves comuns (AWS, Stripe, GitHub PAT). ",[27,8535,8536],{},"Rotação automática"," de tokens críticos (90 dias máximo). Se um token vazar, a janela de exposição é finita.",[12,8539,8540],{},"Em GitLab CI, use variables com flag \"masked\" e \"protected\". Em Actions, use environment-scoped secrets com approval rules. Em Drone\u002FWoodpecker, segredos são escopados por repo e nunca aparecem em logs por padrão.",[368,8542,8544],{"id":8543},"erro-3-runner-rodando-no-mesmo-servidor-de-producao","Erro 3: runner rodando no mesmo servidor de produção",[12,8546,8547],{},"O sintoma: build pesado consome CPU\u002FRAM, produção fica lenta, latência sobe, alarme dispara, on-call acorda. Caso real comum em times pequenos que tentam economizar máquina.",[12,8549,8550],{},"A correção: runners em servidor separado de produção, sempre. Se o orçamento aperta, runner em VPS de R$30\u002Fmês ainda é mais barato do que um incidente de produção em horário comercial.",[368,8552,8554],{"id":8553},"erro-4-workflow-que-nao-roda-fora-do-ci","Erro 4: workflow que não roda fora do CI",[12,8556,8557],{},"O sintoma: o build do CI é um script de 200 linhas inline no YAML, com 15 variáveis de ambiente que o sistema injeta. Quando algo dá errado, ninguém consegue reproduzir local sem fazer engenharia reversa do YAML.",[12,8559,8560,8561,571,8564,8567,8568,8571,8572,8575],{},"A correção: o CI deve chamar comandos que existem como ",[231,8562,8563],{},"Makefile",[231,8565,8566],{},"script\u002Fbuild",", ou ",[231,8569,8570],{},"package.json scripts",". O YAML do CI orquestra; a lógica vive em scripts versionados que rodam em qualquer terminal. Se você não consegue rodar ",[231,8573,8574],{},"make ci"," localmente e ver o mesmo resultado, o seu CI não é portável.",[12,8577,8578],{},"Drone\u002FWoodpecker força essa disciplina por design (cada step é um container). Actions e GitLab CI permitem o anti-padrão; cabe ao time evitar.",[19,8580,4244],{"id":4243},[12,8582,8583],{},[27,8584,8585],{},"GitHub Actions é mais rápido que Drone?",[12,8587,8588],{},"Em build cru, depende do runner: o pool gerenciado do Actions usa máquinas de 2 vCPU; um runner self-hosted em uma máquina de 4 vCPU é mais rápido. Em tempo total de pipeline (incluindo fila), Actions ganha quando há volume — eles têm capacidade ociosa enorme. Self-hosted (qualquer ferramenta) tem fila proporcional ao número de runners que você provisiona.",[12,8590,8591],{},[27,8592,8593],{},"Posso usar GitLab CI com repo no GitHub?",[12,8595,8596],{},"Tecnicamente sim, via \"pull mirror\" (GitLab espelha o GitHub e roda CI nele). Na prática é frágil: webhooks atrasam, status checks não voltam pro GitHub do jeito que o time espera, MRs ficam confusos. Não vale a pena. Se você está no GitHub, use Actions ou Drone\u002FWoodpecker (que aceitam GitHub como fonte nativa).",[12,8598,8599],{},[27,8600,8601],{},"Self-hosted runners do GitHub Actions valem a pena?",[12,8603,8604],{},"Pra repos privados com volume alto (mais de 5000 minutos\u002Fmês), sim. Você economiza minutos pagos em troca de operar máquinas. Pra repos públicos, não — risco de segurança (forks maliciosos rodando código na sua máquina) supera benefício. ARC (Actions Runner Controller) ajuda em escala, mas adiciona uma camada de Kubernetes; só faz sentido pra times que já operam K8s.",[12,8606,8607],{},[27,8608,8609],{},"Woodpecker é estável o suficiente em 2026?",[12,8611,8612],{},"Sim. Releases mensais, base de código sólida (forkado de Drone, que tinha cinco anos de produção), comunidade ativa. Em produção em centenas de empresas pequenas e médias. Não é a aposta segura \"ninguém é demitido por escolher\" — essa é Actions ou GitLab — mas em três anos de fork não houve incidente comunitário grave. Pra time pequeno self-hosted, é a escolha sensata.",[12,8614,8615],{},[27,8616,8617],{},"ArgoCD e FluxCD entram nessa decisão?",[12,8619,8620],{},"Não diretamente. ArgoCD\u002FFluxCD são ferramentas de GitOps pra Kubernetes, não CI. Eles assistem um repo Git e aplicam mudanças em cluster. CI continua sendo Actions\u002FGitLab\u002FDrone gerando imagens; ArgoCD\u002FFlux aplicam o deploy. Se você não está em Kubernetes, ArgoCD\u002FFlux não são pra você. Times em outros orquestradores fazem deploy direto do CI ou via APIs do orquestrador.",[12,8622,8623],{},[27,8624,8625],{},"Quantos runners simultâneos pra time de 5 devs?",[12,8627,8628],{},"Regra prática: um runner por dois desenvolvedores ativos, mais um runner extra pra builds longos não bloquear PRs rápidos. Time de cinco devs: três runners é confortável. Em horários de pico (release day), suba pra cinco temporariamente. Cada runner consome 1 a 2 GB de RAM em workload típica; um servidor de 8 GB roda quatro runners sem dor.",[12,8630,8631],{},[27,8632,8633],{},"Cache de dependências — qual ferramenta lida melhor?",[12,8635,8636,8637,8640],{},"GitLab CI tem cache nativo por chave\u002Fpath, integrado ao registro próprio. GitHub Actions tem ",[231,8638,8639],{},"actions\u002Fcache"," (gratuito, 10 GB por repo). Drone\u002FWoodpecker dependem de plugin de cache externo (S3, MinIO local) — mais setup mas mais flexível. Em volume moderado, todos resolvem; em volume alto (monorepo grande), GitLab tem vantagem por integração com o registro.",[12,8642,8643],{},[27,8644,8645],{},"Migrar de GitHub Actions pra Drone — quanto trabalho?",[12,8647,8648],{},"Pra workflows simples (build + teste + push), 1 a 2 dias. Pra workflows que dependem de muitas ações do marketplace, 1 a 2 semanas (precisa reescrever cada ação como container). A maior dor é segredos e ambientes — exporte e reimporte com cuidado. Recomendação: faça migração projeto a projeto, não tudo de uma vez.",[12,8650,8651],{},[27,8652,8653],{},"Posso rodar runners de Actions e Drone\u002FWoodpecker no mesmo servidor?",[12,8655,8656],{},"Tecnicamente sim, ambos são containers. Na prática, isolamento melhora: runners em servidores separados evitam que um build pesado afete o outro. Se o orçamento é apertado, dois servidores de R$40\u002Fmês são melhores que um de R$80\u002Fmês com tudo junto.",[12,8658,7573],{},[19,8660,8662],{"id":8661},"em-resumo","Em resumo",[12,8664,8665],{},"CI\u002FCD em 2026 não tem ferramenta vencedora. Tem perfis de uso e tradeoffs honestos:",[2735,8667,8668,8674,8680,8686,8692],{},[70,8669,8670,8673],{},[27,8671,8672],{},"Você está no GitHub e o volume é leve a médio?"," Actions, plano gratuito. Não procure problema onde não tem.",[70,8675,8676,8679],{},[27,8677,8678],{},"Você está no GitLab self-hosted?"," GitLab CI nativo. Já está pago.",[70,8681,8682,8685],{},[27,8683,8684],{},"Você quer custo previsível e tem 1-2h\u002Fmês de ops disponível?"," Woodpecker self-hosted em VPS R$80. A escolha mais econômica.",[70,8687,8688,8691],{},[27,8689,8690],{},"Você tem monorepo grande com workflow complexo?"," GitLab CI (DAG nativo) ou Actions com reusable workflows.",[70,8693,8694,8697],{},[27,8695,8696],{},"Você tem volume alto e dor de pricing de minutos?"," Híbrido: Actions pra workflows leves, runners self-hosted pra pesados.",[12,8699,8700],{},"Se você está pensando em rodar a ferramenta de CI como parte do mesmo cluster que serve produção — com alta disponibilidade real, métricas integradas e backup sem montar stack à parte — instale o HeroCtl em um servidor:",[224,8702,8704],{"className":8703,"code":2949,"language":2530},[2528],[231,8705,2949],{"__ignoreMap":229},[12,8707,8708],{},"A partir daí, descrever um servidor de Woodpecker com três runners auto-escaláveis é um arquivo de configuração de cinquenta linhas. O cluster cuida do resto: distribui os runners pelos nós, mantém o servidor disponível mesmo com perda de máquina, faz backup do estado, expõe métricas no painel embutido.",[12,8710,8711,8712,8714,8715,8719],{},"Pra quem quer mais contexto, vale ler também ",[3337,8713,3345],{"href":3344}," — discute quando faz sentido sair de docker-compose pra um plano de controle replicado, com os mesmos critérios honestos deste post. E pra times pensando em simplificar a stack de orquestração inteira, ",[3337,8716,8718],{"href":8717},"\u002Fblog\u002Fmigrar-de-kubernetes-pra-stack-mais-simples-case","Migrar de Kubernetes pra uma stack mais simples — case real"," tem números de uma migração real, com ganhos e dores.",[12,8721,8722,8723,8726],{},"A escolha de CI\u002FCD é uma das decisões mais duradouras do time. Vale alguns dias de comparação honesta antes de copiar o ",[231,8724,8725],{},".github\u002Fworkflows\u002F"," do projeto anterior — porque três anos depois, migrar custa caro.",{"title":229,"searchDepth":244,"depth":244,"links":8728},[8729,8730,8731,8736,8740,8745,8746,8747,8748,8755,8756,8762,8763],{"id":7533,"depth":244,"text":7534},{"id":7576,"depth":244,"text":7577},{"id":7613,"depth":244,"text":7614,"children":8732},[8733,8734,8735],{"id":7624,"depth":271,"text":7625},{"id":7660,"depth":271,"text":7661},{"id":7721,"depth":271,"text":7722},{"id":7754,"depth":244,"text":7755,"children":8737},[8738,8739],{"id":7761,"depth":271,"text":7762},{"id":7801,"depth":271,"text":7802},{"id":7888,"depth":244,"text":7889,"children":8741},[8742,8743,8744],{"id":7895,"depth":271,"text":7896},{"id":7942,"depth":271,"text":7943},{"id":7972,"depth":271,"text":7973},{"id":7986,"depth":244,"text":7987},{"id":8123,"depth":244,"text":8124},{"id":8196,"depth":244,"text":8197},{"id":8383,"depth":244,"text":8384,"children":8749},[8750,8751,8752,8753,8754],{"id":8390,"depth":271,"text":8391},{"id":8400,"depth":271,"text":8401},{"id":8413,"depth":271,"text":8414},{"id":8426,"depth":271,"text":8427},{"id":8436,"depth":271,"text":8437},{"id":8446,"depth":244,"text":8447},{"id":8495,"depth":244,"text":8496,"children":8757},[8758,8759,8760,8761],{"id":8499,"depth":271,"text":8500},{"id":8522,"depth":271,"text":8523},{"id":8543,"depth":271,"text":8544},{"id":8553,"depth":271,"text":8554},{"id":4243,"depth":244,"text":4244},{"id":8661,"depth":244,"text":8662},"comparativo","2026-05-15","GitHub Actions venceu mindshare mas tem custos de minutos. GitLab CI é mais completo mas pesa mais. Drone (e Woodpecker) auto-hospedado roda em VPS pequeno. Comparação prática.",{},"\u002Fblog\u002Fgithub-actions-vs-gitlab-ci-vs-drone-self-hosted","14 min",{"title":7518,"description":8766},{"loc":8768},"blog\u002Fgithub-actions-vs-gitlab-ci-vs-drone-self-hosted",[8774,8775,8776,8777,8764],"github-actions","gitlab-ci","drone","ci-cd","6HE70abQMrpxYtjVIHfg4cJABlHozxJVxg5Q13QBw0M",{"id":8780,"title":8781,"author":7,"body":8782,"category":3379,"cover":3380,"date":11779,"description":11780,"draft":3383,"extension":3384,"lastReviewed":3380,"meta":11781,"navigation":411,"path":11782,"readingTime":6388,"seo":11783,"sitemap":11784,"stem":11785,"tags":11786,"__hash__":11791},"blog_pt\u002Fblog\u002Fmonitoring-stack-completa-prometheus-grafana-loki-passo-a-passo.md","Monitoring stack completa em 2026: Prometheus + Grafana + Loki passo a passo",{"type":9,"value":8783,"toc":11759},[8784,8797,8800,8802,8809,8820,8823,8826,8830,8833,8871,8886,8890,8893,8926,8929,8933,8939,8942,8945,8972,8986,8990,8995,9002,9008,9015,9363,9382,9385,9423,9446,9450,9455,9461,9741,9751,9770,9774,9780,9783,9862,9882,9888,9926,9936,9940,9944,9950,10204,10207,10210,10397,10407,10414,10418,10423,10432,10438,10547,10554,10561,10581,10588,10592,10596,10599,10609,10947,10954,11172,11183,11197,11201,11205,11211,11234,11241,11245,11250,11253,11256,11289,11292,11324,11333,11344,11348,11351,11354,11380,11383,11387,11437,11440,11443,11447,11578,11581,11585,11588,11598,11608,11620,11626,11628,11634,11640,11646,11652,11661,11675,11681,11687,11691,11694,11722,11734,11737,11753,11756],[12,8785,8786,8787,571,8790,571,8793,8796],{},"A primeira vez que o seu site cair às três da manhã, você vai descobrir uma coisa desconfortável: não tem como saber o que aconteceu. Não tem gráfico de CPU, não tem log do contêiner que morreu, não tem alerta que avisou antes. Você vai abrir um terminal, conectar nos servidores um por um, rodar ",[231,8788,8789],{},"top",[231,8791,8792],{},"df",[231,8794,8795],{},"journalctl",", e tentar reconstituir uma cena de crime que já esfriou.",[12,8798,8799],{},"Esse post é o atalho pra você não passar por isso. Em quatro horas, com R$80 a R$120 por mês de hardware, dá pra montar a stack de observabilidade open-source que substitui Datadog, New Relic e CloudWatch em 95% dos casos pra startup. As ferramentas são as mesmas que rodam dentro de empresas com dezenas de milhares de servidores — e cabem confortavelmente num VPS pequeno pro time que está começando.",[19,8801,22],{"id":21},[12,8803,8804,8805,8808],{},"A stack de monitoring open-source padrão em 2026 — ",[27,8806,8807],{},"Prometheus + Grafana + Loki + Alertmanager"," — cabe em um único VPS de 4 GB de RAM e cobre métricas, logs centralizados, dashboards e alertas. Esse tutorial mostra setup passo a passo pra um cluster de 4 a 5 servidores em aproximadamente quatro horas, usando docker-compose ou job specs do orquestrador.",[12,8810,8811,8812,8815,8816,8819],{},"Pra startup brasileira, isso significa ",[27,8813,8814],{},"R$80 a R$120 por mês de hardware"," contra ",[27,8817,8818],{},"R$1.000 a R$2.000 por mês"," de SaaS de observabilidade equivalente. O custo de tempo é honesto: quatro horas de setup inicial mais duas a quatro horas por mês de manutenção contínua.",[12,8821,8822],{},"Resultado entregável ao final do tutorial: dashboards de CPU, RAM, disco, rede e métricas HTTP; logs pesquisáveis com retenção de 30 dias; alertas roteados pra Slack, Discord ou e-mail. Pré-requisitos: 1 VPS Linux com 4 GB de RAM e 50 GB de SSD, Docker instalado, e um domínio com DNS controlado por você.",[12,8824,8825],{},"A escolha entre rodar essa stack num VPS dedicado fora do cluster de produção ou como job dentro do próprio orquestrador é uma decisão arquitetural — cobrimos as duas opções no passo 8 e em \"Como rodar isso dentro do HeroCtl\".",[19,8827,8829],{"id":8828},"o-que-cada-componente-faz-em-uma-frase","O que cada componente faz, em uma frase",[12,8831,8832],{},"Antes de instalar qualquer coisa, vale entender o papel de cada peça. A stack tem seis componentes; a confusão geralmente vem de pensar que algum deles é \"o sistema de monitoring\". Não é. Cada um faz uma coisa.",[2735,8834,8835,8841,8847,8853,8859,8865],{},[70,8836,8837,8840],{},[27,8838,8839],{},"Prometheus"," é um banco de dados de séries temporais (TSDB) que coleta métricas via HTTP scrape — ele puxa os números, ninguém empurra. Retém 15 dias por padrão.",[70,8842,8843,8846],{},[27,8844,8845],{},"Grafana"," é a camada de visualização. Conecta em Prometheus, em Loki, em Postgres, em quase qualquer fonte estruturada, e desenha gráficos.",[70,8848,8849,8852],{},[27,8850,8851],{},"Loki"," é a peça de logs. Sintaxe similar ao Prometheus, indexa apenas labels (não o conteúdo dos logs), e por causa disso fica cerca de dez vezes mais barato que ELK pra rodar.",[70,8854,8855,8858],{},[27,8856,8857],{},"Promtail"," (ou o Grafana Agent, que está substituindo o Promtail em 2026) é o coletor que lê os arquivos de log de cada servidor e envia pro Loki.",[70,8860,8861,8864],{},[27,8862,8863],{},"node_exporter"," roda em cada nó monitorado e expõe um endpoint HTTP com CPU, RAM, disco e rede em formato Prometheus.",[70,8866,8867,8870],{},[27,8868,8869],{},"Alertmanager"," recebe regras de alerta do Prometheus e cuida do roteamento — Slack, e-mail, PagerDuty, webhook arbitrário.",[12,8872,8873,8874,571,8877,571,8880,571,8883,101],{},"Quem desenha a primeira stack costuma confundir Prometheus com \"monitoring\" e Grafana com \"dashboards bonitos\". A separação real é: ",[27,8875,8876],{},"Prometheus guarda números",[27,8878,8879],{},"Loki guarda texto",[27,8881,8882],{},"Grafana mostra ambos",[27,8884,8885],{},"Alertmanager grita quando algum número fica errado",[19,8887,8889],{"id":8888},"qual-e-a-arquitetura-recomendada","Qual é a arquitetura recomendada?",[12,8891,8892],{},"Pra um cluster de 3 a 5 servidores rodando aplicações de produção, a topologia que tem dado certo na prática é separar o servidor de observabilidade do resto. Um nó dedicado, fora do cluster que ele monitora, com dois objetivos: não morrer junto quando o cluster morrer, e não competir por CPU\u002FRAM com a aplicação real.",[2735,8894,8895,8901,8907,8917],{},[70,8896,8897,8900],{},[27,8898,8899],{},"1 servidor \"observability\" dedicado",", 4 GB de RAM, 50 GB de SSD. Roda Prometheus, Grafana, Loki, Alertmanager.",[70,8902,8903,8906],{},[27,8904,8905],{},"Cada servidor monitorado"," roda apenas dois processos leves: node_exporter (métricas de sistema) e Promtail (envio de logs).",[70,8908,8909,8912,8913,8916],{},[27,8910,8911],{},"As suas aplicações"," expõem um endpoint ",[231,8914,8915],{},"\u002Fmetrics"," em formato Prometheus. Se você usa um framework popular, existe um cliente pronto. Se não, é uma biblioteca de poucas dezenas de linhas.",[70,8918,8919,8921,8922,8925],{},[27,8920,8845],{}," fica acessível via subdomínio (",[231,8923,8924],{},"monitor.seudominio.com",") com TLS automático e autenticação básica em frente.",[12,8927,8928],{},"Essa separação tem um custo: você paga por mais um VPS. Em troca, quando o cluster principal cair, você ainda consegue olhar os gráficos pra entender o que aconteceu. Pra startup, esse trade-off compensa quase sempre — o pior cenário em monitoring é descobrir que a única coisa que parou junto com o site foi o sistema que ia te avisar que o site parou.",[19,8930,8932],{"id":8931},"passo-1-como-provisionar-o-vps-de-observabilidade","Passo 1 — Como provisionar o VPS de observabilidade?",[12,8934,8935,8936,101],{},"Tempo estimado: ",[27,8937,8938],{},"10 minutos",[12,8940,8941],{},"Qualquer provedor barato serve. Os dois com melhor custo-benefício pro caso brasileiro hoje são a Hetzner (CPX21 a 7,99 EUR por mês com 3 vCPUs e 4 GB de RAM, datacenter na Alemanha) e a DigitalOcean (Basic Droplet de US$24 por mês com a mesma configuração, datacenters mais próximos do Brasil). Pra workload de monitoring, latência de scrape em datacenter europeu não causa problema — Prometheus puxa a cada 15 segundos por padrão, então 200ms de RTT entre Hetzner e seus servidores não atrapalha.",[12,8943,8944],{},"Provisionando:",[67,8946,8947,8950,8953,8959,8966],{},[70,8948,8949],{},"Crie o VPS com Ubuntu 24.04 LTS ou Debian 12.",[70,8951,8952],{},"Adicione a sua chave SSH pública na criação. Desabilite login por senha.",[70,8954,8955,8956,101],{},"Instale Docker e o plugin de compose: ",[231,8957,8958],{},"curl -fsSL https:\u002F\u002Fget.docker.com | sh && apt install docker-compose-plugin",[70,8960,8961,8962,8965],{},"Configure o firewall: porta 22 (SSH) aberta, porta 443 (HTTPS) aberta, todas as outras fechadas. As portas internas (3000, 9090, 3100, 9093) só ficam acessíveis pelo ",[231,8963,8964],{},"localhost"," do próprio VPS — o reverse proxy expõe Grafana via 443.",[70,8967,8968,8969,8971],{},"Aponte o DNS: crie um registro A ",[231,8970,8924],{}," pra o IP do VPS.",[12,8973,341,8974,8977,8978,8981,8982,8985],{},[231,8975,8976],{},"docker --version"," retorna 26.x ou superior; ",[231,8979,8980],{},"dig monitor.seudominio.com"," retorna o IP correto; ",[231,8983,8984],{},"ssh root@monitor.seudominio.com"," conecta sem pedir senha.",[19,8987,8989],{"id":8988},"passo-2-como-subir-a-stack-via-docker-compose","Passo 2 — Como subir a stack via docker-compose?",[12,8991,8935,8992,101],{},[27,8993,8994],{},"45 minutos",[12,8996,8997,8998,9001],{},"Crie o diretório de trabalho em ",[231,8999,9000],{},"\u002Fopt\u002Fobservability\u002F"," com a seguinte estrutura:",[224,9003,9006],{"className":9004,"code":9005,"language":2530},[2528],"\u002Fopt\u002Fobservability\u002F\n├── docker-compose.yml\n├── prometheus\u002F\n│   ├── prometheus.yml\n│   └── alerts.yml\n├── alertmanager\u002F\n│   └── alertmanager.yml\n├── loki\u002F\n│   └── loki-config.yml\n└── grafana\u002F\n    └── provisioning\u002F\n        └── datasources\u002F\n            └── datasources.yml\n",[231,9007,9005],{"__ignoreMap":229},[12,9009,9010,9011,9014],{},"O ",[231,9012,9013],{},"docker-compose.yml"," abreviado mas funcional:",[224,9016,9020],{"className":9017,"code":9018,"language":9019,"meta":229,"style":229},"language-yaml shiki shiki-themes github-dark-default","services:\n  prometheus:\n    image: prom\u002Fprometheus:v2.55.0\n    volumes:\n      - .\u002Fprometheus:\u002Fetc\u002Fprometheus\n      - prometheus-data:\u002Fprometheus\n    command:\n      - '--config.file=\u002Fetc\u002Fprometheus\u002Fprometheus.yml'\n      - '--storage.tsdb.retention.time=30d'\n      - '--web.enable-lifecycle'  # permite reload via HTTP POST\n    ports:\n      - '127.0.0.1:9090:9090'\n    restart: unless-stopped\n\n  grafana:\n    image: grafana\u002Fgrafana:11.3.0\n    volumes:\n      - grafana-data:\u002Fvar\u002Flib\u002Fgrafana\n      - .\u002Fgrafana\u002Fprovisioning:\u002Fetc\u002Fgrafana\u002Fprovisioning\n    environment:\n      - GF_SECURITY_ADMIN_PASSWORD=${GRAFANA_PASSWORD}\n      - GF_USERS_ALLOW_SIGN_UP=false\n    ports:\n      - '127.0.0.1:3000:3000'\n    restart: unless-stopped\n\n  loki:\n    image: grafana\u002Floki:3.2.0\n    volumes:\n      - .\u002Floki\u002Floki-config.yml:\u002Fetc\u002Floki\u002Fconfig.yml\n      - loki-data:\u002Floki\n    command: -config.file=\u002Fetc\u002Floki\u002Fconfig.yml\n    ports:\n      - '127.0.0.1:3100:3100'\n    restart: unless-stopped\n\n  alertmanager:\n    image: prom\u002Falertmanager:v0.27.0\n    volumes:\n      - .\u002Falertmanager:\u002Fetc\u002Falertmanager\n    ports:\n      - '127.0.0.1:9093:9093'\n    restart: unless-stopped\n\nvolumes:\n  prometheus-data:\n  grafana-data:\n  loki-data:\n","yaml",[231,9021,9022,9031,9038,9048,9055,9063,9070,9077,9084,9091,9101,9108,9115,9125,9129,9136,9145,9151,9158,9165,9172,9179,9186,9192,9199,9207,9211,9218,9227,9233,9240,9247,9256,9262,9269,9277,9281,9288,9297,9303,9310,9316,9323,9331,9335,9342,9349,9356],{"__ignoreMap":229},[234,9023,9024,9028],{"class":236,"line":237},[234,9025,9027],{"class":9026},"sPWt5","services",[234,9029,9030],{"class":387},":\n",[234,9032,9033,9036],{"class":236,"line":244},[234,9034,9035],{"class":9026},"  prometheus",[234,9037,9030],{"class":387},[234,9039,9040,9043,9045],{"class":236,"line":271},[234,9041,9042],{"class":9026},"    image",[234,9044,6564],{"class":387},[234,9046,9047],{"class":255},"prom\u002Fprometheus:v2.55.0\n",[234,9049,9050,9053],{"class":236,"line":415},[234,9051,9052],{"class":9026},"    volumes",[234,9054,9030],{"class":387},[234,9056,9057,9060],{"class":236,"line":434},[234,9058,9059],{"class":387},"      - ",[234,9061,9062],{"class":255},".\u002Fprometheus:\u002Fetc\u002Fprometheus\n",[234,9064,9065,9067],{"class":236,"line":459},[234,9066,9059],{"class":387},[234,9068,9069],{"class":255},"prometheus-data:\u002Fprometheus\n",[234,9071,9072,9075],{"class":236,"line":464},[234,9073,9074],{"class":9026},"    command",[234,9076,9030],{"class":387},[234,9078,9079,9081],{"class":236,"line":479},[234,9080,9059],{"class":387},[234,9082,9083],{"class":255},"'--config.file=\u002Fetc\u002Fprometheus\u002Fprometheus.yml'\n",[234,9085,9086,9088],{"class":236,"line":484},[234,9087,9059],{"class":387},[234,9089,9090],{"class":255},"'--storage.tsdb.retention.time=30d'\n",[234,9092,9093,9095,9098],{"class":236,"line":490},[234,9094,9059],{"class":387},[234,9096,9097],{"class":255},"'--web.enable-lifecycle'",[234,9099,9100],{"class":240},"  # permite reload via HTTP POST\n",[234,9102,9103,9106],{"class":236,"line":508},[234,9104,9105],{"class":9026},"    ports",[234,9107,9030],{"class":387},[234,9109,9110,9112],{"class":236,"line":529},[234,9111,9059],{"class":387},[234,9113,9114],{"class":255},"'127.0.0.1:9090:9090'\n",[234,9116,9117,9120,9122],{"class":236,"line":535},[234,9118,9119],{"class":9026},"    restart",[234,9121,6564],{"class":387},[234,9123,9124],{"class":255},"unless-stopped\n",[234,9126,9127],{"class":236,"line":546},[234,9128,412],{"emptyLinePlaceholder":411},[234,9130,9131,9134],{"class":236,"line":552},[234,9132,9133],{"class":9026},"  grafana",[234,9135,9030],{"class":387},[234,9137,9138,9140,9142],{"class":236,"line":557},[234,9139,9042],{"class":9026},[234,9141,6564],{"class":387},[234,9143,9144],{"class":255},"grafana\u002Fgrafana:11.3.0\n",[234,9146,9147,9149],{"class":236,"line":594},[234,9148,9052],{"class":9026},[234,9150,9030],{"class":387},[234,9152,9153,9155],{"class":236,"line":635},[234,9154,9059],{"class":387},[234,9156,9157],{"class":255},"grafana-data:\u002Fvar\u002Flib\u002Fgrafana\n",[234,9159,9160,9162],{"class":236,"line":643},[234,9161,9059],{"class":387},[234,9163,9164],{"class":255},".\u002Fgrafana\u002Fprovisioning:\u002Fetc\u002Fgrafana\u002Fprovisioning\n",[234,9166,9167,9170],{"class":236,"line":659},[234,9168,9169],{"class":9026},"    environment",[234,9171,9030],{"class":387},[234,9173,9174,9176],{"class":236,"line":683},[234,9175,9059],{"class":387},[234,9177,9178],{"class":255},"GF_SECURITY_ADMIN_PASSWORD=${GRAFANA_PASSWORD}\n",[234,9180,9181,9183],{"class":236,"line":695},[234,9182,9059],{"class":387},[234,9184,9185],{"class":255},"GF_USERS_ALLOW_SIGN_UP=false\n",[234,9187,9188,9190],{"class":236,"line":717},[234,9189,9105],{"class":9026},[234,9191,9030],{"class":387},[234,9193,9194,9196],{"class":236,"line":723},[234,9195,9059],{"class":387},[234,9197,9198],{"class":255},"'127.0.0.1:3000:3000'\n",[234,9200,9201,9203,9205],{"class":236,"line":729},[234,9202,9119],{"class":9026},[234,9204,6564],{"class":387},[234,9206,9124],{"class":255},[234,9208,9209],{"class":236,"line":734},[234,9210,412],{"emptyLinePlaceholder":411},[234,9212,9213,9216],{"class":236,"line":771},[234,9214,9215],{"class":9026},"  loki",[234,9217,9030],{"class":387},[234,9219,9220,9222,9224],{"class":236,"line":776},[234,9221,9042],{"class":9026},[234,9223,6564],{"class":387},[234,9225,9226],{"class":255},"grafana\u002Floki:3.2.0\n",[234,9228,9229,9231],{"class":236,"line":815},[234,9230,9052],{"class":9026},[234,9232,9030],{"class":387},[234,9234,9235,9237],{"class":236,"line":820},[234,9236,9059],{"class":387},[234,9238,9239],{"class":255},".\u002Floki\u002Floki-config.yml:\u002Fetc\u002Floki\u002Fconfig.yml\n",[234,9241,9242,9244],{"class":236,"line":826},[234,9243,9059],{"class":387},[234,9245,9246],{"class":255},"loki-data:\u002Floki\n",[234,9248,9249,9251,9253],{"class":236,"line":846},[234,9250,9074],{"class":9026},[234,9252,6564],{"class":387},[234,9254,9255],{"class":255},"-config.file=\u002Fetc\u002Floki\u002Fconfig.yml\n",[234,9257,9258,9260],{"class":236,"line":859},[234,9259,9105],{"class":9026},[234,9261,9030],{"class":387},[234,9263,9264,9266],{"class":236,"line":872},[234,9265,9059],{"class":387},[234,9267,9268],{"class":255},"'127.0.0.1:3100:3100'\n",[234,9270,9271,9273,9275],{"class":236,"line":898},[234,9272,9119],{"class":9026},[234,9274,6564],{"class":387},[234,9276,9124],{"class":255},[234,9278,9279],{"class":236,"line":913},[234,9280,412],{"emptyLinePlaceholder":411},[234,9282,9283,9286],{"class":236,"line":1886},[234,9284,9285],{"class":9026},"  alertmanager",[234,9287,9030],{"class":387},[234,9289,9290,9292,9294],{"class":236,"line":1901},[234,9291,9042],{"class":9026},[234,9293,6564],{"class":387},[234,9295,9296],{"class":255},"prom\u002Falertmanager:v0.27.0\n",[234,9298,9299,9301],{"class":236,"line":1920},[234,9300,9052],{"class":9026},[234,9302,9030],{"class":387},[234,9304,9305,9307],{"class":236,"line":1944},[234,9306,9059],{"class":387},[234,9308,9309],{"class":255},".\u002Falertmanager:\u002Fetc\u002Falertmanager\n",[234,9311,9312,9314],{"class":236,"line":1962},[234,9313,9105],{"class":9026},[234,9315,9030],{"class":387},[234,9317,9318,9320],{"class":236,"line":1978},[234,9319,9059],{"class":387},[234,9321,9322],{"class":255},"'127.0.0.1:9093:9093'\n",[234,9324,9325,9327,9329],{"class":236,"line":1984},[234,9326,9119],{"class":9026},[234,9328,6564],{"class":387},[234,9330,9124],{"class":255},[234,9332,9333],{"class":236,"line":1992},[234,9334,412],{"emptyLinePlaceholder":411},[234,9336,9337,9340],{"class":236,"line":2004},[234,9338,9339],{"class":9026},"volumes",[234,9341,9030],{"class":387},[234,9343,9344,9347],{"class":236,"line":2014},[234,9345,9346],{"class":9026},"  prometheus-data",[234,9348,9030],{"class":387},[234,9350,9351,9354],{"class":236,"line":2020},[234,9352,9353],{"class":9026},"  grafana-data",[234,9355,9030],{"class":387},[234,9357,9358,9361],{"class":236,"line":2029},[234,9359,9360],{"class":9026},"  loki-data",[234,9362,9030],{"class":387},[12,9364,9365,9366,9369,9370,9373,9374,9377,9378,9381],{},"Três pontos importantes nesse arquivo. Primeiro, todas as portas estão amarradas a ",[231,9367,9368],{},"127.0.0.1"," — nenhum dos serviços é acessível diretamente da internet. Segundo, os volumes são nomeados (não bind mounts), então sobrevivem a ",[231,9371,9372],{},"docker-compose down",". Terceiro, a senha do Grafana vem de variável de ambiente: crie um ",[231,9375,9376],{},".env"," ao lado do compose com ",[231,9379,9380],{},"GRAFANA_PASSWORD=algo_longo_aleatorio"," e nunca comite isso.",[12,9383,9384],{},"Suba a stack:",[224,9386,9388],{"className":226,"code":9387,"language":228,"meta":229,"style":229},"cd \u002Fopt\u002Fobservability\ndocker compose up -d\ndocker compose ps  # todos devem estar \"Up\" \u002F healthy\n",[231,9389,9390,9398,9411],{"__ignoreMap":229},[234,9391,9392,9395],{"class":236,"line":237},[234,9393,9394],{"class":251},"cd",[234,9396,9397],{"class":255}," \u002Fopt\u002Fobservability\n",[234,9399,9400,9402,9405,9408],{"class":236,"line":244},[234,9401,1118],{"class":247},[234,9403,9404],{"class":255}," compose",[234,9406,9407],{"class":255}," up",[234,9409,9410],{"class":251}," -d\n",[234,9412,9413,9415,9417,9420],{"class":236,"line":271},[234,9414,1118],{"class":247},[234,9416,9404],{"class":255},[234,9418,9419],{"class":255}," ps",[234,9421,9422],{"class":240},"  # todos devem estar \"Up\" \u002F healthy\n",[12,9424,9425,9426,9429,9430,1895,9433,9429,9436,1895,9439,9442,9443,101],{},"Validação rápida: ",[231,9427,9428],{},"curl localhost:9090\u002F-\u002Fready"," retorna ",[231,9431,9432],{},"Prometheus Server is Ready",[231,9434,9435],{},"curl localhost:3100\u002Fready",[231,9437,9438],{},"ready",[231,9440,9441],{},"curl localhost:3000\u002Fapi\u002Fhealth"," retorna JSON com ",[231,9444,9445],{},"\"database\": \"ok\"",[19,9447,9449],{"id":9448},"passo-3-como-configurar-os-scrapes-do-prometheus","Passo 3 — Como configurar os scrapes do Prometheus?",[12,9451,8935,9452,101],{},[27,9453,9454],{},"30 minutos",[12,9456,9010,9457,9460],{},[231,9458,9459],{},"prometheus\u002Fprometheus.yml"," é onde você diz pro Prometheus quais endpoints raspar. Pra um cluster de 4 servidores, fica assim:",[224,9462,9464],{"className":9017,"code":9463,"language":9019,"meta":229,"style":229},"global:\n  scrape_interval: 15s\n  evaluation_interval: 15s\n\nalerting:\n  alertmanagers:\n    - static_configs:\n        - targets: ['alertmanager:9093']\n\nrule_files:\n  - 'alerts.yml'\n\nscrape_configs:\n  - job_name: 'prometheus'\n    static_configs:\n      - targets: ['localhost:9090']\n\n  - job_name: 'node'\n    static_configs:\n      - targets:\n          - 'server-1.seudominio.internal:9100'\n          - 'server-2.seudominio.internal:9100'\n          - 'server-3.seudominio.internal:9100'\n          - 'worker-1.seudominio.internal:9100'\n        labels:\n          environment: 'production'\n\n  - job_name: 'apps'\n    static_configs:\n      - targets:\n          - 'api.seudominio.internal:8080'\n          - 'worker.seudominio.internal:8080'\n        labels:\n          environment: 'production'\n    metrics_path: '\u002Fmetrics'\n",[231,9465,9466,9473,9483,9492,9496,9503,9510,9520,9537,9541,9548,9556,9560,9567,9579,9586,9599,9603,9614,9620,9628,9636,9643,9650,9657,9664,9674,9678,9689,9695,9703,9710,9717,9723,9731],{"__ignoreMap":229},[234,9467,9468,9471],{"class":236,"line":237},[234,9469,9470],{"class":9026},"global",[234,9472,9030],{"class":387},[234,9474,9475,9478,9480],{"class":236,"line":244},[234,9476,9477],{"class":9026},"  scrape_interval",[234,9479,6564],{"class":387},[234,9481,9482],{"class":255},"15s\n",[234,9484,9485,9488,9490],{"class":236,"line":271},[234,9486,9487],{"class":9026},"  evaluation_interval",[234,9489,6564],{"class":387},[234,9491,9482],{"class":255},[234,9493,9494],{"class":236,"line":415},[234,9495,412],{"emptyLinePlaceholder":411},[234,9497,9498,9501],{"class":236,"line":434},[234,9499,9500],{"class":9026},"alerting",[234,9502,9030],{"class":387},[234,9504,9505,9508],{"class":236,"line":459},[234,9506,9507],{"class":9026},"  alertmanagers",[234,9509,9030],{"class":387},[234,9511,9512,9515,9518],{"class":236,"line":464},[234,9513,9514],{"class":387},"    - ",[234,9516,9517],{"class":9026},"static_configs",[234,9519,9030],{"class":387},[234,9521,9522,9525,9528,9531,9534],{"class":236,"line":479},[234,9523,9524],{"class":387},"        - ",[234,9526,9527],{"class":9026},"targets",[234,9529,9530],{"class":387},": [",[234,9532,9533],{"class":255},"'alertmanager:9093'",[234,9535,9536],{"class":387},"]\n",[234,9538,9539],{"class":236,"line":484},[234,9540,412],{"emptyLinePlaceholder":411},[234,9542,9543,9546],{"class":236,"line":490},[234,9544,9545],{"class":9026},"rule_files",[234,9547,9030],{"class":387},[234,9549,9550,9553],{"class":236,"line":508},[234,9551,9552],{"class":387},"  - ",[234,9554,9555],{"class":255},"'alerts.yml'\n",[234,9557,9558],{"class":236,"line":529},[234,9559,412],{"emptyLinePlaceholder":411},[234,9561,9562,9565],{"class":236,"line":535},[234,9563,9564],{"class":9026},"scrape_configs",[234,9566,9030],{"class":387},[234,9568,9569,9571,9574,9576],{"class":236,"line":546},[234,9570,9552],{"class":387},[234,9572,9573],{"class":9026},"job_name",[234,9575,6564],{"class":387},[234,9577,9578],{"class":255},"'prometheus'\n",[234,9580,9581,9584],{"class":236,"line":552},[234,9582,9583],{"class":9026},"    static_configs",[234,9585,9030],{"class":387},[234,9587,9588,9590,9592,9594,9597],{"class":236,"line":557},[234,9589,9059],{"class":387},[234,9591,9527],{"class":9026},[234,9593,9530],{"class":387},[234,9595,9596],{"class":255},"'localhost:9090'",[234,9598,9536],{"class":387},[234,9600,9601],{"class":236,"line":594},[234,9602,412],{"emptyLinePlaceholder":411},[234,9604,9605,9607,9609,9611],{"class":236,"line":635},[234,9606,9552],{"class":387},[234,9608,9573],{"class":9026},[234,9610,6564],{"class":387},[234,9612,9613],{"class":255},"'node'\n",[234,9615,9616,9618],{"class":236,"line":643},[234,9617,9583],{"class":9026},[234,9619,9030],{"class":387},[234,9621,9622,9624,9626],{"class":236,"line":659},[234,9623,9059],{"class":387},[234,9625,9527],{"class":9026},[234,9627,9030],{"class":387},[234,9629,9630,9633],{"class":236,"line":683},[234,9631,9632],{"class":387},"          - ",[234,9634,9635],{"class":255},"'server-1.seudominio.internal:9100'\n",[234,9637,9638,9640],{"class":236,"line":695},[234,9639,9632],{"class":387},[234,9641,9642],{"class":255},"'server-2.seudominio.internal:9100'\n",[234,9644,9645,9647],{"class":236,"line":717},[234,9646,9632],{"class":387},[234,9648,9649],{"class":255},"'server-3.seudominio.internal:9100'\n",[234,9651,9652,9654],{"class":236,"line":723},[234,9653,9632],{"class":387},[234,9655,9656],{"class":255},"'worker-1.seudominio.internal:9100'\n",[234,9658,9659,9662],{"class":236,"line":729},[234,9660,9661],{"class":9026},"        labels",[234,9663,9030],{"class":387},[234,9665,9666,9669,9671],{"class":236,"line":734},[234,9667,9668],{"class":9026},"          environment",[234,9670,6564],{"class":387},[234,9672,9673],{"class":255},"'production'\n",[234,9675,9676],{"class":236,"line":771},[234,9677,412],{"emptyLinePlaceholder":411},[234,9679,9680,9682,9684,9686],{"class":236,"line":776},[234,9681,9552],{"class":387},[234,9683,9573],{"class":9026},[234,9685,6564],{"class":387},[234,9687,9688],{"class":255},"'apps'\n",[234,9690,9691,9693],{"class":236,"line":815},[234,9692,9583],{"class":9026},[234,9694,9030],{"class":387},[234,9696,9697,9699,9701],{"class":236,"line":820},[234,9698,9059],{"class":387},[234,9700,9527],{"class":9026},[234,9702,9030],{"class":387},[234,9704,9705,9707],{"class":236,"line":826},[234,9706,9632],{"class":387},[234,9708,9709],{"class":255},"'api.seudominio.internal:8080'\n",[234,9711,9712,9714],{"class":236,"line":846},[234,9713,9632],{"class":387},[234,9715,9716],{"class":255},"'worker.seudominio.internal:8080'\n",[234,9718,9719,9721],{"class":236,"line":859},[234,9720,9661],{"class":9026},[234,9722,9030],{"class":387},[234,9724,9725,9727,9729],{"class":236,"line":872},[234,9726,9668],{"class":9026},[234,9728,6564],{"class":387},[234,9730,9673],{"class":255},[234,9732,9733,9736,9738],{"class":236,"line":898},[234,9734,9735],{"class":9026},"    metrics_path",[234,9737,6564],{"class":387},[234,9739,9740],{"class":255},"'\u002Fmetrics'\n",[12,9742,9743,9744,9746,9747,9750],{},"Pra clusters maiores ou que mudam de composição com frequência, troque o ",[231,9745,9517],{}," por ",[231,9748,9749],{},"file_sd_configs"," apontando pra um JSON que você gera automaticamente. Pra 4 servidores estáticos, o arquivo acima resolve.",[12,9752,9753,9754,9757,9758,9761,9762,9765,9766,9769],{},"Reload: ",[231,9755,9756],{},"curl -X POST localhost:9090\u002F-\u002Freload",". Confira em ",[231,9759,9760],{},"localhost:9090\u002Ftargets"," se todos os jobs estão ",[231,9763,9764],{},"UP",". Os que estiverem ",[231,9767,9768],{},"DOWN"," ainda não foram instrumentados — esse é o passo 4.",[19,9771,9773],{"id":9772},"passo-4-como-instalar-o-node_exporter-em-cada-servidor","Passo 4 — Como instalar o node_exporter em cada servidor?",[12,9775,8935,9776,9779],{},[27,9777,9778],{},"15 minutos"," para 4 servidores.",[12,9781,9782],{},"Em cada servidor monitorado, rode o node_exporter. Há duas formas: binário direto via systemd, ou contêiner Docker. Em 2026 o consenso é container — facilita atualização e isolamento. Em cada nó:",[224,9784,9786],{"className":226,"code":9785,"language":228,"meta":229,"style":229},"docker run -d \\\n  --name node-exporter \\\n  --restart unless-stopped \\\n  --net=\"host\" \\\n  --pid=\"host\" \\\n  -v \"\u002F:\u002Fhost:ro,rslave\" \\\n  prom\u002Fnode-exporter:v1.8.2 \\\n  --path.rootfs=\u002Fhost\n",[231,9787,9788,9801,9811,9821,9831,9840,9850,9857],{"__ignoreMap":229},[234,9789,9790,9792,9795,9798],{"class":236,"line":237},[234,9791,1118],{"class":247},[234,9793,9794],{"class":255}," run",[234,9796,9797],{"class":251}," -d",[234,9799,9800],{"class":383}," \\\n",[234,9802,9803,9806,9809],{"class":236,"line":244},[234,9804,9805],{"class":251},"  --name",[234,9807,9808],{"class":255}," node-exporter",[234,9810,9800],{"class":383},[234,9812,9813,9816,9819],{"class":236,"line":271},[234,9814,9815],{"class":251},"  --restart",[234,9817,9818],{"class":255}," unless-stopped",[234,9820,9800],{"class":383},[234,9822,9823,9826,9829],{"class":236,"line":415},[234,9824,9825],{"class":251},"  --net=",[234,9827,9828],{"class":255},"\"host\"",[234,9830,9800],{"class":383},[234,9832,9833,9836,9838],{"class":236,"line":434},[234,9834,9835],{"class":251},"  --pid=",[234,9837,9828],{"class":255},[234,9839,9800],{"class":383},[234,9841,9842,9845,9848],{"class":236,"line":459},[234,9843,9844],{"class":251},"  -v",[234,9846,9847],{"class":255}," \"\u002F:\u002Fhost:ro,rslave\"",[234,9849,9800],{"class":383},[234,9851,9852,9855],{"class":236,"line":464},[234,9853,9854],{"class":255},"  prom\u002Fnode-exporter:v1.8.2",[234,9856,9800],{"class":383},[234,9858,9859],{"class":236,"line":479},[234,9860,9861],{"class":251},"  --path.rootfs=\u002Fhost\n",[12,9863,9010,9864,9867,9868,9871,9872,571,9875,2403,9878,9881],{},[231,9865,9866],{},"--net=host"," é necessário pra ele enxergar as interfaces de rede reais. O bind mount em ",[231,9869,9870],{},"\u002Fhost"," permite ler ",[231,9873,9874],{},"\u002Fproc",[231,9876,9877],{},"\u002Fsys",[231,9879,9880],{},"\u002Fetc\u002Fpasswd"," do host (read-only) sem rodar o contêiner com privilégios de raiz.",[12,9883,9884,9885,1272],{},"Firewall: abra a porta 9100 apenas pra o IP do servidor de observabilidade. No Ubuntu com ",[231,9886,9887],{},"ufw",[224,9889,9891],{"className":226,"code":9890,"language":228,"meta":229,"style":229},"ufw allow from \u003CIP_DO_OBSERVABILITY> to any port 9100\n",[231,9892,9893],{"__ignoreMap":229},[234,9894,9895,9897,9900,9903,9906,9909,9912,9914,9917,9920,9923],{"class":236,"line":237},[234,9896,9887],{"class":247},[234,9898,9899],{"class":255}," allow",[234,9901,9902],{"class":255}," from",[234,9904,9905],{"class":383}," \u003C",[234,9907,9908],{"class":255},"IP_DO_OBSERVABILIT",[234,9910,9911],{"class":387},"Y",[234,9913,1935],{"class":383},[234,9915,9916],{"class":255}," to",[234,9918,9919],{"class":255}," any",[234,9921,9922],{"class":255}," port",[234,9924,9925],{"class":251}," 9100\n",[12,9927,9928,9929,9932,9933,101],{},"Validação: do servidor de observability, ",[231,9930,9931],{},"curl http:\u002F\u002Fserver-1.seudominio.internal:9100\u002Fmetrics"," deve retornar centenas de linhas começando com ",[231,9934,9935],{},"# HELP node_cpu_seconds_total...",[19,9937,9939],{"id":9938},"passo-5-como-configurar-loki-promtail","Passo 5 — Como configurar Loki + Promtail?",[12,9941,8935,9942,101],{},[27,9943,9454],{},[12,9945,9946,9947,1272],{},"O Loki já está rodando no compose do passo 2. Falta o ",[231,9948,9949],{},"loki-config.yml",[224,9951,9953],{"className":9017,"code":9952,"language":9019,"meta":229,"style":229},"auth_enabled: false\n\nserver:\n  http_listen_port: 3100\n\ncommon:\n  path_prefix: \u002Floki\n  storage:\n    filesystem:\n      chunks_directory: \u002Floki\u002Fchunks\n      rules_directory: \u002Floki\u002Frules\n  replication_factor: 1\n  ring:\n    kvstore:\n      store: inmemory\n\nschema_config:\n  configs:\n    - from: 2024-01-01\n      store: tsdb\n      object_store: filesystem\n      schema: v13\n      index:\n        prefix: index_\n        period: 24h\n\nlimits_config:\n  retention_period: 720h  # 30 dias\n  reject_old_samples: true\n  reject_old_samples_max_age: 168h\n",[231,9954,9955,9965,9969,9976,9986,9990,9997,10007,10014,10021,10031,10041,10051,10058,10065,10075,10079,10086,10093,10104,10113,10123,10133,10140,10150,10160,10164,10171,10184,10194],{"__ignoreMap":229},[234,9956,9957,9960,9962],{"class":236,"line":237},[234,9958,9959],{"class":9026},"auth_enabled",[234,9961,6564],{"class":387},[234,9963,9964],{"class":251},"false\n",[234,9966,9967],{"class":236,"line":244},[234,9968,412],{"emptyLinePlaceholder":411},[234,9970,9971,9974],{"class":236,"line":271},[234,9972,9973],{"class":9026},"server",[234,9975,9030],{"class":387},[234,9977,9978,9981,9983],{"class":236,"line":415},[234,9979,9980],{"class":9026},"  http_listen_port",[234,9982,6564],{"class":387},[234,9984,9985],{"class":251},"3100\n",[234,9987,9988],{"class":236,"line":434},[234,9989,412],{"emptyLinePlaceholder":411},[234,9991,9992,9995],{"class":236,"line":459},[234,9993,9994],{"class":9026},"common",[234,9996,9030],{"class":387},[234,9998,9999,10002,10004],{"class":236,"line":464},[234,10000,10001],{"class":9026},"  path_prefix",[234,10003,6564],{"class":387},[234,10005,10006],{"class":255},"\u002Floki\n",[234,10008,10009,10012],{"class":236,"line":479},[234,10010,10011],{"class":9026},"  storage",[234,10013,9030],{"class":387},[234,10015,10016,10019],{"class":236,"line":484},[234,10017,10018],{"class":9026},"    filesystem",[234,10020,9030],{"class":387},[234,10022,10023,10026,10028],{"class":236,"line":490},[234,10024,10025],{"class":9026},"      chunks_directory",[234,10027,6564],{"class":387},[234,10029,10030],{"class":255},"\u002Floki\u002Fchunks\n",[234,10032,10033,10036,10038],{"class":236,"line":508},[234,10034,10035],{"class":9026},"      rules_directory",[234,10037,6564],{"class":387},[234,10039,10040],{"class":255},"\u002Floki\u002Frules\n",[234,10042,10043,10046,10048],{"class":236,"line":529},[234,10044,10045],{"class":9026},"  replication_factor",[234,10047,6564],{"class":387},[234,10049,10050],{"class":251},"1\n",[234,10052,10053,10056],{"class":236,"line":535},[234,10054,10055],{"class":9026},"  ring",[234,10057,9030],{"class":387},[234,10059,10060,10063],{"class":236,"line":546},[234,10061,10062],{"class":9026},"    kvstore",[234,10064,9030],{"class":387},[234,10066,10067,10070,10072],{"class":236,"line":552},[234,10068,10069],{"class":9026},"      store",[234,10071,6564],{"class":387},[234,10073,10074],{"class":255},"inmemory\n",[234,10076,10077],{"class":236,"line":557},[234,10078,412],{"emptyLinePlaceholder":411},[234,10080,10081,10084],{"class":236,"line":594},[234,10082,10083],{"class":9026},"schema_config",[234,10085,9030],{"class":387},[234,10087,10088,10091],{"class":236,"line":635},[234,10089,10090],{"class":9026},"  configs",[234,10092,9030],{"class":387},[234,10094,10095,10097,10099,10101],{"class":236,"line":643},[234,10096,9514],{"class":387},[234,10098,391],{"class":9026},[234,10100,6564],{"class":387},[234,10102,10103],{"class":251},"2024-01-01\n",[234,10105,10106,10108,10110],{"class":236,"line":659},[234,10107,10069],{"class":9026},[234,10109,6564],{"class":387},[234,10111,10112],{"class":255},"tsdb\n",[234,10114,10115,10118,10120],{"class":236,"line":683},[234,10116,10117],{"class":9026},"      object_store",[234,10119,6564],{"class":387},[234,10121,10122],{"class":255},"filesystem\n",[234,10124,10125,10128,10130],{"class":236,"line":695},[234,10126,10127],{"class":9026},"      schema",[234,10129,6564],{"class":387},[234,10131,10132],{"class":255},"v13\n",[234,10134,10135,10138],{"class":236,"line":717},[234,10136,10137],{"class":9026},"      index",[234,10139,9030],{"class":387},[234,10141,10142,10145,10147],{"class":236,"line":723},[234,10143,10144],{"class":9026},"        prefix",[234,10146,6564],{"class":387},[234,10148,10149],{"class":255},"index_\n",[234,10151,10152,10155,10157],{"class":236,"line":729},[234,10153,10154],{"class":9026},"        period",[234,10156,6564],{"class":387},[234,10158,10159],{"class":255},"24h\n",[234,10161,10162],{"class":236,"line":734},[234,10163,412],{"emptyLinePlaceholder":411},[234,10165,10166,10169],{"class":236,"line":771},[234,10167,10168],{"class":9026},"limits_config",[234,10170,9030],{"class":387},[234,10172,10173,10176,10178,10181],{"class":236,"line":776},[234,10174,10175],{"class":9026},"  retention_period",[234,10177,6564],{"class":387},[234,10179,10180],{"class":255},"720h",[234,10182,10183],{"class":240},"  # 30 dias\n",[234,10185,10186,10189,10191],{"class":236,"line":815},[234,10187,10188],{"class":9026},"  reject_old_samples",[234,10190,6564],{"class":387},[234,10192,10193],{"class":251},"true\n",[234,10195,10196,10199,10201],{"class":236,"line":820},[234,10197,10198],{"class":9026},"  reject_old_samples_max_age",[234,10200,6564],{"class":387},[234,10202,10203],{"class":255},"168h\n",[12,10205,10206],{},"Storage em filesystem é o suficiente pra começar. Quando você passar de 50 GB de logs por dia ou quiser retenção de 90+ dias, migra pra S3 (ou compatível). Não migre antes — complica a operação sem ganho real.",[12,10208,10209],{},"Em cada servidor monitorado, instale o Promtail (ou Grafana Agent) também via container:",[224,10211,10213],{"className":9017,"code":10212,"language":9019,"meta":229,"style":229},"# \u002Fopt\u002Fpromtail\u002Fpromtail-config.yml em cada servidor\nserver:\n  http_listen_port: 9080\n\nclients:\n  - url: http:\u002F\u002Fmonitor.seudominio.com:3100\u002Floki\u002Fapi\u002Fv1\u002Fpush\n\nscrape_configs:\n  - job_name: system\n    static_configs:\n      - targets: [localhost]\n        labels:\n          job: varlogs\n          host: ${HOSTNAME}\n          __path__: \u002Fvar\u002Flog\u002F*.log\n\n  - job_name: docker\n    docker_sd_configs:\n      - host: unix:\u002F\u002F\u002Fvar\u002Frun\u002Fdocker.sock\n    relabel_configs:\n      - source_labels: ['__meta_docker_container_name']\n        target_label: 'container'\n",[231,10214,10215,10220,10226,10235,10239,10246,10258,10262,10268,10279,10285,10297,10303,10313,10323,10333,10337,10348,10355,10366,10373,10387],{"__ignoreMap":229},[234,10216,10217],{"class":236,"line":237},[234,10218,10219],{"class":240},"# \u002Fopt\u002Fpromtail\u002Fpromtail-config.yml em cada servidor\n",[234,10221,10222,10224],{"class":236,"line":244},[234,10223,9973],{"class":9026},[234,10225,9030],{"class":387},[234,10227,10228,10230,10232],{"class":236,"line":271},[234,10229,9980],{"class":9026},[234,10231,6564],{"class":387},[234,10233,10234],{"class":251},"9080\n",[234,10236,10237],{"class":236,"line":415},[234,10238,412],{"emptyLinePlaceholder":411},[234,10240,10241,10244],{"class":236,"line":434},[234,10242,10243],{"class":9026},"clients",[234,10245,9030],{"class":387},[234,10247,10248,10250,10253,10255],{"class":236,"line":459},[234,10249,9552],{"class":387},[234,10251,10252],{"class":9026},"url",[234,10254,6564],{"class":387},[234,10256,10257],{"class":255},"http:\u002F\u002Fmonitor.seudominio.com:3100\u002Floki\u002Fapi\u002Fv1\u002Fpush\n",[234,10259,10260],{"class":236,"line":464},[234,10261,412],{"emptyLinePlaceholder":411},[234,10263,10264,10266],{"class":236,"line":479},[234,10265,9564],{"class":9026},[234,10267,9030],{"class":387},[234,10269,10270,10272,10274,10276],{"class":236,"line":484},[234,10271,9552],{"class":387},[234,10273,9573],{"class":9026},[234,10275,6564],{"class":387},[234,10277,10278],{"class":255},"system\n",[234,10280,10281,10283],{"class":236,"line":490},[234,10282,9583],{"class":9026},[234,10284,9030],{"class":387},[234,10286,10287,10289,10291,10293,10295],{"class":236,"line":508},[234,10288,9059],{"class":387},[234,10290,9527],{"class":9026},[234,10292,9530],{"class":387},[234,10294,8964],{"class":255},[234,10296,9536],{"class":387},[234,10298,10299,10301],{"class":236,"line":529},[234,10300,9661],{"class":9026},[234,10302,9030],{"class":387},[234,10304,10305,10308,10310],{"class":236,"line":535},[234,10306,10307],{"class":9026},"          job",[234,10309,6564],{"class":387},[234,10311,10312],{"class":255},"varlogs\n",[234,10314,10315,10318,10320],{"class":236,"line":546},[234,10316,10317],{"class":9026},"          host",[234,10319,6564],{"class":387},[234,10321,10322],{"class":255},"${HOSTNAME}\n",[234,10324,10325,10328,10330],{"class":236,"line":552},[234,10326,10327],{"class":9026},"          __path__",[234,10329,6564],{"class":387},[234,10331,10332],{"class":255},"\u002Fvar\u002Flog\u002F*.log\n",[234,10334,10335],{"class":236,"line":557},[234,10336,412],{"emptyLinePlaceholder":411},[234,10338,10339,10341,10343,10345],{"class":236,"line":594},[234,10340,9552],{"class":387},[234,10342,9573],{"class":9026},[234,10344,6564],{"class":387},[234,10346,10347],{"class":255},"docker\n",[234,10349,10350,10353],{"class":236,"line":635},[234,10351,10352],{"class":9026},"    docker_sd_configs",[234,10354,9030],{"class":387},[234,10356,10357,10359,10361,10363],{"class":236,"line":643},[234,10358,9059],{"class":387},[234,10360,1650],{"class":9026},[234,10362,6564],{"class":387},[234,10364,10365],{"class":255},"unix:\u002F\u002F\u002Fvar\u002Frun\u002Fdocker.sock\n",[234,10367,10368,10371],{"class":236,"line":659},[234,10369,10370],{"class":9026},"    relabel_configs",[234,10372,9030],{"class":387},[234,10374,10375,10377,10380,10382,10385],{"class":236,"line":683},[234,10376,9059],{"class":387},[234,10378,10379],{"class":9026},"source_labels",[234,10381,9530],{"class":387},[234,10383,10384],{"class":255},"'__meta_docker_container_name'",[234,10386,9536],{"class":387},[234,10388,10389,10392,10394],{"class":236,"line":695},[234,10390,10391],{"class":9026},"        target_label",[234,10393,6564],{"class":387},[234,10395,10396],{"class":255},"'container'\n",[12,10398,10399,10400,10403,10404,10406],{},"Importante: o endpoint ",[231,10401,10402],{},"http:\u002F\u002Fmonitor.seudominio.com:3100\u002Floki\u002Fapi\u002Fv1\u002Fpush"," precisa estar acessível dos servidores. Se você seguiu o passo 2 e amarrou Loki em ",[231,10405,9368],{},", você tem duas opções: expor a 3100 via reverse proxy com autenticação básica, ou abrir um túnel SSH\u002FWireGuard entre os servidores. A segunda opção é mais segura e o que recomendamos.",[12,10408,10409,10410,10413],{},"Validação: no Grafana, vá em Explore, selecione a fonte de dados Loki, rode ",[231,10411,10412],{},"{job=\"varlogs\"}"," e veja os logs aparecendo em tempo real.",[19,10415,10417],{"id":10416},"passo-6-como-importar-os-dashboards-do-grafana","Passo 6 — Como importar os dashboards do Grafana?",[12,10419,8935,10420,101],{},[27,10421,10422],{},"20 minutos",[12,10424,10425,10426,10429,10430,101],{},"Acesse ",[231,10427,10428],{},"https:\u002F\u002Fmonitor.seudominio.com"," (depois de configurar o reverse proxy do passo 8 — pode pular pra lá agora se quiser). Login admin com a senha do ",[231,10431,9376],{},[12,10433,10434,10435,1272],{},"Adicione as duas fontes de dados via provisioning automático. Em ",[231,10436,10437],{},"grafana\u002Fprovisioning\u002Fdatasources\u002Fdatasources.yml",[224,10439,10441],{"className":9017,"code":10440,"language":9019,"meta":229,"style":229},"apiVersion: 1\ndatasources:\n  - name: Prometheus\n    type: prometheus\n    access: proxy\n    url: http:\u002F\u002Fprometheus:9090\n    isDefault: true\n  - name: Loki\n    type: loki\n    access: proxy\n    url: http:\u002F\u002Floki:3100\n",[231,10442,10443,10452,10459,10471,10481,10491,10501,10510,10521,10530,10538],{"__ignoreMap":229},[234,10444,10445,10448,10450],{"class":236,"line":237},[234,10446,10447],{"class":9026},"apiVersion",[234,10449,6564],{"class":387},[234,10451,10050],{"class":251},[234,10453,10454,10457],{"class":236,"line":244},[234,10455,10456],{"class":9026},"datasources",[234,10458,9030],{"class":387},[234,10460,10461,10463,10466,10468],{"class":236,"line":271},[234,10462,9552],{"class":387},[234,10464,10465],{"class":9026},"name",[234,10467,6564],{"class":387},[234,10469,10470],{"class":255},"Prometheus\n",[234,10472,10473,10476,10478],{"class":236,"line":415},[234,10474,10475],{"class":9026},"    type",[234,10477,6564],{"class":387},[234,10479,10480],{"class":255},"prometheus\n",[234,10482,10483,10486,10488],{"class":236,"line":434},[234,10484,10485],{"class":9026},"    access",[234,10487,6564],{"class":387},[234,10489,10490],{"class":255},"proxy\n",[234,10492,10493,10496,10498],{"class":236,"line":459},[234,10494,10495],{"class":9026},"    url",[234,10497,6564],{"class":387},[234,10499,10500],{"class":255},"http:\u002F\u002Fprometheus:9090\n",[234,10502,10503,10506,10508],{"class":236,"line":464},[234,10504,10505],{"class":9026},"    isDefault",[234,10507,6564],{"class":387},[234,10509,10193],{"class":251},[234,10511,10512,10514,10516,10518],{"class":236,"line":479},[234,10513,9552],{"class":387},[234,10515,10465],{"class":9026},[234,10517,6564],{"class":387},[234,10519,10520],{"class":255},"Loki\n",[234,10522,10523,10525,10527],{"class":236,"line":484},[234,10524,10475],{"class":9026},[234,10526,6564],{"class":387},[234,10528,10529],{"class":255},"loki\n",[234,10531,10532,10534,10536],{"class":236,"line":490},[234,10533,10485],{"class":9026},[234,10535,6564],{"class":387},[234,10537,10490],{"class":255},[234,10539,10540,10542,10544],{"class":236,"line":508},[234,10541,10495],{"class":9026},[234,10543,6564],{"class":387},[234,10545,10546],{"class":255},"http:\u002F\u002Floki:3100\n",[12,10548,10549,10550,10553],{},"Reinicie o Grafana com ",[231,10551,10552],{},"docker compose restart grafana"," e as fontes aparecem automaticamente.",[12,10555,10556,10557,10560],{},"Importe os dashboards prontos. Em ",[27,10558,10559],{},"Dashboards → New → Import",", cole o ID do dashboard:",[2735,10562,10563,10569,10575],{},[70,10564,10565,10568],{},[27,10566,10567],{},"1860"," — Node Exporter Full. CPU, RAM, disco, rede, sistema de arquivos. É o dashboard mais usado da comunidade Prometheus, com razão.",[70,10570,10571,10574],{},[27,10572,10573],{},"13639"," — Logs \u002F App. Visualização básica de logs do Loki com filtros por job, container, host.",[70,10576,10577,10580],{},[27,10578,10579],{},"15172"," — Cluster overview. Visão consolidada por servidor, útil pra cluster pequeno.",[12,10582,10583,10584,10587],{},"Customize cada um pra usar ",[231,10585,10586],{},"environment=\"production\""," no filtro padrão. Depois de duas semanas usando, você vai querer criar dashboards próprios pra workloads específicos — não tem atalho aí, é tempo de cadeira.",[19,10589,10591],{"id":10590},"passo-7-como-configurar-alertas-basicos","Passo 7 — Como configurar alertas básicos?",[12,10593,8935,10594,101],{},[27,10595,8994],{},[12,10597,10598],{},"Alertas são onde 80% dos times tropeçam: ou colocam pouquíssimos e descobrem incidentes pelos clientes, ou colocam dezenas e desensibilizam o time.",[12,10600,10601,10602,10605,10606,1272],{},"Comece com ",[27,10603,10604],{},"seis alertas essenciais",". Em ",[231,10607,10608],{},"prometheus\u002Falerts.yml",[224,10610,10612],{"className":9017,"code":10611,"language":9019,"meta":229,"style":229},"groups:\n  - name: essentials\n    interval: 30s\n    rules:\n      - alert: ServerDown\n        expr: up{job=\"node\"} == 0\n        for: 2m\n        labels:\n          severity: critical\n        annotations:\n          summary: \"Servidor {{ $labels.instance }} está fora do ar\"\n\n      - alert: HighCPU\n        expr: 100 - (avg by(instance) (rate(node_cpu_seconds_total{mode=\"idle\"}[5m])) * 100) > 80\n        for: 10m\n        labels:\n          severity: warning\n\n      - alert: DiskAlmostFull\n        expr: (node_filesystem_avail_bytes{mountpoint=\"\u002F\"} \u002F node_filesystem_size_bytes{mountpoint=\"\u002F\"}) * 100 \u003C 15\n        for: 5m\n        labels:\n          severity: critical\n\n      - alert: HighMemory\n        expr: (1 - (node_memory_MemAvailable_bytes \u002F node_memory_MemTotal_bytes)) * 100 > 90\n        for: 10m\n        labels:\n          severity: warning\n\n      - alert: HighHTTPErrorRate\n        expr: sum(rate(http_requests_total{status=~\"5..\"}[5m])) \u002F sum(rate(http_requests_total[5m])) > 0.05\n        for: 5m\n        labels:\n          severity: critical\n\n      - alert: HighLatency\n        expr: histogram_quantile(0.99, sum(rate(http_request_duration_seconds_bucket[5m])) by (le)) > 2\n        for: 10m\n        labels:\n          severity: warning\n",[231,10613,10614,10621,10632,10642,10649,10661,10671,10681,10687,10697,10704,10714,10718,10729,10738,10747,10753,10762,10766,10777,10786,10795,10801,10809,10813,10824,10833,10841,10847,10855,10859,10870,10879,10887,10893,10901,10905,10916,10925,10933,10939],{"__ignoreMap":229},[234,10615,10616,10619],{"class":236,"line":237},[234,10617,10618],{"class":9026},"groups",[234,10620,9030],{"class":387},[234,10622,10623,10625,10627,10629],{"class":236,"line":244},[234,10624,9552],{"class":387},[234,10626,10465],{"class":9026},[234,10628,6564],{"class":387},[234,10630,10631],{"class":255},"essentials\n",[234,10633,10634,10637,10639],{"class":236,"line":271},[234,10635,10636],{"class":9026},"    interval",[234,10638,6564],{"class":387},[234,10640,10641],{"class":255},"30s\n",[234,10643,10644,10647],{"class":236,"line":415},[234,10645,10646],{"class":9026},"    rules",[234,10648,9030],{"class":387},[234,10650,10651,10653,10656,10658],{"class":236,"line":434},[234,10652,9059],{"class":387},[234,10654,10655],{"class":9026},"alert",[234,10657,6564],{"class":387},[234,10659,10660],{"class":255},"ServerDown\n",[234,10662,10663,10666,10668],{"class":236,"line":459},[234,10664,10665],{"class":9026},"        expr",[234,10667,6564],{"class":387},[234,10669,10670],{"class":255},"up{job=\"node\"} == 0\n",[234,10672,10673,10676,10678],{"class":236,"line":464},[234,10674,10675],{"class":9026},"        for",[234,10677,6564],{"class":387},[234,10679,10680],{"class":255},"2m\n",[234,10682,10683,10685],{"class":236,"line":479},[234,10684,9661],{"class":9026},[234,10686,9030],{"class":387},[234,10688,10689,10692,10694],{"class":236,"line":484},[234,10690,10691],{"class":9026},"          severity",[234,10693,6564],{"class":387},[234,10695,10696],{"class":255},"critical\n",[234,10698,10699,10702],{"class":236,"line":490},[234,10700,10701],{"class":9026},"        annotations",[234,10703,9030],{"class":387},[234,10705,10706,10709,10711],{"class":236,"line":508},[234,10707,10708],{"class":9026},"          summary",[234,10710,6564],{"class":387},[234,10712,10713],{"class":255},"\"Servidor {{ $labels.instance }} está fora do ar\"\n",[234,10715,10716],{"class":236,"line":529},[234,10717,412],{"emptyLinePlaceholder":411},[234,10719,10720,10722,10724,10726],{"class":236,"line":535},[234,10721,9059],{"class":387},[234,10723,10655],{"class":9026},[234,10725,6564],{"class":387},[234,10727,10728],{"class":255},"HighCPU\n",[234,10730,10731,10733,10735],{"class":236,"line":546},[234,10732,10665],{"class":9026},[234,10734,6564],{"class":387},[234,10736,10737],{"class":255},"100 - (avg by(instance) (rate(node_cpu_seconds_total{mode=\"idle\"}[5m])) * 100) > 80\n",[234,10739,10740,10742,10744],{"class":236,"line":552},[234,10741,10675],{"class":9026},[234,10743,6564],{"class":387},[234,10745,10746],{"class":255},"10m\n",[234,10748,10749,10751],{"class":236,"line":557},[234,10750,9661],{"class":9026},[234,10752,9030],{"class":387},[234,10754,10755,10757,10759],{"class":236,"line":594},[234,10756,10691],{"class":9026},[234,10758,6564],{"class":387},[234,10760,10761],{"class":255},"warning\n",[234,10763,10764],{"class":236,"line":635},[234,10765,412],{"emptyLinePlaceholder":411},[234,10767,10768,10770,10772,10774],{"class":236,"line":643},[234,10769,9059],{"class":387},[234,10771,10655],{"class":9026},[234,10773,6564],{"class":387},[234,10775,10776],{"class":255},"DiskAlmostFull\n",[234,10778,10779,10781,10783],{"class":236,"line":659},[234,10780,10665],{"class":9026},[234,10782,6564],{"class":387},[234,10784,10785],{"class":255},"(node_filesystem_avail_bytes{mountpoint=\"\u002F\"} \u002F node_filesystem_size_bytes{mountpoint=\"\u002F\"}) * 100 \u003C 15\n",[234,10787,10788,10790,10792],{"class":236,"line":683},[234,10789,10675],{"class":9026},[234,10791,6564],{"class":387},[234,10793,10794],{"class":255},"5m\n",[234,10796,10797,10799],{"class":236,"line":695},[234,10798,9661],{"class":9026},[234,10800,9030],{"class":387},[234,10802,10803,10805,10807],{"class":236,"line":717},[234,10804,10691],{"class":9026},[234,10806,6564],{"class":387},[234,10808,10696],{"class":255},[234,10810,10811],{"class":236,"line":723},[234,10812,412],{"emptyLinePlaceholder":411},[234,10814,10815,10817,10819,10821],{"class":236,"line":729},[234,10816,9059],{"class":387},[234,10818,10655],{"class":9026},[234,10820,6564],{"class":387},[234,10822,10823],{"class":255},"HighMemory\n",[234,10825,10826,10828,10830],{"class":236,"line":734},[234,10827,10665],{"class":9026},[234,10829,6564],{"class":387},[234,10831,10832],{"class":255},"(1 - (node_memory_MemAvailable_bytes \u002F node_memory_MemTotal_bytes)) * 100 > 90\n",[234,10834,10835,10837,10839],{"class":236,"line":771},[234,10836,10675],{"class":9026},[234,10838,6564],{"class":387},[234,10840,10746],{"class":255},[234,10842,10843,10845],{"class":236,"line":776},[234,10844,9661],{"class":9026},[234,10846,9030],{"class":387},[234,10848,10849,10851,10853],{"class":236,"line":815},[234,10850,10691],{"class":9026},[234,10852,6564],{"class":387},[234,10854,10761],{"class":255},[234,10856,10857],{"class":236,"line":820},[234,10858,412],{"emptyLinePlaceholder":411},[234,10860,10861,10863,10865,10867],{"class":236,"line":826},[234,10862,9059],{"class":387},[234,10864,10655],{"class":9026},[234,10866,6564],{"class":387},[234,10868,10869],{"class":255},"HighHTTPErrorRate\n",[234,10871,10872,10874,10876],{"class":236,"line":846},[234,10873,10665],{"class":9026},[234,10875,6564],{"class":387},[234,10877,10878],{"class":255},"sum(rate(http_requests_total{status=~\"5..\"}[5m])) \u002F sum(rate(http_requests_total[5m])) > 0.05\n",[234,10880,10881,10883,10885],{"class":236,"line":859},[234,10882,10675],{"class":9026},[234,10884,6564],{"class":387},[234,10886,10794],{"class":255},[234,10888,10889,10891],{"class":236,"line":872},[234,10890,9661],{"class":9026},[234,10892,9030],{"class":387},[234,10894,10895,10897,10899],{"class":236,"line":898},[234,10896,10691],{"class":9026},[234,10898,6564],{"class":387},[234,10900,10696],{"class":255},[234,10902,10903],{"class":236,"line":913},[234,10904,412],{"emptyLinePlaceholder":411},[234,10906,10907,10909,10911,10913],{"class":236,"line":1886},[234,10908,9059],{"class":387},[234,10910,10655],{"class":9026},[234,10912,6564],{"class":387},[234,10914,10915],{"class":255},"HighLatency\n",[234,10917,10918,10920,10922],{"class":236,"line":1901},[234,10919,10665],{"class":9026},[234,10921,6564],{"class":387},[234,10923,10924],{"class":255},"histogram_quantile(0.99, sum(rate(http_request_duration_seconds_bucket[5m])) by (le)) > 2\n",[234,10926,10927,10929,10931],{"class":236,"line":1920},[234,10928,10675],{"class":9026},[234,10930,6564],{"class":387},[234,10932,10746],{"class":255},[234,10934,10935,10937],{"class":236,"line":1944},[234,10936,9661],{"class":9026},[234,10938,9030],{"class":387},[234,10940,10941,10943,10945],{"class":236,"line":1962},[234,10942,10691],{"class":9026},[234,10944,6564],{"class":387},[234,10946,10761],{"class":255},[12,10948,10949,10950,10953],{},"E o ",[231,10951,10952],{},"alertmanager\u002Falertmanager.yml"," apontando pra um webhook do Slack ou Discord:",[224,10955,10957],{"className":9017,"code":10956,"language":9019,"meta":229,"style":229},"route:\n  group_by: ['alertname', 'severity']\n  group_wait: 30s\n  group_interval: 5m\n  repeat_interval: 4h\n  receiver: 'slack-default'\n  routes:\n    - match:\n        severity: critical\n      receiver: 'slack-critical'\n      repeat_interval: 1h\n\nreceivers:\n  - name: 'slack-default'\n    slack_configs:\n      - api_url: 'https:\u002F\u002Fhooks.slack.com\u002Fservices\u002FSEU\u002FWEBHOOK\u002FAQUI'\n        channel: '#alerts'\n        send_resolved: true\n\n  - name: 'slack-critical'\n    slack_configs:\n      - api_url: 'https:\u002F\u002Fhooks.slack.com\u002Fservices\u002FSEU\u002FWEBHOOK\u002FAQUI'\n        channel: '#alerts-critical'\n        send_resolved: true\n",[231,10958,10959,10966,10983,10992,11001,11011,11021,11028,11037,11046,11056,11066,11070,11077,11087,11094,11106,11116,11125,11129,11139,11145,11155,11164],{"__ignoreMap":229},[234,10960,10961,10964],{"class":236,"line":237},[234,10962,10963],{"class":9026},"route",[234,10965,9030],{"class":387},[234,10967,10968,10971,10973,10976,10978,10981],{"class":236,"line":244},[234,10969,10970],{"class":9026},"  group_by",[234,10972,9530],{"class":387},[234,10974,10975],{"class":255},"'alertname'",[234,10977,571],{"class":387},[234,10979,10980],{"class":255},"'severity'",[234,10982,9536],{"class":387},[234,10984,10985,10988,10990],{"class":236,"line":271},[234,10986,10987],{"class":9026},"  group_wait",[234,10989,6564],{"class":387},[234,10991,10641],{"class":255},[234,10993,10994,10997,10999],{"class":236,"line":415},[234,10995,10996],{"class":9026},"  group_interval",[234,10998,6564],{"class":387},[234,11000,10794],{"class":255},[234,11002,11003,11006,11008],{"class":236,"line":434},[234,11004,11005],{"class":9026},"  repeat_interval",[234,11007,6564],{"class":387},[234,11009,11010],{"class":255},"4h\n",[234,11012,11013,11016,11018],{"class":236,"line":459},[234,11014,11015],{"class":9026},"  receiver",[234,11017,6564],{"class":387},[234,11019,11020],{"class":255},"'slack-default'\n",[234,11022,11023,11026],{"class":236,"line":464},[234,11024,11025],{"class":9026},"  routes",[234,11027,9030],{"class":387},[234,11029,11030,11032,11035],{"class":236,"line":479},[234,11031,9514],{"class":387},[234,11033,11034],{"class":9026},"match",[234,11036,9030],{"class":387},[234,11038,11039,11042,11044],{"class":236,"line":484},[234,11040,11041],{"class":9026},"        severity",[234,11043,6564],{"class":387},[234,11045,10696],{"class":255},[234,11047,11048,11051,11053],{"class":236,"line":490},[234,11049,11050],{"class":9026},"      receiver",[234,11052,6564],{"class":387},[234,11054,11055],{"class":255},"'slack-critical'\n",[234,11057,11058,11061,11063],{"class":236,"line":508},[234,11059,11060],{"class":9026},"      repeat_interval",[234,11062,6564],{"class":387},[234,11064,11065],{"class":255},"1h\n",[234,11067,11068],{"class":236,"line":529},[234,11069,412],{"emptyLinePlaceholder":411},[234,11071,11072,11075],{"class":236,"line":535},[234,11073,11074],{"class":9026},"receivers",[234,11076,9030],{"class":387},[234,11078,11079,11081,11083,11085],{"class":236,"line":546},[234,11080,9552],{"class":387},[234,11082,10465],{"class":9026},[234,11084,6564],{"class":387},[234,11086,11020],{"class":255},[234,11088,11089,11092],{"class":236,"line":552},[234,11090,11091],{"class":9026},"    slack_configs",[234,11093,9030],{"class":387},[234,11095,11096,11098,11101,11103],{"class":236,"line":557},[234,11097,9059],{"class":387},[234,11099,11100],{"class":9026},"api_url",[234,11102,6564],{"class":387},[234,11104,11105],{"class":255},"'https:\u002F\u002Fhooks.slack.com\u002Fservices\u002FSEU\u002FWEBHOOK\u002FAQUI'\n",[234,11107,11108,11111,11113],{"class":236,"line":594},[234,11109,11110],{"class":9026},"        channel",[234,11112,6564],{"class":387},[234,11114,11115],{"class":255},"'#alerts'\n",[234,11117,11118,11121,11123],{"class":236,"line":635},[234,11119,11120],{"class":9026},"        send_resolved",[234,11122,6564],{"class":387},[234,11124,10193],{"class":251},[234,11126,11127],{"class":236,"line":643},[234,11128,412],{"emptyLinePlaceholder":411},[234,11130,11131,11133,11135,11137],{"class":236,"line":659},[234,11132,9552],{"class":387},[234,11134,10465],{"class":9026},[234,11136,6564],{"class":387},[234,11138,11055],{"class":255},[234,11140,11141,11143],{"class":236,"line":683},[234,11142,11091],{"class":9026},[234,11144,9030],{"class":387},[234,11146,11147,11149,11151,11153],{"class":236,"line":695},[234,11148,9059],{"class":387},[234,11150,11100],{"class":9026},[234,11152,6564],{"class":387},[234,11154,11105],{"class":255},[234,11156,11157,11159,11161],{"class":236,"line":717},[234,11158,11110],{"class":9026},[234,11160,6564],{"class":387},[234,11162,11163],{"class":255},"'#alerts-critical'\n",[234,11165,11166,11168,11170],{"class":236,"line":723},[234,11167,11120],{"class":9026},[234,11169,6564],{"class":387},[234,11171,10193],{"class":251},[12,11173,11174,11175,11178,11179,11182],{},"Dois detalhes que economizam noite de sono. O ",[231,11176,11177],{},"for: 10m"," em CPU evita que picos curtos virem alertas — o servidor pode chegar a 95% por 30 segundos e isso ser normal. O ",[231,11180,11181],{},"repeat_interval: 4h"," pra warnings garante que um warning resolvido em uma hora não vire 60 mensagens — o Alertmanager agrupa.",[12,11184,11185,11186,11188,11189,11192,11193,11196],{},"Recarregue o Prometheus (",[231,11187,9756],{},") e teste forçando um alerta: ",[231,11190,11191],{},"stress --cpu 4 --timeout 700s"," em algum servidor deve disparar ",[231,11194,11195],{},"HighCPU"," em 10 minutos.",[19,11198,11200],{"id":11199},"passo-8-como-colocar-reverse-proxy-e-tls-na-frente","Passo 8 — Como colocar reverse proxy e TLS na frente?",[12,11202,8935,11203,101],{},[27,11204,10422],{},[12,11206,11207,11208,11210],{},"Pra acessar Grafana via ",[231,11209,10428],{}," com certificado válido, você precisa de algo na frente da porta 3000. Duas opções:",[67,11212,11213,11222],{},[70,11214,11215,11217,11218,11221],{},[27,11216,5605],{}," — se você já tem o cluster HeroCtl rodando, basta declarar o Grafana como job com ",[231,11219,11220],{},"ingress: { host: monitor.seudominio.com, tls: true }",". Certificado Let's Encrypt automático, sem ferramenta adicional.",[70,11223,11224,11227,11228],{},[27,11225,11226],{},"Caddy standalone"," no próprio VPS de observabilidade — também emite Let's Encrypt automaticamente. Caddyfile mínimo:",[224,11229,11232],{"className":11230,"code":11231,"language":2530},[2528],"monitor.seudominio.com {\n  reverse_proxy localhost:3000\n  basicauth \u002Flogin {\n    admin \u003Chash_bcrypt>\n  }\n}\n",[231,11233,11231],{"__ignoreMap":229},[12,11235,11236,11237,11240],{},"Pra defesa em profundidade, mantenha autenticação básica do Caddy\u002Froteador na frente do login do Grafana — duas barreiras, não uma. A segunda é especialmente importante porque o login default do Grafana é ",[231,11238,11239],{},"admin\u002Fadmin"," e a primeira coisa que bots fazem em um Grafana exposto é tentar essa combinação.",[19,11242,11244],{"id":11243},"passo-9-como-instrumentar-metricas-de-aplicacao","Passo 9 — Como instrumentar métricas de aplicação?",[12,11246,8935,11247,101],{},[27,11248,11249],{},"varia conforme número de aplicações",[12,11251,11252],{},"Métricas de sistema são metade da história. A outra metade é o que sua aplicação está fazendo — quantas requisições por segundo, qual a latência p99, quantos erros, qual o tamanho da fila de jobs em background.",[12,11254,11255],{},"Cada linguagem popular tem cliente Prometheus oficial:",[2735,11257,11258,11266,11274,11281],{},[70,11259,11260,6564,11263],{},[27,11261,11262],{},"Node.js",[231,11264,11265],{},"prom-client",[70,11267,11268,6564,11271],{},[27,11269,11270],{},"Python",[231,11272,11273],{},"prometheus-client",[70,11275,11276,6564,11279],{},[27,11277,11278],{},"Ruby",[231,11280,11273],{},[70,11282,11283,6564,11286],{},[27,11284,11285],{},"Go",[231,11287,11288],{},"github.com\u002Fprometheus\u002Fclient_golang",[12,11290,11291],{},"O padrão mínimo são três métricas por endpoint HTTP:",[2735,11293,11294,11308,11314],{},[70,11295,11296,11299,11300,571,11303,571,11306,101],{},[231,11297,11298],{},"http_requests_total"," — counter, com labels ",[231,11301,11302],{},"method",[231,11304,11305],{},"path",[231,11307,614],{},[70,11309,11310,11313],{},[231,11311,11312],{},"http_request_duration_seconds"," — histogram, mesmo set de labels.",[70,11315,11316,11319,11320,11323],{},[231,11317,11318],{},"app_errors_total"," — counter, com label ",[231,11321,11322],{},"kind"," (\"validation\", \"db\", \"external_api\", etc).",[12,11325,11326,11327,11329,11330,11332],{},"Exponha tudo isso em ",[231,11328,8915],{},". Adicione o endpoint no ",[231,11331,9564],{}," do Prometheus. Em horas você tem dashboards por endpoint, alertas por taxa de erro, e a capacidade de responder \"o que estava acontecendo às 3:14 de ontem\" com um gráfico em vez de um chute.",[12,11334,11335,11336,11339,11340,11343],{},"Cuidado com ",[27,11337,11338],{},"cardinalidade",". Cada combinação única de labels vira uma série temporal separada. Se você botar ",[231,11341,11342],{},"user_id"," como label, com 100k usuários você cria 100k séries — e o Prometheus vai consumir 8+ GB de RAM só pra indexar isso. Regra prática: labels têm valores em conjuntos pequenos (status code: 5 valores; método: 5 valores; path: dezenas). Identificadores únicos vão em logs, não em métricas.",[19,11345,11347],{"id":11346},"como-rodar-isso-dentro-do-heroctl-em-vez-de-vps-dedicado","Como rodar isso dentro do HeroCtl em vez de VPS dedicado?",[12,11349,11350],{},"Pra clusters que já rodam o orquestrador, faz sentido considerar a stack como mais um job. Trade-off: você economiza um VPS, mas perde isolamento (se o cluster morrer, o monitoring morre junto).",[12,11352,11353],{},"A topologia fica assim:",[2735,11355,11356,11362,11368,11374],{},[70,11357,11358,11361],{},[27,11359,11360],{},"1 job spec único"," com 4 tasks: prometheus, grafana, loki, alertmanager.",[70,11363,11364,11367],{},[27,11365,11366],{},"Volumes replicados"," no cluster — os dados sobrevivem a falha de um nó.",[70,11369,11370,11373],{},[27,11371,11372],{},"Roteador integrado"," faz o TLS automático via subdomínio. Não precisa de Caddy adicional.",[70,11375,11376,11379],{},[27,11377,11378],{},"Métricas do próprio cluster"," já são expostas em formato Prometheus na API administrativa, então o scrape é direto.",[12,11381,11382],{},"Pra produção crítica, recomendamos a separação física (VPS dedicado fora do cluster). Pra projeto pessoal, MVP, ou time pequeno onde \"tudo cair junto\" é aceitável, rodar dentro é mais barato e operacionalmente mais simples. O job spec inteiro fica em torno de 80 linhas de manifesto.",[19,11384,11386],{"id":11385},"quanto-custa-essa-stack-por-mes-no-brasil","Quanto custa essa stack por mês no Brasil?",[119,11388,11389,11399],{},[122,11390,11391],{},[125,11392,11393,11396],{},[128,11394,11395],{},"Item",[128,11397,11398],{},"Custo mensal (BRL)",[141,11400,11401,11409,11417,11425],{},[125,11402,11403,11406],{},[146,11404,11405],{},"VPS observability dedicado (4 GB RAM)",[146,11407,11408],{},"R$40 a R$80",[125,11410,11411,11414],{},[146,11412,11413],{},"Object storage pra retenção longa de logs (opcional)",[146,11415,11416],{},"R$30",[125,11418,11419,11422],{},[146,11420,11421],{},"Tempo de manutenção (2 a 4h × valor da hora)",[146,11423,11424],{},"R$200 a R$400",[125,11426,11427,11432],{},[146,11428,11429],{},[27,11430,11431],{},"Total operacional",[146,11433,11434],{},[27,11435,11436],{},"R$300 a R$500",[12,11438,11439],{},"Pra comparação, uma assinatura de Datadog ou New Relic com cobertura equivalente (5 hosts, retenção de logs de 30 dias, alertas, dashboards) sai em torno de R$1.500 a R$2.000 por mês — sem contar o overage automático que aparece no fim do mês quando alguém esquece um log verboso ligado.",[12,11441,11442],{},"A diferença não é pequena: em um ano, a stack open-source self-hosted economiza entre R$12.000 e R$18.000. Pra startup em estágio inicial, isso é meio engenheiro júnior.",[19,11444,11446],{"id":11445},"tabela-de-portas-recursos-e-caracteristicas-por-componente","Tabela de portas, recursos e características por componente",[119,11448,11449,11469],{},[122,11450,11451],{},[125,11452,11453,11455,11458,11460,11463,11466],{},[128,11454,130],{},[128,11456,11457],{},"Porta",[128,11459,3873],{},[128,11461,11462],{},"Disco",[128,11464,11465],{},"Retenção default",[128,11467,11468],{},"Formato dos dados",[141,11470,11471,11490,11508,11526,11545,11561],{},[125,11472,11473,11475,11478,11481,11484,11487],{},[146,11474,8839],{},[146,11476,11477],{},"9090",[146,11479,11480],{},"512 MB",[146,11482,11483],{},"10 GB",[146,11485,11486],{},"15 dias",[146,11488,11489],{},"TSDB binário",[125,11491,11492,11494,11497,11500,11503,11505],{},[146,11493,8845],{},[146,11495,11496],{},"3000",[146,11498,11499],{},"256 MB",[146,11501,11502],{},"1 GB",[146,11504,3056],{},[146,11506,11507],{},"SQLite ou Postgres",[125,11509,11510,11512,11515,11517,11520,11523],{},[146,11511,8851],{},[146,11513,11514],{},"3100",[146,11516,11480],{},[146,11518,11519],{},"30 GB",[146,11521,11522],{},"30 dias (configurável)",[146,11524,11525],{},"chunks comprimidos",[125,11527,11528,11531,11534,11537,11540,11542],{},[146,11529,11530],{},"Promtail \u002F Agent",[146,11532,11533],{},"9080",[146,11535,11536],{},"128 MB",[146,11538,11539],{},"mínimo",[146,11541,3056],{},[146,11543,11544],{},"passa por valor",[125,11546,11547,11549,11552,11554,11556,11558],{},[146,11548,8869],{},[146,11550,11551],{},"9093",[146,11553,11536],{},[146,11555,11502],{},[146,11557,3056],{},[146,11559,11560],{},"log de notificações",[125,11562,11563,11565,11568,11571,11573,11575],{},[146,11564,8863],{},[146,11566,11567],{},"9100",[146,11569,11570],{},"64 MB",[146,11572,11539],{},[146,11574,3056],{},[146,11576,11577],{},"endpoint de scrape",[12,11579,11580],{},"Essas são as mínimas viáveis pra cluster pequeno. Em produção com 30 servidores e tráfego real, multiplique RAM por 3 e disco por 5.",[19,11582,11584],{"id":11583},"os-quatro-erros-que-matam-stack-de-monitoring-nova","Os quatro erros que matam stack de monitoring nova",[12,11586,11587],{},"Times montando observabilidade pela primeira vez tropeçam quase sempre nos mesmos quatro erros. Saber sobre eles antes economiza meses.",[12,11589,11590,11593,11594,11597],{},[27,11591,11592],{},"Não monitorar o monitoring."," O Prometheus parou de scrape na quinta-feira; ninguém viu. Na quarta-feira da semana seguinte um servidor caiu de verdade e descobriram que não tinha alerta porque o Prometheus estava morto há 6 dias. Solução: configure um cron externo simples (até um Pingdom gratuito serve) que bate em ",[231,11595,11596],{},"https:\u002F\u002Fmonitor.seudominio.com\u002Fapi\u002Fhealth"," a cada 5 minutos e te avisa quando o próprio Grafana cair.",[12,11599,11600,11603,11604,11607],{},[27,11601,11602],{},"Sem estratégia de retenção."," Disco enche em três meses, Prometheus para de gravar, alguém deleta tudo no desespero, perde 90 dias de histórico. Configure ",[231,11605,11606],{},"--storage.tsdb.retention.time=30d"," desde o dia um e estabeleça um job de housekeeping.",[12,11609,11610,11613,11614,571,11616,11619],{},[27,11611,11612],{},"Cardinalidade alta em labels."," Já cobrimos no passo 9, mas vale repetir: cada ",[231,11615,11342],{},[231,11617,11618],{},"request_id"," ou UUID que vira label é um número que multiplica explosivamente o consumo de RAM do Prometheus. Identificadores únicos vão pra Loki, não pro Prometheus.",[12,11621,11622,11625],{},[27,11623,11624],{},"Alertas barulhentos."," O time recebe 200 alertas por dia. Em duas semanas, ninguém olha mais. Quando o site cair de verdade, o alerta vai estar no meio de outros 199. Solução: comece com seis alertas (os do passo 7), audite a cada duas semanas, e exclua tudo que disparou mas não exigiu ação humana. Alerta sem ação é ruído.",[19,11627,3226],{"id":3225},[12,11629,11630,11633],{},[27,11631,11632],{},"Posso rodar tudo num VPS de 2 GB?","\nTecnicamente sim, pra cluster de até 3 servidores e poucas aplicações. Na prática você vai bater no teto de RAM em 2 a 3 meses, especialmente se importar dashboards densos no Grafana. Pague os 50 reais a mais e vá direto pro VPS de 4 GB — o tempo que você economiza não brigando com OOM kills paga sozinho.",[12,11635,11636,11639],{},[27,11637,11638],{},"Quanto de disco pra 30 dias de logs?","\nDepende totalmente do volume de logs da sua aplicação. Regra grosseira pra startup pequena: cluster de 4 servidores com aplicações web normais gera 1 a 5 GB de logs por dia depois de compressão do Loki. Trinta dias dá entre 30 e 150 GB. Comece com 50 GB de SSD, monitore o crescimento por duas semanas, expanda se necessário. Se você for muito mais que isso, é hora de ir pra object storage.",[12,11641,11642,11645],{},[27,11643,11644],{},"Grafana Cloud vs self-hosted, qual escolher?","\nGrafana Cloud free tier é generoso (10k séries, 50 GB de logs, retenção de 14 dias) e elimina o trabalho de manter o servidor. Pra projeto solo ou time muito pequeno, faz sentido. A partir do momento que você passa do free tier, os preços escalam rápido — a partir de US$50\u002Fmês — e você perde o controle sobre os dados. Self-hosted custa hardware + tempo, Cloud custa dinheiro + lock-in. Pra empresa que pretende crescer e tem um dev DevOps no time, self-hosted ganha.",[12,11647,11648,11651],{},[27,11649,11650],{},"Promtail ou Grafana Agent?","\nEm 2026, o Grafana Agent (rebatizado pra Grafana Alloy) está substituindo o Promtail oficialmente. Pra setup novo, vá direto de Alloy. Pra setup que já roda Promtail há tempo, não tem urgência em migrar — o Promtail vai continuar funcionando por anos.",[12,11653,11654,11657,11658,11660],{},[27,11655,11656],{},"OpenTelemetry encaixa onde nessa stack?","\nOTel é o padrão de instrumentação de aplicação que está consolidando. Em vez de usar ",[231,11659,11265],{}," direto, você usa o SDK do OTel e ele exporta pra Prometheus, Loki e Tempo simultaneamente. A vantagem grande é portabilidade — se você quiser trocar Prometheus por outra coisa daqui a 3 anos, sua aplicação não muda uma linha. Pra startup começando hoje, recomendamos OTel desde o dia um.",[12,11662,11663,11666,11667,11670,11671,11674],{},[27,11664,11665],{},"Como faço backup do Prometheus?","\nPrometheus tem snapshot via API: ",[231,11668,11669],{},"curl -X POST localhost:9090\u002Fapi\u002Fv1\u002Fadmin\u002Ftsdb\u002Fsnapshot"," cria um snapshot no diretório de dados. Faça isso uma vez por dia via cron, faça ",[231,11672,11673],{},"tar.gz"," e envie pra object storage. Em caso de desastre, o que você perde é métricas — e métricas, diferente de logs, são tipicamente recuperáveis em horas (volta a coletar e os dashboards voltam). Logs perdidos são perdidos pra sempre, então invista mais em backup de Loki.",[12,11676,11677,11680],{},[27,11678,11679],{},"Tempo (traces distribuídos) vale instalar agora?","\nNão. Traces ficam úteis a partir do momento que você tem 5+ serviços conversando entre si e debugar latência envolve seguir uma requisição por vários hops. Pra arquitetura monolítica ou poucos serviços, traces dão trabalho desproporcional ao valor. Adicione quando a complexidade pedir.",[12,11682,11683,11686],{},[27,11684,11685],{},"Loki indexa full-text como ELK?","\nNão, e essa é a feature, não bug. Loki indexa apenas labels (job, host, container, severity) e o conteúdo do log fica comprimido sem índice. Pra buscar texto, você filtra por labels primeiro e depois faz grep nos chunks resultantes. Isso é o que torna o Loki dez vezes mais barato que ELK em RAM e CPU. Em troca, queries de texto livre em todo o histórico são mais lentas. Pra 90% dos casos de debugging, filtrar por job + host + janela de tempo já reduz pra dezenas de MB onde o grep voa.",[19,11688,11690],{"id":11689},"proximos-passos","Próximos passos",[12,11692,11693],{},"Subiu a stack, tem dashboard, tem alerta, tem log pesquisável? Boa. As próximas três coisas que valem o investimento são, em ordem:",[67,11695,11696,11702,11716],{},[70,11697,11698,11701],{},[27,11699,11700],{},"Custom dashboards por aplicação"," — métricas de negócio (assinaturas criadas\u002Fhora, jobs processados, fila de e-mails) em vez de só infraestrutura.",[70,11703,11704,11707,11708,11711,11712,11715],{},[27,11705,11706],{},"Runbooks linkados nos alertas"," — toda regra em ",[231,11709,11710],{},"alerts.yml"," deve ter ",[231,11713,11714],{},"annotations.runbook_url"," apontando pra uma página explicando o que fazer. Quando o alerta dispara às 3 da manhã, o sono não pensa.",[70,11717,11718,11721],{},[27,11719,11720],{},"Revisão mensal de alertas"," — 30 minutos uma vez por mês auditando o que disparou no mês anterior, deletando o que virou ruído, ajustando thresholds.",[12,11723,11724,11725,11729,11730,101],{},"Pra quem quer ir além e entender por que escolhemos essa stack em vez de SaaS gerenciado, leia ",[3337,11726,11728],{"href":11727},"\u002Fblog\u002Fobservabilidade-sem-datadog-stack-startup","Observabilidade sem Datadog: a stack da startup brasileira",". E pra fechar o ciclo de operação — porque não adianta saber que o banco caiu se você não consegue restaurar — vale ler ",[3337,11731,11733],{"href":11732},"\u002Fblog\u002Fbackup-banco-em-cluster-estrategias-3-da-manha","Backup de banco em cluster: estratégias pras 3 da manhã",[12,11735,11736],{},"Se você quer pular essa montagem toda e rodar a stack como job dentro de um orquestrador que já cuida de TLS, rolling deploy e replicação de volume:",[224,11738,11739],{"className":226,"code":5319,"language":228,"meta":229,"style":229},[231,11740,11741],{"__ignoreMap":229},[234,11742,11743,11745,11747,11749,11751],{"class":236,"line":237},[234,11744,1220],{"class":247},[234,11746,2958],{"class":251},[234,11748,5330],{"class":255},[234,11750,2964],{"class":383},[234,11752,2967],{"class":247},[12,11754,11755],{},"Quatro horas viram quarenta minutos. O resto é o mesmo trabalho de pensar quais alertas importam — e nessa parte ninguém te livra.",[3351,11757,11758],{},"html pre.shiki code .sFSAA, html code.shiki .sFSAA{--shiki-default:#79C0FF}html pre.shiki code .s9uIt, html code.shiki .s9uIt{--shiki-default:#A5D6FF}html pre.shiki code .sQhOw, html code.shiki .sQhOw{--shiki-default:#FFA657}html pre.shiki code .sH3jZ, html code.shiki .sH3jZ{--shiki-default:#8B949E}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}html pre.shiki code .sPWt5, html code.shiki .sPWt5{--shiki-default:#7EE787}",{"title":229,"searchDepth":244,"depth":244,"links":11760},[11761,11762,11763,11764,11765,11766,11767,11768,11769,11770,11771,11772,11773,11774,11775,11776,11777,11778],{"id":21,"depth":244,"text":22},{"id":8828,"depth":244,"text":8829},{"id":8888,"depth":244,"text":8889},{"id":8931,"depth":244,"text":8932},{"id":8988,"depth":244,"text":8989},{"id":9448,"depth":244,"text":9449},{"id":9772,"depth":244,"text":9773},{"id":9938,"depth":244,"text":9939},{"id":10416,"depth":244,"text":10417},{"id":10590,"depth":244,"text":10591},{"id":11199,"depth":244,"text":11200},{"id":11243,"depth":244,"text":11244},{"id":11346,"depth":244,"text":11347},{"id":11385,"depth":244,"text":11386},{"id":11445,"depth":244,"text":11446},{"id":11583,"depth":244,"text":11584},{"id":3225,"depth":244,"text":3226},{"id":11689,"depth":244,"text":11690},"2026-05-12","Tutorial honesto pra subir métricas, logs e dashboards pro seu cluster — em 4 horas, sem Datadog. Stack open-source que cabe em 1 VPS de R$80\u002Fmês.",{},"\u002Fblog\u002Fmonitoring-stack-completa-prometheus-grafana-loki-passo-a-passo",{"title":8781,"description":11780},{"loc":11782},"blog\u002Fmonitoring-stack-completa-prometheus-grafana-loki-passo-a-passo",[11787,11788,11789,11790,3393,3379],"prometheus","grafana","loki","monitoring","oC9lCwAyyAdpz2EHoBKkH49q8NBMUQp9-nioO314jWo",{"id":11793,"title":11794,"author":7,"body":11795,"category":3379,"cover":3380,"date":12744,"description":12745,"draft":3383,"extension":3384,"lastReviewed":3380,"meta":12746,"navigation":411,"path":12747,"readingTime":4401,"seo":12748,"sitemap":12749,"stem":12750,"tags":12751,"__hash__":12756},"blog_pt\u002Fblog\u002Fcloudflare-na-frente-cluster-auto-hospedado-vale-pena.md","Cloudflare na frente do cluster auto-hospedado: vale a pena em 2026?",{"type":9,"value":11796,"toc":12722},[11797,11800,11804,11807,11810,11813,11817,11820,11876,11879,11883,11886,12026,12029,12033,12036,12042,12048,12055,12058,12062,12065,12091,12098,12101,12121,12125,12128,12166,12169,12173,12176,12249,12268,12272,12275,12281,12287,12293,12299,12303,12306,12326,12329,12333,12536,12539,12543,12546,12566,12570,12573,12577,12583,12587,12590,12594,12597,12633,12636,12640,12643,12647,12650,12654,12661,12665,12668,12672,12675,12678,12694,12697,12700,12717,12720],[12,11798,11799],{},"A pergunta volta toda semana num grupo de DevOps brasileiro: \"subi meu cluster com três servidores na DigitalOcean, vale botar Cloudflare na frente?\". A resposta curta é \"quase sempre sim\" — mas o \"quase\" carrega trade-offs que ninguém menciona até a primeira vez que algo quebra em produção e você passa duas horas debugando uma regra de cache que mascarou um 500 do app. Este post é a versão longa, com critérios mensuráveis, da decisão que você precisa tomar antes de mover o nameserver.",[19,11801,11803],{"id":11802},"tldr-o-resumo-de-200-palavras","TL;DR — o resumo de 200 palavras",[12,11805,11806],{},"Cloudflare grátis virou padrão de fato pra qualquer site brasileiro com tráfego: protege contra DDoS sem limite contratual, emite certificado SSL automático, cacha assets em mais de 300 cidades, esconde o IP de origem do servidor e ainda entrega DNS com sub-10ms. Pra cluster auto-hospedado — seja HeroCtl, Coolify, k3s ou Docker Swarm — pôr Cloudflare na frente é decisão fácil em cerca de 90% dos casos.",[12,11808,11809],{},"Os 10% restantes têm trade-offs concretos: latência adicional de 10 a 30ms em rotas dinâmicas, TLS termina em Cloudflare por padrão (não é mais ponta-a-ponta até o seu servidor), regras de cache podem mascarar bugs sutis no app, e o lock-in cresce conforme você adota Workers, R2 e Pages.",[12,11811,11812],{},"Vale a pena quando: você quer DDoS protection sem pagar; cache global pra reduzir custo de banda; esconder o IP do servidor de scanners. Não vale quando: compliance financeiro\u002Fsaúde exige TLS verdadeiramente ponta-a-ponta; você precisa de p99 abaixo de 50ms em rotas dinâmicas; o cluster já tem CDN edge interna em múltiplos data centers. Cluster com roteador integrado já cobre cerca de 60% do que Cloudflare oferece — combinar os dois é o caminho mais comum.",[19,11814,11816],{"id":11815},"o-que-cloudflare-oferece-gratis-em-2026","O que Cloudflare oferece grátis em 2026?",[12,11818,11819],{},"A oferta gratuita aumentou de tamanho ano a ano. Hoje, o plano grátis cobre o que era plano pago de 2019:",[2735,11821,11822,11828,11834,11840,11846,11852,11858,11864,11870],{},[70,11823,11824,11827],{},[27,11825,11826],{},"DDoS protection sem limite contratual"," — Layer 3, 4 e 7. Cloudflare absorve ataques de centenas de Gbps sem cobrar excedente.",[70,11829,11830,11833],{},[27,11831,11832],{},"Certificado SSL\u002FTLS automático"," — emitido em minutos pelo próprio Cloudflare, renovado automaticamente. Wildcard requer plano Advanced Certificate Manager (US$ 10\u002Fmês).",[70,11835,11836,11839],{},[27,11837,11838],{},"CDN global"," — mais de 300 cidades em mais de 120 países. Inclui São Paulo, Rio, Fortaleza, Curitiba e Porto Alegre.",[70,11841,11842,11845],{},[27,11843,11844],{},"DNS authoritative"," — sub-10ms global médio, anycast, com APIs para automação.",[70,11847,11848,11851],{},[27,11849,11850],{},"Bot protection básica"," — bloqueio de bots conhecidos e desafios JavaScript em tráfego suspeito.",[70,11853,11854,11857],{},[27,11855,11856],{},"Cache de assets estáticos"," — extensões reconhecidas (CSS, JS, imagens, fontes) cacheadas por padrão.",[70,11859,11860,11863],{},[27,11861,11862],{},"Page Rules"," — três regras grátis para forçar HTTPS, cache extra, redirects.",[70,11865,11866,11869],{},[27,11867,11868],{},"Always Online"," — quando a origem cai, Cloudflare serve a última versão cacheada.",[70,11871,11872,11875],{},[27,11873,11874],{},"Web Analytics"," — métricas de RUM (visitas, países, navegadores), sem cookies.",[12,11877,11878],{},"A linha de corte é generosa o suficiente pra que um site de 10 mil visitantes\u002Fdia rode 100% no grátis sem nenhum problema operacional.",[19,11880,11882],{"id":11881},"e-o-que-cloudflare-cobra-extra","E o que Cloudflare cobra extra?",[12,11884,11885],{},"Quatro planos: Free, Pro (US$ 25\u002Fmês por domínio), Business (US$ 250\u002Fmês por domínio) e Enterprise (sob consulta, em geral acima de US$ 5 mil\u002Fmês).",[119,11887,11888,11905],{},[122,11889,11890],{},[125,11891,11892,11895,11897,11900,11903],{},[128,11893,11894],{},"Recurso",[128,11896,7831],{},[128,11898,11899],{},"Pro US$ 25",[128,11901,11902],{},"Business US$ 250",[128,11904,4359],{},[141,11906,11907,11923,11937,11950,11964,11979,11992,12009],{},[125,11908,11909,11912,11914,11917,11920],{},[146,11910,11911],{},"WAF managed rulesets",[146,11913,3059],{},[146,11915,11916],{},"Sim (OWASP básico)",[146,11918,11919],{},"Sim (avançado)",[146,11921,11922],{},"Custom",[125,11924,11925,11928,11930,11933,11935],{},[146,11926,11927],{},"Image Resizing",[146,11929,3059],{},[146,11931,11932],{},"Sim (US$ 5\u002FM)",[146,11934,3065],{},[146,11936,3065],{},[125,11938,11939,11942,11944,11946,11948],{},[146,11940,11941],{},"Polish (otimização de imagem)",[146,11943,3059],{},[146,11945,3065],{},[146,11947,3065],{},[146,11949,3065],{},[125,11951,11952,11955,11957,11960,11962],{},[146,11953,11954],{},"Argo Smart Routing",[146,11956,3059],{},[146,11958,11959],{},"US$ 5\u002Fmês add-on",[146,11961,3065],{},[146,11963,3065],{},[125,11965,11966,11969,11971,11974,11976],{},[146,11967,11968],{},"Page Rules incluídas",[146,11970,2699],{},[146,11972,11973],{},"20",[146,11975,5651],{},[146,11977,11978],{},"125+",[125,11980,11981,11984,11986,11988,11990],{},[146,11982,11983],{},"Cache Reserve",[146,11985,3059],{},[146,11987,3059],{},[146,11989,3065],{},[146,11991,3065],{},[125,11993,11994,11997,12000,12003,12006],{},[146,11995,11996],{},"Customer Support SLA",[146,11998,11999],{},"Best-effort",[146,12001,12002],{},"24h",[146,12004,12005],{},"Chat 24\u002F7",[146,12007,12008],{},"Engenheiro dedicado",[125,12010,12011,12014,12017,12020,12023],{},[146,12012,12013],{},"Análise de logs",[146,12015,12016],{},"Última hora",[146,12018,12019],{},"Últimas 24h",[146,12021,12022],{},"Últimos 7 dias",[146,12024,12025],{},"30 dias",[12,12027,12028],{},"Workers e R2 têm tier gratuito independente do plano: 100 mil requisições\u002Fdia para Workers, 10 GB de armazenamento e 1 milhão de operações Class A\u002Fmês para R2. Para um site marketing modesto, dá pra rodar storage de imagens no R2 sem nunca chegar à fatura.",[19,12030,12032],{"id":12031},"cloudflare-adiciona-latencia","Cloudflare adiciona latência?",[12,12034,12035],{},"A pergunta honesta. Resposta também honesta: depende da rota.",[12,12037,12038,12041],{},[27,12039,12040],{},"Para rotas com cache"," (HTML estático, assets, imagens otimizadas), Cloudflare reduz latência. O usuário em Recife pega o conteúdo do POP de Fortaleza ou São Paulo em 15 a 40ms, em vez de fazer round-trip até seu servidor em New Jersey ou Frankfurt. Economia típica: 150 a 250ms por request.",[12,12043,12044,12047],{},[27,12045,12046],{},"Para rotas dinâmicas"," (API, dashboard logado, checkout), o tráfego passa pelo proxy do Cloudflare antes de chegar ao seu servidor. Isso adiciona entre 10 e 30ms em condições normais. O número exato depende de qual POP o usuário está conectado e onde está o servidor de origem.",[12,12049,12050,12051,12054],{},"Mensuramos no cluster público em produção: a média de tempo de resposta do ",[231,12052,12053],{},"manage.heroctl.com\u002Fv1\u002Fnodes"," é de 38ms sem proxy Cloudflare e 51ms com proxy ativado, requisitando do mesmo notebook em São Paulo. Um delta de 13ms — perceptível em benchmark, invisível para humano.",[12,12056,12057],{},"A latência só é dealbreaker em três cenários reais: jogos online, leilão financeiro de alta frequência, e cargas WebSocket de baixa latência (trading, colaboração ao vivo). Pro resto, os 13ms somem no tempo de render do navegador.",[19,12059,12061],{"id":12060},"cloudflare-quebra-tls-ponta-a-ponta","Cloudflare quebra TLS ponta-a-ponta?",[12,12063,12064],{},"Por padrão, sim. Veja os modos:",[2735,12066,12067,12073,12079,12085],{},[70,12068,12069,12072],{},[27,12070,12071],{},"Flexible"," (NUNCA use isso) — TLS só entre cliente e Cloudflare. Conexão Cloudflare → servidor é HTTP puro. Vulnerável a interceptação na perna interna.",[70,12074,12075,12078],{},[27,12076,12077],{},"Full"," — TLS entre cliente e Cloudflare, e separadamente entre Cloudflare e servidor. Mas Cloudflare aceita certificado inválido\u002Fauto-assinado no servidor. Risco de man-in-the-middle entre Cloudflare e a origem.",[70,12080,12081,12084],{},[27,12082,12083],{},"Full (strict)"," — TLS em ambas as pernas, e Cloudflare exige certificado válido na origem. Esta é a configuração mínima razoável.",[70,12086,12087,12090],{},[27,12088,12089],{},"Strict (SSL-Only Origin Pull)"," — Cloudflare verifica que o certificado da origem foi emitido por uma CA pública e válida pro hostname. Mais seguro que Full strict.",[12,12092,12093,12094,12097],{},"Em todos esses modos, ",[27,12095,12096],{},"Cloudflare descriptografa o tráfego no meio do caminho",". Eles veem corpo de request, headers, cookies — tudo. Para a maioria dos casos isso é aceitável (o contrato com Cloudflare é claro), mas em compliance estrito (saúde, financeiro, governo) pode quebrar requisito de auditoria.",[12,12099,12100],{},"A saída real para ponta-a-ponta:",[2735,12102,12103,12109,12115],{},[70,12104,12105,12108],{},[27,12106,12107],{},"Authenticated Origin Pulls"," — Cloudflare apresenta um certificado cliente quando conecta na sua origem; o servidor só aceita conexões dessa cadeia. Continua descriptografando no meio, mas pelo menos só Cloudflare consegue chegar na sua origem.",[70,12110,12111,12114],{},[27,12112,12113],{},"Cloudflare Tunnel + cliente mTLS na ponta"," — para apps internas, Tunnel substitui IP público e exige certificado de cliente.",[70,12116,12117,12120],{},[27,12118,12119],{},"Gray cloud (DNS only)"," — desativa o proxy. Você perde DDoS protection, cache, WAF — mas ganha conexão direta cliente-servidor com TLS verdadeiramente ponta-a-ponta. É uma opção válida quando compliance manda.",[19,12122,12124],{"id":12123},"vou-ficar-lock-in-com-cloudflare","Vou ficar lock-in com Cloudflare?",[12,12126,12127],{},"Depende exclusivamente de quais features você adota. Vamos por camada:",[2735,12129,12130,12136,12142,12148,12154,12160],{},[70,12131,12132,12135],{},[27,12133,12134],{},"DNS"," — trivialmente reversível. Mover nameserver leva 24 a 48h de propagação e nada quebra. Lock-in zero.",[70,12137,12138,12141],{},[27,12139,12140],{},"Proxy + cache + WAF"," — reversível em horas. Você desativa o orange cloud, ajusta DNS para apontar direto pro servidor, reconfigura WAF na sua origem (se houver). Lock-in baixo.",[70,12143,12144,12147],{},[27,12145,12146],{},"Workers"," — lock-in real. A API de Workers é proprietária; reescrever pra Lambda@Edge ou Fastly Compute@Edge custa de dias a semanas dependendo do código. Não é o pior caso, mas conte com retrabalho.",[70,12149,12150,12153],{},[27,12151,12152],{},"R2 Object Storage"," — API compatível com S3, então código continua funcionando. Mas R2 não cobra egress (S3 cobra US$ 0,09\u002FGB), então mover pra outro provedor encarece a conta. Lock-in econômico, não técnico.",[70,12155,12156,12159],{},[27,12157,12158],{},"Pages"," — lock-in moderado. Build process é custom; rewrite pra Vercel\u002FNetlify\u002Fstatic host genérico leva uma tarde, mas exige.",[70,12161,12162,12165],{},[27,12163,12164],{},"Zero Trust"," — lock-in alto. Políticas, identidade, túneis: rewrite completo pra Tailscale\u002FTwingate\u002Fequivalente.",[12,12167,12168],{},"A recomendação operacional é: use o core Cloudflare (DNS + proxy + WAF + Page Rules) sem hesitar — você pode reverter num dia. Adote Workers\u002FR2\u002FPages só com consciência clara de que está aceitando lock-in proporcional ao valor que aquela feature entrega.",[19,12170,12172],{"id":12171},"configuracao-minima-recomendada-para-cluster-auto-hospedado","Configuração mínima recomendada para cluster auto-hospedado",[12,12174,12175],{},"Sequência prática, sem segredo:",[67,12177,12178,12184,12193,12203,12209,12215,12221,12231,12237,12243],{},[70,12179,12180,12183],{},[27,12181,12182],{},"Crie conta no Cloudflare"," e adicione o domínio. O site vai escanear seus DNS records atuais e copiar para a nova zona.",[70,12185,12186,12189,12190,101],{},[27,12187,12188],{},"Mude os nameservers"," no registrar (Hostinger, Registro.br, GoDaddy, onde quer que esteja). Espera de 4 a 48 horas pela propagação. Verifique com ",[231,12191,12192],{},"dig NS heroctl.com +short",[70,12194,12195,12198,12199,12202],{},[27,12196,12197],{},"DNS records do cluster",": crie um registro A para o domínio raiz apontando pro IP do servidor que recebe tráfego, e um registro A wildcard ",[231,12200,12201],{},"*"," apontando pro mesmo IP. Marque ambos com proxy ativado (orange cloud).",[70,12204,12205,12208],{},[27,12206,12207],{},"SSL\u002FTLS mode",": configure Full (strict). Isso exige que o cluster tenha um certificado válido. O roteador integrado do HeroCtl emite Let's Encrypt automaticamente — funciona out-of-the-box.",[70,12210,12211,12214],{},[27,12212,12213],{},"Always Use HTTPS",": ON. Redireciona qualquer HTTP para HTTPS na borda.",[70,12216,12217,12220],{},[27,12218,12219],{},"HSTS",": 6 meses, include subdomains, no preload por enquanto. Preload é decisão definitiva — não dá pra desfazer rápido se algo quebrar.",[70,12222,12223,12226,12227,12230],{},[27,12224,12225],{},"Page Rule de cache"," para assets estáticos: ",[231,12228,12229],{},"*heroctl.com\u002Fstatic\u002F*"," → Cache Level: Cache Everything, Edge Cache TTL: 1 mês.",[70,12232,12233,12236],{},[27,12234,12235],{},"WAF managed ruleset"," (Pro+): ative o Cloudflare Managed Ruleset e o OWASP Core Rule Set em modo Block para regras de score alto.",[70,12238,12239,12242],{},[27,12240,12241],{},"Security Level",": Medium. Low deixa passar bot demais; High desafia gente legítima.",[70,12244,12245,12248],{},[27,12246,12247],{},"Bot Fight Mode",": ON no plano grátis. Controla scrapers básicos sem pedir CAPTCHA pro humano.",[12,12250,12251,12252,12255,12256,12259,12260,12263,12264,12267],{},"Depois de aplicar tudo isso, rode ",[231,12253,12254],{},"curl -I https:\u002F\u002Fseudominio.com"," e confirme: header ",[231,12257,12258],{},"cf-ray"," presente, header ",[231,12261,12262],{},"server: cloudflare",", header ",[231,12265,12266],{},"strict-transport-security"," com max-age longo.",[19,12269,12271],{"id":12270},"quando-nao-vale-cloudflare","Quando NÃO vale Cloudflare?",[12,12273,12274],{},"Quatro cenários onde a recomendação muda. Importam mais que parecem.",[12,12276,12277,12280],{},[27,12278,12279],{},"Cluster com CDN\u002Fedge interna robusta."," Se você já roda em quatro ou cinco regiões geograficamente espalhadas, com balanceamento DNS por proximidade e cache local em cada região, a CDN do Cloudflare adiciona latência sem ganho. Vale rodar gray cloud (só DNS) e manter o resto direto.",[12,12282,12283,12286],{},[27,12284,12285],{},"Compliance financeiro ou de saúde com mTLS ponta-a-ponta obrigatório."," A LGPD por si só não exige isso; mas auditorias específicas (PCI-DSS Level 1 com requisitos custom, certificações HIPAA estritas, frameworks bancários) podem exigir que tráfego cifrado nunca seja descriptografado em terceiro. Como Cloudflare descriptografa no meio do caminho, não passa.",[12,12288,12289,12292],{},[27,12290,12291],{},"Apps puramente internas (intranet\u002FSaaS B2B fechado)."," Cloudflare Free não cobre Zero Trust avançado. Pra app que serve exclusivamente funcionários, Tailscale ou WireGuard nativo entregam mais com menos.",[12,12294,12295,12298],{},[27,12296,12297],{},"Sites pequenos sem tráfego e sem inimigo público."," Blog pessoal de 200 visitas\u002Fmês, sem formulário de pagamento, sem dados sensíveis. DNS direto na Hostinger\u002FRegistro.br + Let's Encrypt do roteador integrado serve perfeitamente. Adicionar Cloudflare é cerimônia desnecessária.",[19,12300,12302],{"id":12301},"como-cloudflare-interage-com-cluster-de-alta-disponibilidade","Como Cloudflare interage com cluster de alta disponibilidade?",[12,12304,12305],{},"Aqui o desenho importa. Cluster com três ou mais nós serve tráfego em todos eles — não tem nó \"principal\" único. A configuração pragmática é:",[2735,12307,12308,12314,12320],{},[70,12309,12310,12313],{},[27,12311,12312],{},"DNS round-robin com saúde",": registre A records pro IP de todos os nós que rodam o roteador. Cloudflare faz health check (Pro+) e remove nó quebrado da rotação automaticamente.",[70,12315,12316,12319],{},[27,12317,12318],{},"Failover de Cloudflare",": ~30 segundos pra detectar nó morto e tirar da rotação (configurável até 5 segundos no Enterprise).",[70,12321,12322,12325],{},[27,12323,12324],{},"Failover interno do cluster",": o roteador integrado do HeroCtl reroteia tráfego entre nós saudáveis em cerca de 5 segundos. Eleição de novo coordenador acontece em ~7 segundos quando o nó-líder cai.",[12,12327,12328],{},"Combinado, downtime real percebido pelo usuário fica abaixo de 40 segundos no pior caso (Cloudflare detecta + cluster reage). Sem Cloudflare, fica em ~7 segundos (cluster sozinho). Com Cloudflare e configuração de monitoramento agressiva (Pro+), volta pra ~10 segundos. A escolha é clara: se você não precisa de DDoS protection, o cluster sozinho já é mais rápido. Se precisa, Cloudflare adiciona 30s de detecção em troca de proteção contra ataque.",[19,12330,12332],{"id":12331},"tabela-comparativa-12-criterios-de-decisao","Tabela comparativa: 12 critérios de decisão",[119,12334,12335,12353],{},[122,12336,12337],{},[125,12338,12339,12341,12344,12347,12350],{},[128,12340,2983],{},[128,12342,12343],{},"Sem Cloudflare",[128,12345,12346],{},"CF Free",[128,12348,12349],{},"CF Pro US$ 25",[128,12351,12352],{},"CF Business US$ 250",[141,12354,12355,12371,12388,12405,12421,12434,12449,12464,12479,12491,12503,12519],{},[125,12356,12357,12360,12363,12366,12368],{},[146,12358,12359],{},"DDoS Layer 3\u002F4",[146,12361,12362],{},"Você se vira",[146,12364,12365],{},"Ilimitado",[146,12367,12365],{},[146,12369,12370],{},"Ilimitado + SLA",[125,12372,12373,12376,12379,12382,12385],{},[146,12374,12375],{},"DDoS Layer 7",[146,12377,12378],{},"Não tem",[146,12380,12381],{},"Básico",[146,12383,12384],{},"Avançado",[146,12386,12387],{},"Avançado + Custom Rules",[125,12389,12390,12393,12396,12399,12402],{},[146,12391,12392],{},"Latência adicional em rotas dinâmicas",[146,12394,12395],{},"0ms",[146,12397,12398],{},"+13 a 30ms",[146,12400,12401],{},"+10 a 25ms (Argo opcional)",[146,12403,12404],{},"+5 a 15ms (Argo incluído)",[125,12406,12407,12410,12413,12416,12418],{},[146,12408,12409],{},"Cache global de estáticos",[146,12411,12412],{},"Você monta",[146,12414,12415],{},"300+ cidades",[146,12417,12415],{},[146,12419,12420],{},"300+ cidades + Reserve",[125,12422,12423,12426,12428,12430,12432],{},[146,12424,12425],{},"Esconde IP do servidor",[146,12427,3059],{},[146,12429,3065],{},[146,12431,3065],{},[146,12433,3065],{},[125,12435,12436,12439,12441,12444,12446],{},[146,12437,12438],{},"TLS ponta-a-ponta verdadeiro",[146,12440,3065],{},[146,12442,12443],{},"Não (descriptografa)",[146,12445,3059],{},[146,12447,12448],{},"Não (mas Origin Pulls)",[125,12450,12451,12454,12456,12458,12461],{},[146,12452,12453],{},"WAF managed",[146,12455,12378],{},[146,12457,3059],{},[146,12459,12460],{},"OWASP básico",[146,12462,12463],{},"OWASP avançado",[125,12465,12466,12469,12471,12473,12476],{},[146,12467,12468],{},"Bot protection",[146,12470,12412],{},[146,12472,12247],{},[146,12474,12475],{},"Super Bot Fight",[146,12477,12478],{},"Bot Management ML",[125,12480,12481,12483,12485,12487,12489],{},[146,12482,11862],{},[146,12484,3056],{},[146,12486,2699],{},[146,12488,11973],{},[146,12490,5651],{},[125,12492,12493,12495,12497,12499,12501],{},[146,12494,11868],{},[146,12496,3059],{},[146,12498,3065],{},[146,12500,3065],{},[146,12502,3065],{},[125,12504,12505,12508,12511,12513,12516],{},[146,12506,12507],{},"Custo mensal por domínio",[146,12509,12510],{},"US$ 0",[146,12512,12510],{},[146,12514,12515],{},"US$ 25",[146,12517,12518],{},"US$ 250",[125,12520,12521,12524,12527,12530,12533],{},[146,12522,12523],{},"Lock-in proporcional",[146,12525,12526],{},"Zero",[146,12528,12529],{},"Baixo (DNS+proxy)",[146,12531,12532],{},"Baixo a médio",[146,12534,12535],{},"Médio (Workers\u002FR2 começam a entrar)",[12,12537,12538],{},"A linha que decide pra maioria é \"DDoS Layer 7 + esconde IP\". Esses dois sozinhos justificam o plano grátis. As linhas pagas só fazem sentido com tráfego volumoso ou requisito formal de WAF.",[19,12540,12542],{"id":12541},"cloudflare-gratis-tem-limite-de-trafego","Cloudflare grátis tem limite de tráfego?",[12,12544,12545],{},"Não há limite contratual de banda no plano grátis para tráfego web normal através do proxy. Mas existem três limites práticos que valem mencionar:",[2735,12547,12548,12554,12560],{},[70,12549,12550,12553],{},[27,12551,12552],{},"Section 2.8 dos Terms of Service",": o plano grátis é para sites cujo conteúdo principal é HTML, e Cloudflare reserva o direito de pedir upgrade se você usa o serviço primariamente para servir vídeo ou arquivos grandes. Na prática, raramente acionam — mas se você vira um host de vídeos pirateados de 50TB\u002Fmês, espera receber e-mail.",[70,12555,12556,12559],{},[27,12557,12558],{},"Workers grátis",": 100 mil requisições\u002Fdia. Acima disso, Workers Paid (US$ 5\u002Fmês) com 10M requisições incluídas.",[70,12561,12562,12565],{},[27,12563,12564],{},"R2 grátis",": 10GB de armazenamento, 1M Class A operations\u002Fmês, 10M Class B operations\u002Fmês. Acima, US$ 0,015\u002FGB-mês.",[19,12567,12569],{"id":12568},"posso-usar-cloudflare-dns-sem-o-proxy","Posso usar Cloudflare DNS sem o proxy?",[12,12571,12572],{},"Sim — modo \"DNS only\" (gray cloud). Você usa o DNS do Cloudflare (rápido, free, anycast global) mas tráfego vai direto pro seu servidor sem passar pelo proxy. Perde DDoS, cache, WAF, IP hiding — mantém só a infraestrutura DNS. Útil quando: compliance proíbe descriptografia em terceiro; você só quer DNS rápido sem mexer no path do tráfego; você está testando antes de ativar o proxy.",[19,12574,12576],{"id":12575},"waf-gratis-bloqueia-sql-injection","WAF grátis bloqueia SQL injection?",[12,12578,12579,12580,12582],{},"Cloudflare Free tem ",[27,12581,12247],{}," e regras automáticas de mitigação para padrões óbvios, mas não tem o Managed Ruleset OWASP completo. Para bloqueio confiável de SQL injection, XSS, RCE patterns conhecidos, você precisa do plano Pro ou superior. Alternativa: rodar ModSecurity ou WAF próprio na sua origem — funciona, mas adiciona CPU e configuração.",[19,12584,12586],{"id":12585},"cloudflare-tem-datacenter-no-brasil","Cloudflare tem datacenter no Brasil?",[12,12588,12589],{},"Sim. Em 2026 são cinco POPs brasileiros: São Paulo (dois POPs), Rio de Janeiro, Fortaleza, Curitiba e Porto Alegre. Latência típica de qualquer cidade do Sudeste para um POP fica abaixo de 20ms. O POP de Fortaleza atende muito bem o Nordeste por causa dos cabos submarinos que aterrissam ali (EllaLink, Monet, GlobeNet). Para o Norte, ainda é caminho mais longo — Manaus chega a Fortaleza em 80 a 120ms.",[19,12591,12593],{"id":12592},"como-migrar-nameservers-da-hostinger-pra-cloudflare","Como migrar nameservers da Hostinger pra Cloudflare?",[12,12595,12596],{},"Quatro passos. Demora menos de uma hora ativa, mais até 48h de propagação:",[67,12598,12599,12605,12617,12623],{},[70,12600,12601,12604],{},[27,12602,12603],{},"Cloudflare",": adicione o domínio. O wizard escaneia seus DNS atuais e cria os records correspondentes na nova zona. Confira que copiou tudo — MX, TXT (SPF\u002FDKIM\u002FDMARC), CNAME, A. Erros de cópia aqui causam e-mail derrubado por uma semana.",[70,12606,12607,12609,12610,2403,12613,12616],{},[27,12608,12603],{},": ele te dá dois nameservers (algo como ",[231,12611,12612],{},"kim.ns.cloudflare.com",[231,12614,12615],{},"walt.ns.cloudflare.com","). Anote.",[70,12618,12619,12622],{},[27,12620,12621],{},"Hostinger",": painel → Domínios → seu domínio → Nameservers → \"Use custom nameservers\" → cole os dois do Cloudflare. Salve.",[70,12624,12625,12628,12629,12632],{},[27,12626,12627],{},"Aguarde propagação",". Verifique com ",[231,12630,12631],{},"dig NS seudominio.com +short",". Quando aparecerem os nameservers do Cloudflare, o domínio está sob gestão deles. Records DNS continuam sendo editados no painel do Cloudflare daí pra frente.",[12,12634,12635],{},"Importante: enquanto a propagação acontece, parte dos usuários ainda resolve via Hostinger. Não desligue a zona velha até confirmar que 100% dos resolvers já trocaram (24 a 48 horas é seguro).",[19,12637,12639],{"id":12638},"tls-termina-onde-e2e-quebra","TLS termina onde? E2E quebra?",[12,12641,12642],{},"Em modo proxy (orange cloud), TLS termina em Cloudflare. Eles re-estabelecem outra conexão TLS pro seu servidor (em modo Full strict). Tecnicamente: descriptografa, processa, recriptografa. Para ponta-a-ponta verdadeiro: gray cloud (DNS only) ou Cloudflare Tunnel com configuração custom. Para a maioria das aplicações, \"TLS verdadeiramente ponta-a-ponta\" é menos importante do que parece — o ataque que isso protege (interceptação no meio da rede) requer atacante já dentro da rede do Cloudflare, cenário pouco realista.",[19,12644,12646],{"id":12645},"cloudflare-workers-vs-serverless-do-meu-cloud-quando-vale","Cloudflare Workers vs serverless do meu cloud — quando vale?",[12,12648,12649],{},"Workers são bons para: edge computing onde latência \u003C50ms importa (geo-routing, A\u002FB testing, rewrite de header); transformação leve de request\u002Fresponse; auth na borda (validar JWT antes de chegar na origem). Não são bons para: workload com mais de 30 segundos de runtime; integração heavy com bancos relacionais (latência de cold start de DB driver mata); código que precisa de bibliotecas que dependem de filesystem ou processo. Lambda da AWS continua melhor pra workload de runtime longo; Workers ganham na borda. Use ambos, não substitua um pelo outro.",[19,12651,12653],{"id":12652},"posso-usar-cloudflare-r2-com-cluster-auto-hospedado","Posso usar Cloudflare R2 com cluster auto-hospedado?",[12,12655,12656,12657,12660],{},"Sim — R2 é S3-compatível na API. Seu app usa ",[231,12658,12659],{},"aws-sdk"," configurado com endpoint de R2 e credenciais R2; código continua igual. Vantagem econômica: zero egress fee. Você pode servir downloads pesados (instaladores, vídeos de produto, backups) direto do R2 sem pagar por banda saindo. Desvantagem: durabilidade documentada é 99.999999999% (11 noves), mesma do S3, mas histórico operacional do R2 é mais curto. Para hot path crítico, alguns times preferem manter S3 e usar R2 só para cold storage e static delivery.",[19,12662,12664],{"id":12663},"origem-caiu-always-online-resolve","Origem caiu — Always Online resolve?",[12,12666,12667],{},"Em parte. Always Online serve a última versão cacheada de páginas HTML quando o servidor está fora. Mas: só funciona pra rotas que estavam sendo cacheadas; só serve a versão estática (sem dados dinâmicos atualizados); só dura enquanto Cloudflare mantém o snapshot (geralmente alguns dias). É uma rede de segurança boa pra blog estático e marketing. Não substitui alta disponibilidade real do cluster — pra app dinâmico, o que resolve é o cluster ter três nós e eleição automática quando um cai.",[19,12669,12671],{"id":12670},"fechando-combinando-cloudflare-com-cluster-auto-hospedado","Fechando — combinando Cloudflare com cluster auto-hospedado",[12,12673,12674],{},"A combinação que recomendamos para 90% dos casos é: cluster auto-hospedado com três ou mais nós (alta disponibilidade real) + Cloudflare Free na borda (DDoS, cache, IP hiding). O cluster cuida de roteamento interno, certificados automáticos, failover entre nós em segundos. Cloudflare cuida de proteção pública, cache global e ofuscação de IP. As duas camadas se complementam — não competem.",[12,12676,12677],{},"Para começar do zero com essa combinação:",[224,12679,12680],{"className":226,"code":5319,"language":228,"meta":229,"style":229},[231,12681,12682],{"__ignoreMap":229},[234,12683,12684,12686,12688,12690,12692],{"class":236,"line":237},[234,12685,1220],{"class":247},[234,12687,2958],{"class":251},[234,12689,5330],{"class":255},[234,12691,2964],{"class":383},[234,12693,2967],{"class":247},[12,12695,12696],{},"Você fica com cluster funcional em três nós, certificado Let's Encrypt automático no domínio que escolher, painel web pra submeter jobs, alta disponibilidade real. Depois, adiciona Cloudflare Free na frente do domínio e configura conforme a seção \"Configuração mínima\" deste post. Total de tempo: uma tarde.",[12,12698,12699],{},"Mais leitura nesta linha:",[2735,12701,12702,12711],{},[70,12703,12704,12706,12707,12710],{},[3337,12705,3345],{"href":3344}," — como sair do ",[231,12708,12709],{},"docker compose up"," e chegar em alta disponibilidade real, com os passos intermediários.",[70,12712,12713,12716],{},[3337,12714,12715],{"href":11727},"Observabilidade sem Datadog: stack para startup"," — métricas, logs e tracing sem pagar US$ 2.000\u002Fmês de SaaS de observabilidade.",[12,12718,12719],{},"Cloudflare é uma das poucas ferramentas onde o tier grátis é tão bom que recusar é teimosia. Mas como toda escolha de infra, a parte difícil é entender exatamente onde a fronteira está — e, principalmente, onde ela passa por dentro do tráfego cifrado da sua aplicação.",[3351,12721,4376],{},{"title":229,"searchDepth":244,"depth":244,"links":12723},[12724,12725,12726,12727,12728,12729,12730,12731,12732,12733,12734,12735,12736,12737,12738,12739,12740,12741,12742,12743],{"id":11802,"depth":244,"text":11803},{"id":11815,"depth":244,"text":11816},{"id":11881,"depth":244,"text":11882},{"id":12031,"depth":244,"text":12032},{"id":12060,"depth":244,"text":12061},{"id":12123,"depth":244,"text":12124},{"id":12171,"depth":244,"text":12172},{"id":12270,"depth":244,"text":12271},{"id":12301,"depth":244,"text":12302},{"id":12331,"depth":244,"text":12332},{"id":12541,"depth":244,"text":12542},{"id":12568,"depth":244,"text":12569},{"id":12575,"depth":244,"text":12576},{"id":12585,"depth":244,"text":12586},{"id":12592,"depth":244,"text":12593},{"id":12638,"depth":244,"text":12639},{"id":12645,"depth":244,"text":12646},{"id":12652,"depth":244,"text":12653},{"id":12663,"depth":244,"text":12664},{"id":12670,"depth":244,"text":12671},"2026-05-08","Cloudflare grátis bloqueia DDoS, cacha estático e esconde IP do servidor. Mas adiciona latência, lock-in e features que talvez você não use. Quando vale e quando é overkill.",{},"\u002Fblog\u002Fcloudflare-na-frente-cluster-auto-hospedado-vale-pena",{"title":11794,"description":12745},{"loc":12747},"blog\u002Fcloudflare-na-frente-cluster-auto-hospedado-vale-pena",[12752,12753,12754,12755,3379],"cloudflare","cdn","ddos","performance","qGST7MJgEsJSV0MJ_ynS7WUw-HKUZmOgWdsBidS2IHE",1777362183394]