Moon Flower Society > Guia completo de cloud-init no Proxmox

Guia completo de cloud-init no Proxmox

Manual prático para criar imagens-base, templates e máquinas virtuais configuradas automaticamente com cloud-init no Proxmox VE.

Posted July 25, 2026. Markdown. Tags: pesquisa, cloud-init, proxmox, virtualizacao, automacao, linux.


O cloud-init é a peça que transforma uma imagem Linux genérica em uma máquina virtual pronta para uso: com identidade própria, usuário administrativo, chave SSH, rede, pacotes e configurações aplicados automaticamente no primeiro boot. No Proxmox VE, ele permite manter um template limpo e criar novas VMs em poucos minutos, sem repetir manualmente uma instalação inteira.

Este guia começa do zero e acompanha o fluxo completo: entender o que o Proxmox faz, escolher uma imagem, construir um template, clonar uma VM, configurar DHCP ou IP estático, usar snippets personalizados, criar uma imagem-base própria, validar o resultado e diagnosticar falhas. Os exemplos usam principalmente Ubuntu Server 24.04 LTS e Debian, mas os princípios servem para outras distribuições com suporte ao cloud-init.

Resposta curta: para usar cloud-init no Proxmox, você normalmente não precisa montar um servidor de cloud-init. O Proxmox gera um pequeno disco de configuração no formato NoCloud e o conecta à VM. A imagem Linux lê esse disco no primeiro boot e aplica o conteúdo.

Resumo executivo

Para a maioria dos ambientes Proxmox, o melhor desenho é:

  1. baixar uma imagem cloud oficial e verificar sua integridade;
  2. importá-la em uma VM que servirá como template;
  3. adicionar o disco de cloud-init e habilitar console serial e QEMU Guest Agent;
  4. converter a VM desligada em template;
  5. criar um clone para cada novo servidor;
  6. definir usuário, chave SSH, rede e DNS no clone;
  7. iniciar a VM e aguardar cloud-init status --wait;
  8. usar snippets somente quando a tela padrão do Proxmox não for suficiente;
  9. usar Ansible ou outra ferramenta para configuração contínua depois do primeiro boot.

Há três coisas diferentes que as pessoas costumam chamar de “meu cloud-init”:

Camada O que você cria Quando usar
Template padrão Uma imagem cloud oficial importada no Proxmox Melhor opção para quase todos os servidores
Configuração própria Arquivos user-data, vendor-data, network-config e, raramente, meta-data Quando é preciso instalar pacotes, criar arquivos, executar scripts ou controlar a rede além da interface do Proxmox
Imagem-base própria Uma golden image preparada manualmente ou por Packer Quando o boot precisa ser muito rápido, há agentes obrigatórios ou existe uma base corporativa bem controlada

O cloud-init não substitui completamente Ansible, Salt, Puppet ou scripts de implantação. Ele é especialmente bom para preparar a identidade e o estado inicial da VM. Para alterações recorrentes, aplicações complexas e manutenção contínua, uma ferramenta de configuração posterior costuma ser mais previsível.

Metodologia e critérios

A pesquisa foi construída a partir da documentação atual do cloud-init, da referência oficial do Proxmox VE, das páginas de imagens cloud do Ubuntu, Debian e Fedora e da documentação do Packer. Os comandos foram organizados em uma sequência reproduzível e os exemplos evitam depender de um armazenamento, bridge ou faixa de endereços específicos.

Os critérios principais foram:

Os nomes de storages, bridges, discos, usuários e endereços IP dos exemplos precisam ser adaptados ao ambiente real.

O que é cloud-init

Cloud-init é um sistema de inicialização de instâncias. Ele nasceu para nuvens, mas também funciona muito bem em hipervisores locais. Durante o boot, ele procura uma fonte de dados, lê metadados e instruções e executa módulos em fases diferentes da inicialização.

Entre as tarefas que ele consegue realizar estão:

Cloud-init não é um daemon de administração central que permanece “mandando” na VM. A maior parte da configuração de identidade é aplicada na primeira inicialização da instância. Alguns módulos podem rodar a cada boot, mas outros rodam apenas uma vez por instância.

As quatro fases que importam

Em termos práticos, o boot passa por estas etapas:

  1. Detecção da fonte de dados: o cloud-init procura o disco ou serviço que contém as configurações.
  2. Rede inicial: a rede necessária para continuar o provisionamento é preparada.
  3. Configuração: usuários, pacotes, arquivos e outros módulos são processados.
  4. Finalização: comandos tardios, como runcmd, são executados e o estado final é registrado.

Isso explica por que uma VM pode responder ao ping ou até aceitar conexão enquanto o provisionamento ainda está terminando. Para automações, “a VM ligou” não significa necessariamente “o cloud-init terminou”.

Como o Proxmox entrega a configuração

Em VMs Linux, o Proxmox normalmente utiliza o datasource NoCloud. Ao adicionar um CloudInit Drive, o Proxmox cria uma mídia pequena contendo os dados da instância. A imagem convidada encontra essa mídia durante o boot.

O NoCloud trabalha com quatro documentos principais:

Documento Finalidade
meta-data Identidade da instância, hostname e outros metadados
user-data Usuários, chaves, pacotes, arquivos, comandos e configuração específica
vendor-data Base definida pelo provedor ou administrador, que o usuário pode complementar ou substituir
network-config Interfaces, DHCP, endereços, rotas e DNS

Quando você preenche a aba Cloud-Init na interface do Proxmox, o próprio Proxmox gera esses dados. Quando usa cicustom, você substitui uma ou mais partes por arquivos próprios guardados em um storage com suporte a snippets.

flowchart LR
    A["Template com imagem cloud"] --> B["Clone da VM"]
    C["Opções Cloud-Init do Proxmox"] --> D["Disco NoCloud"]
    E["Snippets personalizados, opcionais"] --> D
    D --> B
    B --> F["Primeiro boot"]
    F --> G["cloud-init aplica identidade, rede e configuração"]
    G --> H["VM pronta para SSH e configuração posterior"]

Não é necessário instalar um servidor HTTP, metadata server ou serviço paralelo para esse fluxo. O disco de configuração anexado pelo Proxmox é suficiente.

O que você precisa antes de começar

No host Proxmox

Para as VMs

Convenções usadas nos exemplos

Item Exemplo Adapte para
VMID do template 9000 Um ID livre reservado a templates
VMID do primeiro clone 101 Um ID livre
Storage de discos local-lvm Seu storage real
Storage de snippets local Um storage do tipo Directory com snippets
Bridge vmbr0 Sua bridge
Usuário pablo Seu usuário administrativo
Nome da VM srv01 Seu padrão de nomes
Rede de documentação 192.0.2.0/24 Sua rede real

Os endereços 192.0.2.0/24 e 2001:db8::/32 são reservados para documentação. Eles não funcionarão como endereços reais da sua rede.

Primeiro passo: crie uma chave SSH

Em um computador confiável, crie uma chave moderna:

ssh-keygen -t ed25519 -a 100 -C "pablo-proxmox"

No Linux, o mesmo comando funciona:

ssh-keygen -t ed25519 -a 100 -C "pablo-proxmox"

O arquivo terminado em .pub é a chave pública e pode ser entregue ao Proxmox. A chave privada nunca deve ser copiada para o template, para o host Proxmox ou para o arquivo cloud-init.

Verifique a chave pública:

ssh-keygen -lf ~/.ssh/id_ed25519.pub

Guarde a chave privada com backup protegido. Perder a chave sem ter outro método de acesso pode deixar a VM inacessível.

Escolhendo uma imagem cloud

Uma imagem cloud é diferente do ISO de instalação. Ela já contém um sistema instalado, preparado para ser clonado e inicializado por cloud-init.

Boas opções:

Distribuição Indicação Observação
Ubuntu Server LTS Servidores gerais e maior quantidade de documentação Use a imagem de release estável, não uma daily build
Debian GenericCloud Base enxuta e previsível Excelente para servidores Debian no Proxmox
Fedora Cloud Pacotes recentes, SELinux e ecossistema Fedora Ciclo de suporte menor que uma LTS
Rocky Linux / AlmaLinux GenericCloud Ambientes compatíveis com RHEL Confirme o nome da imagem e suporte da versão escolhida
openSUSE Leap / MicroOS openSUSE tradicional ou host transacional O fluxo e os módulos disponíveis podem variar

Ubuntu 24.04 LTS

A página oficial publica a imagem noble-server-cloudimg-amd64.img e arquivos de checksums. Baixe a imagem de release atual:

cd /var/lib/vz/template/iso
wget https://cloud-images.ubuntu.com/noble/current/noble-server-cloudimg-amd64.img
wget https://cloud-images.ubuntu.com/noble/current/SHA256SUMS
wget https://cloud-images.ubuntu.com/noble/current/SHA256SUMS.gpg

No mínimo, compare o SHA-256:

sha256sum noble-server-cloudimg-amd64.img
grep noble-server-cloudimg-amd64.img SHA256SUMS

A verificação criptográfica completa exige importar e conferir a chave usada pelo Ubuntu. A documentação oficial de imagens públicas explica o processo de assinatura. Não trate apenas o download por HTTPS como substituto permanente da verificação quando a imagem será usada em produção.

Debian

O Debian publica imagens cloud em cloud.debian.org. Uma imagem GenericCloud para amd64 costuma seguir este padrão:

https://cloud.debian.org/images/cloud/trixie/latest/debian-13-genericcloud-amd64.qcow2

Confirme o nome atual no índice oficial antes de automatizar, pois versões e nomes mudam. Baixe também os arquivos de checksum disponibilizados no mesmo diretório.

Não use imagens aleatórias

Uma imagem-base controla todo o sistema operacional das VMs clonadas. Prefira:

Evite templates de fóruns e discos preparados por terceiros sem processo de construção auditável.

Caminho recomendado: template Ubuntu no Proxmox pela CLI

Este é o procedimento mais curto, reproduzível e fácil de diagnosticar.

1. Crie uma VM vazia

No shell do host Proxmox:

qm create 9000 \
  --name ubuntu-2404-cloud \
  --memory 2048 \
  --cores 2 \
  --cpu host \
  --net0 virtio,bridge=vmbr0

O parâmetro --cpu host oferece os recursos da CPU física e costuma ser adequado quando a VM não precisa migrar entre hosts com processadores diferentes. Em um cluster heterogêneo com migração, use um modelo de CPU comum aos nós.

2. Importe o disco da imagem

qm disk import 9000 noble-server-cloudimg-amd64.img local-lvm

Em versões antigas, ou em tutoriais escritos para elas, o mesmo procedimento aparece como qm importdisk. O Proxmox importa o arquivo e informa o volume criado. Em uma VM nova, ele costuma aparecer como vm-9000-disk-0, mas não presuma o nome em um script sem conferir:

qm config 9000
pvesm list local-lvm --vmid 9000

3. Anexe o disco importado

Se o volume criado foi vm-9000-disk-0:

qm set 9000 \
  --scsihw virtio-scsi-pci \
  --scsi0 local-lvm:vm-9000-disk-0

O controlador VirtIO SCSI oferece bom suporte para Linux moderno. Dependendo do storage e da sua política, você também pode avaliar discard=on e ssd=1, mas não os copie sem entender o suporte do armazenamento.

4. Adicione o CloudInit Drive

qm set 9000 --ide2 local-lvm:cloudinit

Embora apareça como IDE neste exemplo, esse é apenas o pequeno disco de configuração. O disco principal continua em SCSI.

5. Defina boot, console e agente

qm set 9000 --boot order=scsi0
qm set 9000 --serial0 socket --vga serial0
qm set 9000 --agent enabled=1

O console serial é especialmente útil com imagens cloud, que frequentemente são preparadas para ele. Habilitar o agente no Proxmox configura o lado do hipervisor; o pacote qemu-guest-agent e seu serviço também precisam existir dentro da imagem.

6. Aumente o disco-base, se necessário

Confira o tamanho:

qm config 9000

Se quiser que todos os clones comecem, por exemplo, com mais 20 GB:

qm disk resize 9000 scsi0 +20G

Versões anteriores também aceitam ou documentam o atalho qm resize. O cloud-init normalmente expande a partição raiz e o filesystem no primeiro boot se a imagem e sua tabela de partições forem compatíveis. Aumentar o disco virtual não garante sozinho que todo layout personalizado crescerá.

7. Não inicialize o template sem necessidade

Uma imagem cloud oficial já foi preparada para o primeiro boot. Não é preciso iniciá-la, entrar nela e “limpar” antes de convertê-la em template. Cada boot desnecessário cria estado, cache e identidade que depois precisam ser removidos corretamente.

8. Converta em template

qm template 9000

O template deve permanecer desligado e genérico. Configurações específicas de servidor devem ser aplicadas aos clones.

O mesmo fluxo pela interface web

A interface web ajuda a visualizar a VM, mas importar o disco cloud pela CLI ainda é o caminho mais direto.

  1. Em Create VM, escolha um VMID e nome para o template.
  2. Em OS, selecione Do not use any media.
  3. Configure o sistema, CPU, memória e rede.
  4. Conclua sem iniciar a VM.
  5. No shell do host, execute qm disk import para importar a imagem.
  6. Na aba Hardware, adicione o disco importado à VM usando SCSI.
  7. Adicione CloudInit Drive.
  8. Em Options, ajuste a ordem de boot para o disco principal.
  9. Em Hardware, adicione Serial Port 0 e use display serial, se desejado.
  10. Habilite QEMU Guest Agent em Options.
  11. Clique com o botão direito na VM e escolha Convert to template.

O nome exato de alguns controles pode variar entre versões do Proxmox VE, mas os objetos necessários são os mesmos: disco raiz, placa de rede, disco cloud-init e ordem de boot.

Criando a primeira VM a partir do template

1. Faça um clone completo

qm clone 9000 101 --name srv01 --full 1

Um clone completo é independente do template e é a escolha mais simples para servidores permanentes. Linked clones economizam espaço e tempo, mas dependem do disco-base e do suporte a snapshots do storage.

2. Defina usuário e chave SSH

Crie um diretório protegido no host para chaves públicas administrativas:

install -d -m 0700 /root/keys

Copie apenas a chave pública para /root/keys/pablo.pub e configure:

qm set 101 --ciuser pablo
qm set 101 --sshkeys /root/keys/pablo.pub

O Proxmox codifica corretamente o conteúdo para o disco cloud-init. Não informe o caminho de uma chave privada.

3. Configure rede por DHCP

Para IPv4 por DHCP e IPv6 por autoconfiguração:

qm set 101 --ipconfig0 ip=dhcp,ip6=auto

Para somente IPv4 por DHCP:

qm set 101 --ipconfig0 ip=dhcp

4. Ou configure IPv4 estático

qm set 101 \
  --ipconfig0 ip=192.0.2.50/24,gw=192.0.2.1

DNS e domínio de pesquisa:

qm set 101 --nameserver "1.1.1.1 9.9.9.9"
qm set 101 --searchdomain lab.example

Para uma segunda interface:

qm set 101 --net1 virtio,bridge=vmbr1
qm set 101 --ipconfig1 ip=dhcp

Em VLAN:

qm set 101 --net0 virtio,bridge=vmbr0,tag=20

A porta da bridge ou o switch físico também precisa transportar a VLAN. O parâmetro no Proxmox não corrige sozinho uma rede que não entrega a VLAN ao host.

5. Confira o que será entregue

qm config 101
qm cloudinit dump 101 user
qm cloudinit dump 101 network
qm cloudinit dump 101 meta
qm cloudinit pending 101

Essa inspeção é uma das melhores maneiras de detectar um usuário incorreto, uma chave ausente ou um endereço digitado errado antes do boot.

6. Inicie e acompanhe

qm start 101

Abra o console serial ou consulte o endereço obtido pelo DHCP. Dentro da VM:

cloud-init status --wait --long

Quando o estado for done, teste:

ssh pablo@ENDERECO_DA_VM

Se o QEMU Guest Agent estiver instalado e ativo, o Proxmox consegue exibir com mais facilidade os endereços do guest. A descoberta por DHCP também pode ser feita no roteador ou servidor DHCP.

Senha ou chave SSH?

Use chave SSH. Se você configurar uma senha:

qm set 101 --cipassword

o comando pode pedir a senha de forma interativa. Ainda assim, o uso de senha cria mais riscos operacionais e pode exigir que o login por senha esteja habilitado no SSH da imagem.

Nunca coloque uma senha em texto puro dentro de:

Se uma senha temporária for indispensável, use hash compatível com a distribuição, proteja o arquivo, force a troca e desabilite o método depois. Cloud-init não é um cofre de segredos.

O que a aba Cloud-Init do Proxmox resolve

Sem escrever YAML, normalmente é possível configurar:

Isso é suficiente para muitas VMs. Comece por aí. Use snippets apenas quando houver uma necessidade concreta, como instalar uma baseline, escrever arquivos ou executar uma preparação própria.

Snippets personalizados: sua configuração cloud-init

Como habilitar snippets no storage

O storage precisa aceitar o tipo de conteúdo Snippets. No storage local, que normalmente corresponde a /var/lib/vz, isso pode ser habilitado em:

Datacenter → Storage → local → Edit → Content → Snippets

Na CLI, primeiro veja a configuração atual:

pvesm status
grep -A 8 '^dir: local' /etc/pve/storage.cfg

Depois acrescente snippets à lista de conteúdos sem remover os tipos existentes. Um exemplo comum é:

pvesm set local --content iso,vztmpl,backup,snippets

Não copie a lista acima cegamente. O parâmetro --content representa a lista completa. Preserve os tipos que já existem no seu storage.cfg.

No storage padrão local, o diretório costuma ser:

/var/lib/vz/snippets/

Crie-o, se necessário:

install -d -m 0750 /var/lib/vz/snippets

Em cluster, o snippet precisa existir em todos os nós para os quais a VM pode migrar, ou estar em um storage compartilhado acessível por todos. Uma referência local:snippets/... válida no nó A pode falhar no nó B se o arquivo não tiver sido replicado.

Como ligar os arquivos à VM

qm set 101 --cicustom \
  "user=local:snippets/user-data.yaml,vendor=local:snippets/vendor-data.yaml,network=local:snippets/network-config.yaml"

Também existe a parte meta= quando metadados personalizados forem realmente necessários.

O ponto mais importante é: um arquivo customizado substitui aquela parte gerada pelo Proxmox; ele não é simplesmente acrescentado. Se você fornecer user=..., o usuário e a chave configurados na aba do Proxmox podem deixar de aparecer, a menos que seu próprio user-data os contenha.

Uma estratégia segura é:

Exemplo:

qm set 101 --cicustom \
  "vendor=local:snippets/vendor-data.yaml"

Valide o YAML antes do boot

Em uma máquina com cloud-init instalado:

cloud-init schema \
  -c ./user-data.yaml \
  --annotate

Dentro de uma VM já inicializada:

cloud-init schema --system

Validação de sintaxe YAML não garante que todos os comandos de shell funcionarão, mas elimina muitos problemas de indentação, chaves inválidas e tipos incorretos.

Exemplo seguro de user-data

O cabeçalho #cloud-config é obrigatório para identificar o formato.

#cloud-config

hostname: srv01
manage_etc_hosts: true

users:
  - default
  - name: pablo
    gecos: Pablo
    groups: [adm, sudo]
    shell: /bin/bash
    sudo: "ALL=(ALL) NOPASSWD:ALL"
    lock_passwd: true
    ssh_authorized_keys:
      - ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAA... pablo-proxmox

ssh_pwauth: false
disable_root: true

package_update: true
packages:
  - qemu-guest-agent
  - curl
  - vim
  - htop
  - ca-certificates

timezone: America/Sao_Paulo
locale: pt_BR.UTF-8

write_files:
  - path: /etc/motd
    owner: root:root
    permissions: '0644'
    content: |
      Servidor provisionado por cloud-init.
      Consulte a documentação interna antes de alterar a rede.

runcmd:
  - [systemctl, enable, --now, qemu-guest-agent]

final_message: "Cloud-init terminou em $UPTIME segundos."

Observações:

Uma baseline compartilhada com vendor-data

vendor-data é adequado para defaults administrados por você. O usuário ou a configuração específica da VM pode complementar ou substituir partes dessa base.

#cloud-config

package_update: true
packages:
  - qemu-guest-agent
  - curl
  - jq
  - chrony

timezone: America/Sao_Paulo

write_files:
  - path: /etc/sysctl.d/60-lab-baseline.conf
    owner: root:root
    permissions: '0644'
    content: |
      net.ipv4.conf.all.accept_redirects = 0
      net.ipv4.conf.default.accept_redirects = 0

runcmd:
  - [sysctl, --system]
  - [systemctl, enable, --now, qemu-guest-agent]

Não transforme vendor-data em um script gigante. Mantenha ali apenas o que realmente é comum a todas as VMs daquele template.

Exemplo de rede estática personalizada

Quando a configuração gerada pelo Proxmox não cobre um cenário de rede, use network-config v2:

version: 2
ethernets:
  lan0:
    match:
      macaddress: "52:54:00:12:34:56"
    set-name: lan0
    addresses:
      - 192.0.2.50/24
      - 2001:db8:20::50/64
    routes:
      - to: default
        via: 192.0.2.1
      - to: default
        via: 2001:db8:20::1
    nameservers:
      search:
        - lab.example
      addresses:
        - 192.0.2.53
        - 2001:4860:4860::8888

Boas práticas:

Para DHCP:

version: 2
ethernets:
  lan0:
    match:
      macaddress: "52:54:00:12:34:56"
    set-name: lan0
    dhcp4: true
    dhcp6: true

Se o MAC da placa for gerado no clone, um snippet de rede rígido com o MAC antigo não funcionará. Nesse caso, gere o snippet por VM ou deixe a rede sob controle do Proxmox.

Escrevendo arquivos depois que pacotes e usuários existirem

Por padrão, write_files pode ocorrer antes da instalação de pacotes e antes da criação de determinados usuários. Em versões atuais do cloud-init, defer: true adia a escrita:

#cloud-config

packages:
  - nginx

write_files:
  - path: /etc/nginx/conf.d/status.conf
    owner: root:root
    permissions: '0644'
    defer: true
    content: |
      server {
          listen 127.0.0.1:8080;
          server_name _;

          location /health {
              access_log off;
              return 200 "ok\n";
          }
      }

runcmd:
  - [nginx, -t]
  - [systemctl, enable, --now, nginx]

O bind em 127.0.0.1 mantém o endpoint fora da rede até que você decida como expô-lo. Se o serviço deve ser público, configure firewall, TLS e controle de acesso conscientemente.

Tarefas longas: use uma unidade systemd

Comandos muito demorados em runcmd tornam o primeiro boot difícil de acompanhar. Uma alternativa é criar uma unidade oneshot:

#cloud-config

write_files:
  - path: /usr/local/sbin/bootstrap-aplicacao
    owner: root:root
    permissions: '0750'
    content: |
      #!/usr/bin/env bash
      set -euo pipefail
      install -d -m 0750 /var/lib/minha-aplicacao
      date --iso-8601=seconds > /var/lib/minha-aplicacao/provisionado-em

  - path: /etc/systemd/system/bootstrap-aplicacao.service
    owner: root:root
    permissions: '0644'
    content: |
      [Unit]
      Description=Bootstrap inicial da aplicacao
      After=network-online.target
      Wants=network-online.target
      ConditionPathExists=!/var/lib/minha-aplicacao/provisionado-em

      [Service]
      Type=oneshot
      ExecStart=/usr/local/sbin/bootstrap-aplicacao
      RemainAfterExit=yes

      [Install]
      WantedBy=multi-user.target

runcmd:
  - [systemctl, daemon-reload]
  - [systemctl, enable, --now, bootstrap-aplicacao.service]

Para aplicações reais, Ansible, um pipeline ou uma imagem versionada costuma oferecer observabilidade e repetição melhores do que dezenas de linhas de shell dentro do YAML.

Preparando e montando um segundo disco

Cloud-init pode particionar, formatar e montar volumes. Essa capacidade é útil, mas potencialmente destrutiva.

Exemplo conceitual para um disco vazio identificado como /dev/sdb:

#cloud-config

disk_setup:
  /dev/sdb:
    table_type: gpt
    layout: true
    overwrite: false

fs_setup:
  - label: dados
    filesystem: ext4
    device: /dev/sdb
    partition: auto

mounts:
  - [LABEL=dados, /srv/dados, ext4, "defaults,nofail", "0", "2"]

Antes de usar:

Para um disco de dados importante, muitas equipes preferem preparar o volume fora do cloud-init ou usar uma automação que faça verificações explícitas antes de alterar partições.

bootcmd, runcmd e scripts

Recurso Quando roda Uso típico
bootcmd Em todos os boots, bem cedo Pequenos ajustes necessários antes da configuração normal
runcmd Uma vez por instância, na fase final Habilitar serviços e executar bootstrap
Script com #! Conforme o formato de user-data Procedimentos inteiramente em shell
write_files + systemd Controlado pelo systemd Processos idempotentes e mais observáveis

Evite usar /tmp para arquivos que precisam sobreviver até módulos posteriores. Durante o boot, serviços de limpeza podem removê-los. Use /run, /var/tmp ou um diretório próprio conforme o ciclo de vida necessário.

Se o YAML for grande ou tiver múltiplos tipos de conteúdo, cloud-init suporta MIME multipart e outros formatos. Para um ambiente pequeno, um #cloud-config legível e versionado costuma ser melhor.

Criando sua própria imagem-base

Você não precisa construir uma distribuição do zero. A opção mais segura é partir da imagem cloud oficial e adicionar somente o indispensável.

O que vale embutir

O que não deve estar na imagem

Método simples: preparar uma VM e limpá-la

  1. Crie uma VM temporária a partir de uma imagem oficial.
  2. Instale apenas os pacotes-base.
  3. Valide que a VM inicializa corretamente.
  4. Remova segredos e estado específico.
  5. Execute a limpeza final.
  6. Desligue a VM.
  7. Converta-a em template sem voltar a iniciá-la.

Dentro da VM, antes do desligamento final:

sudo apt update
sudo apt install -y cloud-init qemu-guest-agent
sudo systemctl enable qemu-guest-agent

sudo cloud-init clean --logs --machine-id --seed
sudo rm -f /etc/ssh/ssh_host_*
sudo poweroff

O --machine-id deixa o identificador preparado para ser recriado no próximo boot. A remoção das chaves de host SSH evita que clones diferentes apresentem a mesma identidade SSH.

Dependendo da distribuição e dos pacotes instalados, revise também:

Não apague arquivos indiscriminadamente. Faça um checklist específico para a distribuição e teste um clone antes de colocar o template em produção.

Método reproduzível: Packer

O Packer possui builder oficial para Proxmox. Ele consegue criar uma VM, instalar ou personalizar o sistema, desligá-la e convertê-la em template.

Packer passa a valer a pena quando:

Fluxo recomendado:

Repositório Git
  ├── packer/
  │   ├── template.pkr.hcl
  │   └── variáveis não secretas
  ├── cloud-init/
  │   ├── vendor-data.yaml
  │   └── exemplos/
  └── ansible/
      └── baseline.yml

Não armazene tokens do Proxmox dentro do arquivo HCL. Use variáveis de ambiente, um cofre de segredos ou credenciais temporárias com permissões mínimas.

Imagem mutável ou reconstruída?

É tentador ligar o template, atualizá-lo e desligá-lo novamente. Com o tempo, isso acumula estado e torna a origem dos problemas difícil de descobrir.

Uma política melhor:

  1. registre a versão e o checksum da imagem oficial;
  2. mantenha a personalização em código;
  3. reconstrua o template;
  4. teste um clone;
  5. publique uma nova versão;
  6. aposente a anterior depois de confirmar a migração.

Gerando um datasource NoCloud manualmente

O Proxmox já gera o disco de configuração. Ainda assim, criar um seed manual é útil para aprender, testar no QEMU ou usar fora do Proxmox.

Em Ubuntu ou Debian, instale cloud-image-utils:

sudo apt update
sudo apt install cloud-image-utils

Crie user-data.yaml:

#cloud-config
users:
  - default
ssh_pwauth: false
packages:
  - qemu-guest-agent

Crie meta-data.yaml:

instance-id: iid-local-lab-001
local-hostname: lab-001

Gere a mídia:

cloud-localds seed-lab-001.img user-data.yaml meta-data.yaml

O arquivo seed-lab-001.img pode ser conectado à VM junto da imagem cloud. O instance-id é importante: cloud-init o usa para decidir se está diante de uma nova instância.

O NoCloud também consegue consumir uma mídia ISO ou VFAT com label CIDATA e os arquivos esperados na raiz. A ferramenta cloud-localds elimina boa parte dos detalhes manuais.

Como cloud-init decide se é o primeiro boot

Cloud-init guarda cache em /var/lib/cloud. Ao iniciar, ele compara a identidade recebida da fonte de dados com a identidade registrada. Se considerar que é a mesma instância, módulos marcados como “por instância” não serão executados de novo.

Isso causa um erro comum:

  1. a VM é iniciada;
  2. o usuário altera user-data;
  3. reinicia a VM;
  4. espera que tudo rode novamente;
  5. nada acontece porque a instância não mudou.

O fluxo normal é corrigir a configuração e criar um clone novo. Para teste controlado, dentro de uma VM descartável:

sudo cloud-init clean --logs --machine-id --seed
sudo reboot

Esse comando pode recriar identidade, chaves e rede. Não o execute em uma VM de produção apenas para “ver se funciona”.

Atualizando a configuração de uma VM

Antes do primeiro boot:

qm set 101 --ciuser pablo
qm set 101 --sshkeys /root/keys/pablo.pub
qm set 101 --ipconfig0 ip=dhcp
qm cloudinit update 101

O Proxmox normalmente regenera o disco quando necessário, e qm cloudinit update permite solicitar a atualização explicitamente.

Depois que a VM já foi inicializada, alterar a configuração no Proxmox não garante que os módulos por instância rodarão novamente. Escolha uma destas abordagens:

Automação simples para criar clones

Um script pequeno pode padronizar a criação sem esconder o que acontece:

#!/usr/bin/env bash
set -euo pipefail

template_id=9000
vm_id="${1:?Uso: $0 VMID NOME IP/CIDR GATEWAY}"
vm_name="${2:?Informe o nome da VM}"
vm_ip="${3:?Informe IP/CIDR ou dhcp}"
vm_gateway="${4:-}"
ssh_key=/root/keys/pablo.pub

if qm status "$vm_id" >/dev/null 2>&1; then
  echo "O VMID $vm_id já existe." >&2
  exit 1
fi

if [[ ! -r "$ssh_key" ]]; then
  echo "Chave pública não encontrada: $ssh_key" >&2
  exit 1
fi

if [[ "$vm_ip" != "dhcp" && -z "$vm_gateway" ]]; then
  echo "Gateway obrigatório para IP estático." >&2
  exit 1
fi

qm clone "$template_id" "$vm_id" --name "$vm_name" --full 1
qm set "$vm_id" --ciuser pablo
qm set "$vm_id" --sshkeys "$ssh_key"

if [[ "$vm_ip" == "dhcp" ]]; then
  qm set "$vm_id" --ipconfig0 ip=dhcp
else
  qm set "$vm_id" --ipconfig0 "ip=${vm_ip},gw=${vm_gateway}"
fi

qm cloudinit update "$vm_id"

echo "Revise antes de iniciar:"
qm config "$vm_id"
qm cloudinit dump "$vm_id" user
qm cloudinit dump "$vm_id" network

Uso com DHCP:

./criar-vm.sh 101 srv01 dhcp

Uso com IP estático:

./criar-vm.sh 102 srv02 192.0.2.52/24 192.0.2.1

O exemplo para antes de ligar a VM, permitindo revisar a configuração. Se quiser automatizar também o start, acrescente qm start "$vm_id" somente depois de ter validações adequadas de IP, VLAN, storage e quotas.

Cloud-init com Terraform, OpenTofu e Ansible

Em um laboratório maior, as responsabilidades podem ser separadas:

Ferramenta Responsabilidade
Packer Construir e versionar o template
Terraform/OpenTofu Criar VM, CPU, RAM, discos, rede e opções cloud-init
cloud-init Preparar identidade e acesso inicial
Ansible Configurar serviços e manter o estado depois do boot

Uma sequência robusta é:

flowchart TD
    A["Packer cria template versionado"] --> B["Proxmox armazena template"]
    B --> C["OpenTofu ou Terraform cria o clone"]
    C --> D["Proxmox entrega NoCloud"]
    D --> E["cloud-init prepara acesso e baseline mínima"]
    E --> F["Automação espera cloud-init terminar"]
    F --> G["Ansible configura serviços"]
    G --> H["Monitoramento e backup validam a VM"]

Não comece por todas essas ferramentas ao mesmo tempo. Primeiro faça um clone funcionar manualmente. Depois automatize o processo que você já entende.

Diagnóstico dentro da VM

Estado geral

cloud-init status --wait --long

Saída estruturada:

cloud-init status --format=json

Logs

sudo less /var/log/cloud-init.log
sudo less /var/log/cloud-init-output.log

Buscas rápidas:

sudo grep -iE 'error|warning|failed|traceback' /var/log/cloud-init.log
sudo grep -iE 'error|failed' /var/log/cloud-init-output.log

Descobrir a fonte de dados

cloud-id
sudo journalctl -u cloud-init-local -u cloud-init -u cloud-config -u cloud-final

Procure DataSourceNoCloud. Se o resultado indicar que nenhuma fonte foi encontrada, verifique o CloudInit Drive e a ordem de boot no Proxmox.

Ver os dados efetivamente recebidos

sudo ls -la /var/lib/cloud/instance/
sudo sed -n '1,240p' /var/lib/cloud/instance/user-data.txt
sudo sed -n '1,240p' /var/lib/cloud/instance/cloud-config.txt
sudo cat /run/cloud-init/instance-data.json

Esses arquivos podem conter dados sensíveis. Não os publique em tickets ou repositórios sem revisar.

Desempenho e módulos lentos

cloud-init analyze show
cloud-init analyze blame

blame ajuda a encontrar módulos demorados, como atualização de pacotes ou comandos que aguardam a rede.

Pacote de diagnóstico

sudo cloud-init collect-logs

O arquivo gerado reúne informações úteis para suporte. Revise-o antes de compartilhar, pois pode conter hostname, endereços, configuração e partes do user-data.

Diagnóstico no Proxmox

Comece verificando a definição da VM:

qm config 101

Procure:

Inspecione o conteúdo gerado:

qm cloudinit dump 101 user
qm cloudinit dump 101 network
qm cloudinit dump 101 meta
qm cloudinit pending 101

Se usa snippets:

pvesm status
ls -l /var/lib/vz/snippets/
grep -A 8 '^dir: local' /etc/pve/storage.cfg

Confira o log de tarefas pela interface web ou em /var/log/pve/tasks/. Para problemas de rede, valide a bridge e a VLAN no host antes de culpar o cloud-init.

Erros comuns e como corrigir

“Não consigo fazer login”

Possíveis causas:

Verifique no Proxmox:

qm cloudinit dump 101 user

Depois use o console e consulte os logs da VM.

“A VM não recebeu IP”

Verifique:

Se a rede foi personalizada por snippet, compare o MAC do YAML com:

qm config 101 | grep '^net'

“Mudei o YAML, reiniciei e nada aconteceu”

Provavelmente o módulo é por instância e já foi executado. Crie um novo clone para testar. Não use cloud-init clean indiscriminadamente em produção.

“Meu YAML parece válido, mas parte dele foi ignorada”

Possíveis causas:

Valide com:

cloud-init schema -c user-data.yaml --annotate

“O QEMU Guest Agent está habilitado, mas não funciona”

Marcar a opção no Proxmox não instala o agente. Dentro da VM:

sudo systemctl status qemu-guest-agent
sudo systemctl enable --now qemu-guest-agent

Se a unidade não existir, instale o pacote da distribuição.

“O disco aumentou, mas o filesystem não”

Analise:

lsblk
findmnt /
df -hT /
sudo growpart --dry-run /dev/sda 1

O dispositivo e a partição podem não ser /dev/sda1. LVM, Btrfs, partições múltiplas e layouts próprios exigem procedimento específico.

“O clone tem a mesma chave SSH de host”

Isso indica que a imagem foi capturada depois da geração das chaves e não foi limpa corretamente. Reconstrua o template e remova as chaves de host antes do desligamento final:

sudo rm -f /etc/ssh/ssh_host_*
sudo cloud-init clean --logs --machine-id --seed
sudo poweroff

Teste se clones diferentes apresentam fingerprints diferentes.

“O snippet funciona em um nó e falha em outro”

O arquivo está em storage local e não existe no segundo nó. Use storage compartilhado com suporte a snippets ou replique os arquivos e mantenha checksums iguais.

“O apt travou ou o primeiro boot demora muito”

Possíveis causas:

Use cloud-init analyze blame, logs e journalctl. Para produção, publique templates atualizados regularmente em vez de obrigar cada VM a baixar centenas de pacotes no primeiro boot.

Segurança

Não coloque segredos no user-data

Dependendo do ambiente, user-data pode ficar acessível:

Use cloud-init para instalar o cliente de um cofre, criar uma identidade inicial ou iniciar um processo de obtenção de credenciais de curta duração. Não use o YAML como cofre.

Chaves e acesso

Template

Snippets

Organização recomendada

Mesmo em homelab, uma estrutura pequena evita confusão:

infraestrutura/
  README.md
  imagens/
    checksums.txt
    origem-e-versao.md
  cloud-init/
    vendor-data-v1.yaml
    exemplos/
      web-user-data.yaml
      worker-user-data.yaml
      network-static.yaml
  scripts/
    criar-vm.sh
  ansible/
    inventory/
    baseline.yml

No Proxmox:

/var/lib/vz/snippets/
  vendor-data-v1.yaml
  web-user-data-v2.yaml
  network-srv01.yaml

Use nomes que revelem função e versão. Evite um único arquivo chamado config.yaml compartilhado por dezenas de VMs sem histórico claro.

Estratégia de atualização dos templates

Uma rotina mensal ou alinhada ao ciclo de patches pode ser:

  1. verificar nova imagem oficial;
  2. validar assinatura e checksum;
  3. revisar changelog da distribuição e do cloud-init;
  4. reconstruir um template com novo ID;
  5. clonar uma VM de teste;
  6. validar acesso, rede, disco, agente, logs e serviços;
  7. executar scanner de vulnerabilidades aprovado no ambiente;
  8. promover o novo template;
  9. manter o anterior durante uma janela curta;
  10. remover templates obsoletos somente após confirmar backups e dependências.

Exemplo de nomes:

ubuntu-2404-cloud-2026-07
debian-13-cloud-2026-07

O VMID pode ser estável para o template atual ou versionado. Em automação, é mais seguro resolver explicitamente qual versão será usada do que depender de “o último template”.

Plano de teste

Antes de considerar o template pronto, crie pelo menos dois clones descartáveis: um com DHCP e outro com IP estático.

Identidade

Rede

Armazenamento

Cloud-init

Proxmox

Um roteiro prático para seu primeiro laboratório

Fase 1: template mínimo

  1. Baixe Ubuntu 24.04 LTS cloud image.
  2. Verifique checksum.
  3. Crie VMID 9000.
  4. Importe o disco.
  5. Adicione CloudInit Drive.
  6. Configure console serial e QEMU Guest Agent.
  7. Converta em template.

Fase 2: primeiro clone

  1. Clone para VMID 101.
  2. Use DHCP.
  3. Configure seu usuário e chave.
  4. Inicie.
  5. Espere o cloud-init terminar.
  6. Teste SSH.
  7. Reinicie e confirme estabilidade.

Fase 3: rede estática

  1. Clone para VMID 102.
  2. Reserve um endereço fora do pool DHCP ou crie reserva.
  3. Configure IP, prefixo, gateway e DNS.
  4. Inicie com o console disponível.
  5. Confirme rotas e resolução.

Fase 4: baseline

  1. Habilite snippets.
  2. Crie somente vendor-data.
  3. Instale qemu-guest-agent, ferramentas básicas e timezone.
  4. Valide o YAML.
  5. Teste em VMID 103.

Fase 5: configuração contínua

  1. Mantenha cloud-init pequeno.
  2. Instale ou habilite o acesso necessário ao Ansible.
  3. Aplique serviços pelo Ansible.
  4. Recrie uma VM do zero para provar que o processo é reproduzível.

Recomendações práticas

Para começar hoje

Use uma imagem cloud oficial do Ubuntu ou Debian, deixe o Proxmox gerar user-data, network-config e meta-data, e configure apenas usuário, chave e rede. Esse caminho já entrega a maior parte do benefício com poucas peças.

Depois que o básico funcionar

Adicione um vendor-data curto com:

Não coloque a aplicação inteira nessa camada.

Para serviços permanentes

Use:

Para muitos templates

Adote Packer somente depois que o processo manual estiver entendido. Versione a origem, o checksum, os snippets e o código de construção. Gere templates novos em vez de editar indefinidamente o mesmo disco.

Para evitar problemas

Conclusão

No Proxmox, criar seu próprio fluxo de cloud-init é mais simples do que montar uma nuvem inteira. O hipervisor já consegue gerar a mídia NoCloud; você precisa de uma imagem cloud confiável, um template bem preparado e uma configuração por clone.

A melhor primeira versão é deliberadamente pequena: imagem oficial, disco cloud-init, usuário, chave SSH, DHCP ou IP estático e QEMU Guest Agent. Depois, snippets adicionam uma baseline própria. Se a preparação crescer, Packer constrói a imagem e Ansible assume a configuração contínua.

O segredo de um bom template não é conter tudo. É ser limpo, reproduzível, sem identidade reutilizada, sem segredos e fácil de substituir. Quando um clone novo nasce com hostname, rede, chaves e acesso corretos, e o processo pode ser repetido sem trabalho manual dentro da VM, o cloud-init está cumprindo seu papel.

Fontes consultadas


Nota sobre atualidade

Pesquisa concluída em 25 de julho de 2026. Comandos, opções da interface, versões de distribuições e formatos aceitos podem mudar. Confirme nomes de imagens, checksums, suporte da distribuição e sintaxe na documentação oficial antes de aplicar em produção.

View the original Markdown file


Ideas | What changed | RSS | About | Text version

Pablo Murad <pablomurad@pm.me>