Infraestrutura
Como configurar cloud-init em VPS sem trabalho manual
Aprenda como configurar cloud-init em VPS para criar usuários, instalar pacotes, aplicar chaves SSH e ativar firewall já no primeiro boot do servidor.
Resposta direta
Para configurar cloud-init em uma VPS, crie um arquivo YAML iniciado por #cloud-config e envie seu conteúdo no campo user data durante o provisionamento. Nesse arquivo, você pode criar usuários, adicionar chaves públicas SSH, instalar pacotes, gravar configurações, habilitar serviços e executar comandos no primeiro boot. Uma configuração segura costuma criar um usuário administrativo, validar o acesso pela chave SSH e só então desativar login por senha e acesso direto do root. O resultado deve ser conferido com cloud-init status --wait, enquanto falhas aparecem em /var/log/cloud-init.log e /var/log/cloud-init-output.log. O suporte e o formato do campo user data variam entre provedores e imagens, então a imagem escolhida precisa trazer cloud-init instalado e uma fonte de dados compatível.
Resumo rápido
- O arquivo deve começar exatamente com
#cloud-confige usar YAML válido. - Chaves públicas SSH podem ser associadas ao usuário criado no primeiro boot.
- Pacotes, arquivos e serviços devem ser definidos de maneira declarativa sempre que possível.
runcmdnormalmente executa uma vez por instância, na etapa final do cloud-init.- Firewall precisa ser ativado depois que uma regra de acesso SSH tiver sido criada.
cloud-init schemadetecta vários erros antes de a VPS ser provisionada.- Logs locais e o console do provedor são essenciais quando o acesso remoto falha.
Como o cloud-init funciona durante o provisionamento
Cloud-init é um sistema de inicialização usado por imagens de nuvem para receber metadados e configurações da plataforma. Quando uma VPS compatível inicia pela primeira vez, o cloud-init identifica a fonte de dados disponível, lê informações como hostname, rede, chave SSH e user data, depois executa os módulos habilitados pela distribuição. Esse comportamento depende da imagem. Uma instalação mínima criada manualmente pode não incluir o pacote, mesmo que o painel do provedor aceite user data.
Estágios de inicialização
O processo é dividido, de forma simplificada, em estágios local, network, config e final. O estágio local trata informações disponíveis antes da rede. O estágio de rede obtém a fonte de dados e aplica configurações que dependem de conectividade. Nos estágios seguintes entram módulos como criação de usuários, instalação de pacotes, gravação de arquivos e execução de comandos finais.
Essa ordem explica um problema comum: tentar iniciar uma aplicação antes de o pacote correspondente existir. Outro exemplo aparece quando um comando usa DNS ou um repositório externo antes de a rede estar pronta. Para ações finais, como executar systemctl enable --now nginx, runcmd costuma ser mais adequado do que bootcmd. O módulo bootcmd pode executar em cada inicialização, dependendo da frequência configurada, enquanto scripts classificados como per-instance são controlados pelo estado salvo em /var/lib/cloud/.
Cloud-config, scripts e imagens prontas
Há três abordagens práticas para configurar uma instância:
| Abordagem | Melhor uso | Repetibilidade | Tempo no primeiro boot | Risco operacional |
|---|---|---|---|---|
| Cloud-config em YAML | Usuários, pacotes e ajustes iniciais | Alta, se versionado | Médio | Erro de YAML ou indisponibilidade de repositório |
| Script shell em user data | Bootstrap curto ou software sem módulo próprio | Média | Médio ou alto | Comandos parcialmente executados |
| Imagem previamente preparada | Frotas frequentes e dependências grandes | Alta, com pipeline de imagem | Baixo | Imagem desatualizada ou sem rastreabilidade |
Uma VPS de laboratório pode receber todo o bootstrap por cloud-config. Para cinquenta instâncias de uma aplicação com uma imagem de contêiner grande, preparar uma imagem base reduz downloads repetidos. Em ambos os casos, o cloud-init continua útil para inserir hostname, usuário, chave pública e parâmetros específicos daquela instância. Esse modelo também combina bem com Terraform e infraestrutura como código, pois o mesmo template pode ser aplicado a ambientes reproduzíveis.
Como criar usuários e instalar chaves SSH
O primeiro objetivo do bootstrap deve ser garantir um caminho de administração que possa ser testado. Em vez de depender do root, crie um usuário nominal ou de deploy, associe uma chave pública e conceda apenas os privilégios necessários. A chave privada nunca deve entrar no user data, no repositório Git ou no estado do Terraform. O servidor recebe somente a parte pública.
Arquivo cloud-config inicial
O exemplo abaixo cria o usuário deploy, adiciona-o ao grupo administrativo e bloqueia autenticação por senha. A chave mostrada é deliberadamente ilustrativa e não funciona como credencial real. Substitua a linha completa por uma chave pública gerada com ssh-keygen -t ed25519.
#cloud-config
hostname: app-01
manage_etc_hosts: true
users:
- default
- name: deploy
gecos: Deploy User
groups:
- sudo
shell: /bin/bash
sudo: 'ALL=(ALL) NOPASSWD:ALL'
lock_passwd: true
ssh_authorized_keys:
- 'ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIExemploNaoUtilizavel cloud-init-demo'
ssh_pwauth: false
disable_root: true
A entrada default preserva o usuário padrão definido pela imagem, como ubuntu, debian ou outro nome escolhido pela distribuição. Removê-la pode ser desejável em uma política mais rígida, mas isso deve ser testado em uma instância descartável. O grupo administrativo também varia. Debian e Ubuntu normalmente usam sudo, enquanto imagens da família RHEL costumam usar wheel.
Cuidados antes de desativar senhas
Aplique ssh_pwauth: false somente quando houver certeza de que a chave foi aceita pela imagem e pelo módulo SSH. Um recuo incorreto no YAML pode fazer ssh_authorized_keys ficar fora do usuário esperado. Se a autenticação por senha já estiver desativada e o provedor não oferecer console serial ou console web, a instância poderá ficar inacessível.
Um fluxo seguro cria duas VPS pequenas e temporárias. Na primeira, mantenha o usuário padrão e confirme ssh deploy@IP_DA_VPS. Na segunda, remova o usuário padrão e restrinja o root. Para times, uma alternativa é distribuir chaves individuais para ana, bruno e carla, em vez de compartilhar uma única identidade deploy. A revogação fica mais simples, e os registros do sistema indicam quem iniciou cada sessão.
Evite colocar chaves de todos os funcionários diretamente em dezenas de templates. Para um ambiente com rotatividade, use cloud-init para criar o acesso inicial de uma conta de automação e delegue a gestão contínua a Ansible e automação de servidores. Assim, remover uma chave não exige recriar manualmente cada VPS.
Como instalar pacotes, arquivos e serviços
Cloud-config oferece módulos próprios para tarefas frequentes. Eles deixam a intenção mais clara do que um script shell extenso e facilitam a leitura durante uma revisão. Em uma VPS Ubuntu ou Debian destinada a uma API, por exemplo, é possível atualizar o índice, instalar Nginx, Git e fail2ban, criar uma página de verificação e iniciar os serviços no fim do processo.
Instalação declarativa de pacotes
#cloud-config
package_update: true
package_upgrade: false
packages:
- nginx
- git
- curl
- fail2ban
- unattended-upgrades
package_update: true atualiza os metadados antes da instalação. Já uma atualização completa de todos os pacotes pode prolongar o primeiro boot e reiniciar componentes, por isso package_upgrade: true precisa ser uma decisão consciente. Em uma imagem antiga, atualizar tudo pode consumir vários minutos. Em uma imagem recente e mantida pelo provedor, instalar somente as dependências reduz o tempo até a aplicação ficar disponível.
Os nomes mudam entre distribuições. nginx e git são amplamente disponíveis, mas módulos de PHP, versões do Python e nomes de agentes de monitoramento podem variar. Teste o mesmo arquivo na distribuição exata usada em produção, incluindo a versão principal, como Ubuntu 24.04 ou Debian 12.
Criação de arquivos e execução de comandos
O módulo write_files permite criar configurações com proprietário e permissões explícitas. Depois, runcmd habilita os serviços. Este exemplo publica um endpoint estático de saúde e registra a conclusão do bootstrap:
#cloud-config
write_files:
- path: /var/www/html/health.txt
owner: root:root
permissions: '0644'
content: |
status=ok
provisioned_by=cloud-init
- path: /etc/sysctl.d/99-app.conf
owner: root:root
permissions: '0644'
content: |
net.core.somaxconn=4096
runcmd:
- [sysctl, --system]
- [systemctl, enable, nginx]
- [systemctl, restart, nginx]
- [systemctl, enable, fail2ban]
- [systemctl, start, fail2ban]
- [sh, -c, 'date -Is > /var/lib/bootstrap-complete']
A sintaxe em lista evita parte dos problemas de interpretação do shell. Use sh -c apenas quando precisar de redirecionamento, pipes ou expansão. Para uma aplicação Node.js, outro caso prático seria gravar uma unidade systemd em /etc/systemd/system/api.service, executar systemctl daemon-reload e então habilitar o serviço. Segredos, tokens de banco e chaves de API não devem aparecer no YAML, pois user data pode ser armazenado no painel, na API do provedor, no estado da infraestrutura ou dentro da própria instância.
Firewall e hardening no primeiro boot
Automatizar firewall reduz diferenças entre servidores, mas a ordem das ações decide se a VPS continuará acessível. A regra de SSH deve existir antes de o firewall ser habilitado. Também é prudente conhecer o console de recuperação do provedor e manter uma sessão SSH aberta durante o primeiro teste. Uma regra errada aplicada a vinte instâncias replica o bloqueio com a mesma eficiência que replica uma configuração correta.
Ordem segura de aplicação
Em Ubuntu, um bootstrap básico com UFW pode usar os seguintes comandos. O exemplo abre SSH, HTTP e HTTPS. Em um banco privado, as portas 80 e 443 seriam removidas, e a porta do banco deveria aceitar somente a sub-rede da aplicação.
#cloud-config
packages:
- ufw
runcmd:
- [ufw, default, deny, incoming]
- [ufw, default, allow, outgoing]
- [ufw, allow, 22/tcp]
- [ufw, allow, 80/tcp]
- [ufw, allow, 443/tcp]
- [ufw, --force, enable]
- [ufw, status, verbose]
Abrir a porta 22 para toda a internet facilita o primeiro teste, mas não é a única opção. Uma equipe com IP administrativo fixo pode usar uma regra restrita ao CIDR corporativo. Outra solução é acessar a rede privada por VPN e não expor SSH publicamente. Antes de aplicar essa política, confirme que o endereço de origem realmente é estável e que existe uma rota de recuperação.
Limites do hardening automatizado
O cloud-init também pode criar /etc/ssh/sshd_config.d/99-hardening.conf com PasswordAuthentication no, PermitRootLogin no e MaxAuthTries 3. Antes de recarregar o SSH, valide a sintaxe com sshd -t. Uma sequência segura executa sshd -t e somente depois systemctl reload ssh. Se a validação falhar, o comando precisa encerrar sem substituir um daemon funcional.
Hardening não termina no primeiro boot. Patches, auditoria de chaves, rotação de credenciais, monitoramento e backup continuam necessários. Fail2ban pode reduzir tentativas repetidas contra SSH, mas não corrige uma senha fraca ou uma porta desnecessária. Da mesma forma, UFW dentro da VPS não substitui regras de firewall ou security groups fornecidos pela plataforma. As duas camadas podem coexistir, desde que a documentação registre quem controla cada porta.
Para um servidor web de 2 vCPUs e 4 GB de RAM, o custo de UFW é pequeno. Já regras numerosas, inspeção avançada e tráfego de dezenas de milhares de conexões por segundo exigem medição. Não há configuração universal. O conjunto mínimo deve refletir os serviços realmente expostos pela aplicação.
Como validar e corrigir falhas do cloud-init
A maior parte dos incidentes de cloud-init nasce antes da inicialização: YAML mal recuado, módulo desconhecido, pacote inexistente ou comando que pressupõe um arquivo ainda não criado. A validação local reduz esse ciclo de criar, esperar, falhar e destruir uma VPS. Ainda assim, ela não comprova que o repositório estará disponível ou que a imagem implementa todos os módulos da mesma maneira.
Validação antes do provisionamento
Em uma máquina com cloud-init instalado, salve o conteúdo como user-data.yaml e execute:
cloud-init schema --config-file user-data.yaml
Um analisador YAML, como yamllint, ajuda a encontrar tabulações, recuos inconsistentes e valores interpretados com tipo errado. Também é possível iniciar uma máquina virtual local baseada na mesma imagem de nuvem. Esse teste captura diferenças de nome de serviço, gerenciador de pacotes e usuário padrão que uma validação puramente sintática não encontra.
Divida configurações extensas em blocos testáveis. Primeiro crie usuário e chave. Depois acrescente pacotes. Em uma terceira execução, inclua firewall e aplicação. Esse método identifica rapidamente qual alteração quebrou o bootstrap. Em staging, mantenha a mesma base usada na produção, mas troque hostname, tamanho da máquina e credenciais externas. Um ambiente de staging e homologação reproduzível é um bom lugar para validar uma nova versão do cloud-config.
Logs, status e reexecução
Depois de iniciar a VPS, aguarde o término com:
sudo cloud-init status --wait
sudo cloud-init status --long
sudo cloud-init analyze show
O arquivo /var/log/cloud-init.log registra decisões internas, módulos e fontes de dados. Já /var/log/cloud-init-output.log concentra a saída dos comandos e instaladores. Se Nginx não subiu, verifique também systemctl status nginx e journalctl -u nginx. Se nenhum usuário foi criado, confirme se o painel realmente enviou o user data e se a imagem reconheceu a fonte de dados.
cloud-init clean --logs remove estado e permite simular uma nova inicialização, mas não transforma todos os comandos em operações seguras. Um script que acrescenta a mesma linha com echo >> produzirá duplicatas. Um comando que recria banco de dados pode destruir dados. Prefira operações idempotentes, arquivos completamente gerenciados e testes em instâncias descartáveis. Para depuração extrema, preserve snapshot ou imagem da instância falha antes de limpar o estado, pois os logs originais ajudam a explicar a sequência real do problema.
Cloud-init com Terraform, Ansible e staging
Cloud-init, Terraform e Ansible resolvem camadas diferentes. Terraform cria recursos, redes, discos e instâncias. Cloud-init prepara o sistema na primeira inicialização. Ansible mantém a configuração ao longo do tempo. Misturar todas as responsabilidades em um único arquivo de user data funciona em um protótipo, mas dificulta atualização, auditoria e recuperação quando a aplicação cresce.
Divisão de responsabilidades
Um desenho prático usa Terraform para criar uma VPS com 2 vCPUs, 4 GB de RAM, disco de 60 GB, endereço de rede e regras externas. O Terraform renderiza um template cloud-config com hostname e chave pública. O cloud-init cria o usuário da automação, instala Python, configura o firewall mínimo e registra que o bootstrap terminou. Em seguida, Ansible instala a aplicação, configura Nginx, envia certificados e ajusta monitoramento.
Essa separação reduz o tamanho do user data e evita colocar lógica de negócio em um campo que normalmente só roda uma vez. Também permite atualizar a aplicação sem recriar a VPS. Se o objetivo for uma infraestrutura imutável, a estratégia muda: uma nova imagem ou instância é criada, testada e colocada atrás do balanceador, enquanto a anterior é retirada.
Idempotência e ciclo de vida
Templates devem receber variáveis não sensíveis, como ambiente, domínio e identificador da instância. No Terraform, templatefile pode renderizar o YAML, mas mudanças no user data nem sempre são reaplicadas a uma máquina existente. Alguns recursos exigem substituição da instância; outros apenas armazenam o novo valor para o próximo boot ou não executam novamente os módulos per-instance. Confira o comportamento do provedor antes de depender dessa atualização.
Um teste realista cria três instâncias idênticas e verifica quatro condições: acesso SSH pela chave, conclusão de cloud-init status --wait, resposta HTTP do endpoint de saúde e presença do marcador /var/lib/bootstrap-complete. Se uma das três falhar, o pipeline deve interromper a promoção. Essa amostra pequena já revela erros intermitentes de repositório, DNS ou dependência externa.
Mantenha o cloud-config no controle de versão, faça revisão por pares e associe mudanças a uma versão. Um comentário como bootstrap_version: 7 em um arquivo de metadados facilita descobrir qual configuração criou cada servidor. O histórico passa a ser uma ferramenta operacional, não apenas documentação.
Recomendações por perfil
A configuração ideal depende da quantidade de servidores, do impacto de uma falha e da frequência das mudanças. Um arquivo de cinquenta linhas pode bastar para um projeto pessoal. Em produção, o mesmo arquivo precisa de validação automatizada, logs centralizados, revisão e um caminho de recuperação.
Desenvolvedor solo
Comece com uma imagem oficial recente e um cloud-config curto. Crie um usuário, instale a chave SSH, habilite UFW para as portas 22, 80 e 443 e instale no máximo os pacotes essenciais. Uma VPS de 1 a 2 vCPUs e 2 GB de RAM costuma ser suficiente para testar o processo, embora a necessidade real dependa da aplicação. Valide o YAML localmente e mantenha acesso ao console web durante o primeiro boot. Não coloque tokens, senhas ou chaves privadas no arquivo. Se precisar repetir comandos, transforme-os em arquivos gerenciados ou scripts idempotentes antes de ampliar o ambiente.
Time de desenvolvimento
Separe o template base das variáveis de cada ambiente. Use chaves individuais, revisão por pares e um pipeline que crie uma instância temporária a cada alteração relevante. O teste deve confirmar SSH, pacotes, firewall, serviços e endpoint de saúde. Centralize mudanças contínuas em Ansible ou ferramenta equivalente, deixando o cloud-init responsável pelo bootstrap mínimo. Para três ambientes, desenvolvimento, staging e produção, reutilize a mesma estrutura e altere apenas parâmetros documentados. Registre a versão do bootstrap em um arquivo local ou etiqueta da instância para facilitar auditorias e suporte.
Produção e ambientes críticos
Trate o user data como código operacional. Valide sintaxe, execute testes de integração, limite privilégios e mantenha recuperação por console. Use rede privada ou VPN para administração quando o desenho permitir, combine firewall da plataforma com firewall do sistema e monitore o término do cloud-init. Dependências grandes devem ir para uma imagem preparada e versionada, reduzindo downloads no primeiro boot. Faça backup dos dados persistentes fora do disco raiz e teste restauração. Se o serviço exige alta disponibilidade, não confie em uma única VPS corretamente provisionada: distribua instâncias, health checks e dados conforme os requisitos de continuidade do negócio.
Perguntas frequentes
O cloud-init executa em todo boot da VPS?
Nem todos os módulos executam com a mesma frequência. A maior parte das tarefas de criação de usuários, instalação de pacotes e `runcmd` é tratada como per-instance, portanto roda uma vez para aquela identificação de instância. Alguns módulos e scripts podem ser configurados para executar em todo boot. O estado fica armazenado em `/var/lib/cloud/`. Apagar esse diretório ou usar `cloud-init clean` pode provocar nova execução, mas isso deve ser testado em uma VPS descartável. Comandos não idempotentes podem duplicar arquivos, recriar recursos ou interromper aplicações existentes.
Como saber se a imagem da VPS suporta cloud-init?
Consulte a documentação da imagem e do provedor, depois confirme dentro do sistema com `cloud-init --version` e `systemctl status cloud-init`. Imagens oficiais de nuvem de Ubuntu, Debian, Rocky Linux e outras distribuições frequentemente incluem o pacote, mas uma instalação ISO manual pode não incluí-lo. O painel aceitar user data não garante que a imagem saberá processá-lo. Faça um teste simples que crie um arquivo em `/tmp` e verifique `cloud-init status --long`. Se a fonte de dados não for detectada, examine `/var/log/cloud-init.log` e os metadados disponibilizados pela plataforma.
Posso colocar senhas e tokens no user data?
Não é uma prática segura. User data pode aparecer no painel do provedor, em chamadas de API, no estado do Terraform, em logs, em backups ou nos metadados acessíveis pela própria instância. Use o cloud-init para instalar o agente ou criar a identidade inicial que buscará o segredo em um cofre apropriado. Quando uma credencial temporária for inevitável, limite sua validade e seus privilégios, faça a rotação imediatamente após o bootstrap e verifique onde ela ficou armazenada. Chaves privadas SSH, tokens permanentes e senhas de produção nunca devem ser gravados no template versionado.
Qual é a diferença entre cloud-init e Ansible?
Cloud-init atua principalmente durante o provisionamento e a primeira inicialização, criando o acesso mínimo e preparando o sistema. Ansible é mais adequado para aplicar e manter configurações ao longo da vida do servidor. Um fluxo comum usa cloud-init para criar um usuário de automação, instalar Python e liberar a conectividade necessária. Depois, Ansible instala a aplicação, atualiza configurações e gerencia serviços. Cloud-init consegue executar scripts extensos, mas isso aumenta a dificuldade de reexecução e diagnóstico. A combinação das duas ferramentas separa bootstrap inicial de manutenção contínua e melhora a rastreabilidade.
O que fazer quando o cloud-init termina com erro?
Comece com `cloud-init status --long` e consulte `/var/log/cloud-init.log` e `/var/log/cloud-init-output.log`. Identifique o primeiro comando que falhou, não apenas o último erro exibido. Depois verifique rede, DNS, disponibilidade do repositório, nome do pacote e sintaxe do serviço. Se o SSH não funcionar, use o console web ou serial oferecido pelo provedor. Corrija o YAML e teste em uma instância nova, pois vários módulos não executam novamente na mesma máquina. Preserve os logs da tentativa original antes de usar `cloud-init clean`, especialmente em incidentes difíceis de reproduzir.
Cloud-init substitui uma imagem de servidor preparada?
Não necessariamente. Cloud-init é excelente para dados específicos da instância, como hostname, usuário, chave pública, rede e pequenos pacotes. Uma imagem preparada é mais eficiente quando todas as máquinas precisam baixar dependências grandes, agentes ou runtimes que mudam pouco. Por exemplo, instalar centenas de megabytes em cinquenta instâncias aumenta o tempo de implantação e a dependência de repositórios externos. Uma estratégia equilibrada mantém o sistema e dependências pesadas na imagem versionada, enquanto o cloud-init aplica parâmetros finais. A imagem precisa receber atualizações e passar por testes para não carregar vulnerabilidades antigas.
Fontes consultadas
- Cloud-init Documentation · coletado em 09/10/2026
- Cloud-init Configuration Examples · coletado em 09/10/2026
- Cloud-init CLI Commands · coletado em 09/10/2026
- HashiCorp Terraform templatefile Function · coletado em 09/10/2026