Cloud-init — это стандартный механизм «первой настройки» виртуальной машины: на самом первом старте он читает метаданные от провайдера (datasource), применяет их (hostname, сеть, SSH-доступ), а затем выполняет ваш user-data в формате cloud-config. Для админа это означает предсказуемый provisioning: вместо ручных действий в консоли вы описываете желаемое состояние в YAML и получаете одинаковые инстансы.
Ниже — практичный чек-лист и готовые шаблоны: как правильно создавать пользователей, задавать SSH-ключи, аккуратно работать с сетью на Ubuntu/Debian и AlmaLinux, а также как включить growpart и resize_rootfs, чтобы диск и файловая система автоматически «доросли» до размера, который вы выдали VM.
Как cloud-init устроен на VDS: datasource, стадии и где смотреть логи
Ключевые понятия:
- datasource — источник метаданных и user-data (например, NoCloud, ConfigDrive, OpenStack, EC2-совместимые источники). От него зависит, откуда cloud-init возьмёт сеть, SSH-ключи и ваш
cloud-config. - cloud-config — YAML-описание, которое cloud-init понимает «из коробки»: пользователи, пакеты, файлы, команды, настройки диска.
- стадии — обнаружение datasource, настройка сети, применение конфигов, запуск команд.
Почти вся диагностика начинается с трёх команд:
cloud-init status --long
cloud-init query --all
journalctl -u cloud-init -u cloud-config -u cloud-final --no-pager
И ещё полезны файлы:
/var/log/cloud-init.log— подробный лог./var/log/cloud-init-output.log— вывод того, что выполнялось на стадии final./run/cloud-initи/var/lib/cloud— текущее состояние и артефакты.
Если изменения в user-data «не применяются», частая причина в том, что инстанс уже проходил первый запуск cloud-init. Многие модули выполняются один раз.
Для повторного прогона (осторожно на проде) обычно чистят состояние и перезагружают:
sudo cloud-init clean --logs
sudo reboot
Для проверки можно прогнать стадии вручную без ребута:
sudo cloud-init clean --logs
sudo cloud-init init
sudo cloud-init modules --mode=config
sudo cloud-init modules --mode=final
Базовый каркас cloud-config: пользователи, sudo, SSH и запрет пароля
Типовая задача: создать админ-пользователя, выдать ему sudo, положить SSH-ключи и запретить password login. Делайте это в user-data, а не руками: так проще масштабировать и снижать риск «расхождения» серверов.
Пример: один пользователь, только ключи, парольный вход выключен
#cloud-config
users:
- name: deploy
gecos: Deploy User
groups: [sudo]
shell: /bin/bash
sudo: ["ALL=(ALL) NOPASSWD:ALL"]
lock_passwd: true
ssh_authorized_keys:
- "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAI... your-key-comment"
ssh_pwauth: false
disable_root: true
package_update: true
package_upgrade: false
Нюансы, на которых чаще всего ошибаются:
lock_passwd: trueблокирует пароль у пользователя, оставляя вход по ключам.ssh_pwauth: falseпросит cloud-init отключить парольную аутентификацию SSH, но финально проверьте конфиг sshd.disable_root: trueотключает root-логин, если образ это поддерживает (иногда провайдерские образы ведут себя по-своему).
Пароль всё-таки нужен: как задавать безопасно
Иногда пароль требуется для аварийного доступа через консоль/VNC/serial. Тогда задавайте хэш, а не plaintext. Хэш удобно получить так:
openssl passwd -6
И применить в cloud-config:
#cloud-config
users:
- name: admin
groups: [sudo]
shell: /bin/bash
sudo: ["ALL=(ALL) ALL"]
passwd: "$6$rounds=4096$....$...."
lock_passwd: false
ssh_authorized_keys:
- "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAI..."
ssh_pwauth: false
Даже в этом варианте обычно лучше оставить ssh_pwauth: false, чтобы пароль не работал по SSH, но оставался для локальной консоли.
SSH keys в cloud-init: частые ошибки и быстрая диагностика
Когда «ключ добавлен, но вход не работает», сначала проверьте базовые факты, а уже потом «копайте» datasource и приоритеты образа.
- Пользователь действительно создан:
getent passwd deploy. - Права на каталоги и файлы:
ls -ld /home/deploy /home/deploy/.ssh(обычно 700 на.sshи 600 наauthorized_keys). - Что ключи реально записались:
sudo cat /home/deploy/.ssh/authorized_keys. - Логи SSH:
journalctl -u ssh --no-pager -n 200(Ubuntu/Debian) илиjournalctl -u sshd --no-pager -n 200(AlmaLinux).
Если ключ «точно правильный», проверьте, не отдаёт ли провайдер ключи через metadata, а вы параллельно управляете ими в user-data. У разных образов приоритеты могут отличаться.
Если вы строите воспроизводимые серверы и дальше планируете тиражировать конфигурации, полезно зафиксировать подход: «источник истины» для ключей либо metadata провайдера, либо user-data, но не оба одновременно.

Сеть и cloud-init network: Netplan (Ubuntu) vs NetworkManager (AlmaLinux)
Ошибки в сети — это самый быстрый способ потерять SSH-доступ. Общая рекомендация: если провайдер гарантирует рабочую сеть через metadata, не переопределяйте её без необходимости. Если нужно — держите под рукой консоль в панели и план отката.
Проверяем, кто управляет сетью и что cloud-init реально применил
На Ubuntu чаще всего используется Netplan (который генерирует конфиг для systemd-networkd или NetworkManager). На AlmaLinux чаще встречается NetworkManager.
Минимальный набор диагностики:
cloud-init query network
cloud-init query datasource
ip -br a
ip r
resolvectl status
И посмотрите, какие файлы/профили созданы:
- Ubuntu:
/etc/netplan/*.yaml - systemd-networkd:
/etc/systemd/network/*.network - AlmaLinux NetworkManager:
nmcli con show
Пример network-config (v2) для NoCloud: статический IPv4
Если ваш сценарий — datasource NoCloud, то сеть часто задают отдельным документом network-config (не в user-data). Формат v2 выглядит так:
version: 2
ethernets:
eth0:
dhcp4: false
addresses:
- 203.0.113.10/24
gateway4: 203.0.113.1
nameservers:
addresses:
- 1.1.1.1
- 8.8.8.8
Критично: имя интерфейса (eth0) должно совпадать с реальным. На «предсказуемых именах» это может быть ens3, enp1s0 и т.д. Проверяйте по:
ip -br link
DHCP, IPv6 и маршруты
Если сеть выдаётся через DHCP, обычно лучше не трогать network-config и сосредоточиться на пользователях/ключах. При dual-stack (IPv4+IPv6) внимательно проверьте маршрутизацию:
ip -6 r
sysctl net.ipv6.conf.all.disable_ipv6
Если IPv6 включён, но провайдер не выдал корректный route/RA, некоторые приложения могут «подвисать» на попытках соединения по IPv6. Это чаще всего не проблема cloud-init, а исходных сетевых параметров.
Growpart и resize_rootfs: авторасширение диска «как в облаке»
Ситуация из практики: вы увеличили диск у сервера в панели, перезагрузили — а внутри ОС всё ещё старый размер. Нужно расширить раздел и файловую систему. Cloud-init умеет делать это автоматически (чаще на первом старте; дальнейшее зависит от образа и конфигурации модулей).
Обычно участвуют два шага:
growpart— расширяет раздел (partition) до доступного размера диска.resize_rootfs— расширяет файловую систему root (ext4/xfs и т.д.) послеgrowpart.
Пример cloud-config: включаем growpart и resize_rootfs
#cloud-config
growpart:
mode: auto
devices: ["/"]
ignore_growroot_disabled: false
resize_rootfs: true
Практические замечания:
devices: ["/"]часто работает для root, но на некоторых образах нужно указывать конкретный девайс (например,/dev/vda1).- Если root на LVM, цепочка другая: расширяется PV, затем LV, затем ФС. Некоторые образы это автоматизируют, некоторые — нет.
- Если у образа отключён автогроу (например, через настройки growroot), cloud-init может проигнорировать расширение.
Проверка: что реально расширилось
Проверяйте цепочку «диск → раздел → ФС»:
lsblk -f
df -hT
Если lsblk показывает диск увеличенного размера, но раздел прежний — не сработал growpart. Если раздел вырос, а df нет — не отработало расширение файловой системы.
Datasource: почему cloud-init «не видит» user-data и как диагностировать
От datasource зависит, где именно cloud-init ищет метаданные. Типовые симптомы проблем:
cloud-init statusзависает на стадии network;cloud-init query userdataпустой;- в логах виден перебор источников и итоговый
DataSourceNone.
Быстрая диагностика:
cloud-init query datasource
grep -R "DataSource" /var/log/cloud-init.log | tail -n 50
Иногда есть смысл ограничить список источников, чтобы ускорить boot и убрать «ложные» попытки. Делается через /etc/cloud/cloud.cfg.d/:
sudo sh -c 'cat > /etc/cloud/cloud.cfg.d/90-datasource.cfg << "EOF"
datasource_list: [ NoCloud, ConfigDrive ]
EOF'
Меняйте
datasource_listтолько если уверены, что провайдер использует эти источники. Иначе можно «отрезать» правильный datasource и сломать сеть/ключи на первом старте.
Если вы делаете собственные «золотые образы», полезно отдельно выстроить процесс сборки и проверки cloud-init. В тему: как собрать golden image с cloud-init и Packer.

Практические сценарии для админов: что автоматизировать в первую очередь
1) Безопасный старт: пользователь + ключи + базовые пакеты
Минимально разумный набор для нового сервера: создать пользователя, отключить парольный SSH, поставить базовые утилиты.
#cloud-config
users:
- name: ops
groups: [sudo]
shell: /bin/bash
sudo: ["ALL=(ALL) NOPASSWD:ALL"]
lock_passwd: true
ssh_authorized_keys:
- "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAI..."
ssh_pwauth: false
disable_root: true
packages:
- curl
- wget
- vim
- htop
- git
package_update: true
package_upgrade: true
Если вы подбираете площадку под такие сценарии и вам важны быстрые переустановки и доступ к консоли, ориентируйтесь на админские возможности панели и образов. По теме можно свериться с обзором: сравнение панелей и сценариев управления VDS. Тарифы и параметры — на странице облачных VDS.
2) Запись конфигов через write_files
Если нужно положить конфиг-файл (например, для sysctl или агента мониторинга), используйте write_files. Это обычно предсказуемее, чем «echo в heredoc» внутри runcmd.
#cloud-config
write_files:
- path: /etc/sysctl.d/99-custom.conf
permissions: "0644"
content: |
vm.swappiness = 10
net.core.somaxconn = 1024
runcmd:
- sysctl --system
3) Команды: runcmd vs bootcmd и идемпотентность
runcmd выполняется ближе к концу, когда сеть обычно уже поднята. bootcmd — очень рано, и там проще всего «прострелить ногу» (особенно с сетью/диском). Для большинства задач админа runcmd безопаснее.
Думайте об идемпотентности: если вы повторно прогоните cloud-init, команды должны либо ничего не менять, либо корректно обновлять состояние. Где возможно — используйте модули cloud-init вместо «сырого bash».
Частые проблемы и короткие решения
Cloud-init отработал, но SSH всё равно просит пароль
- Проверьте реальную конфигурацию sshd:
sshd -T | grep -E "passwordauthentication|kbdinteractiveauthentication|permitrootlogin" - Убедитесь, что используются правильные drop-in файлы (в Ubuntu часто
/etc/ssh/sshd_config.d/).
Сеть пропала после правок network-config
- Сверьте имя интерфейса:
ip -br link. - Проверьте маршруты:
ip r. - Проверьте DNS:
resolvectl query example.com. - Смотрите логи:
journalctl -b --no-pager | grep -i -E "netplan|networkd|NetworkManager|cloud-init".
Growpart не сработал
- Проверьте, действительно ли диск расширен на уровне гипервизора:
lsblk. - Проверьте схему разметки (GPT/MBR), номер раздела, root на LVM.
- Проверьте логи cloud-init по модулю growpart:
grep -i growpart /var/log/cloud-init.log | tail -n 50.
Рекомендованный шаблон user-data для старта (Ubuntu/Debian/AlmaLinux)
Универсальная база, которая закрывает 80% потребностей: hostname, пользователь, ключи, обновления, базовые пакеты, авторасширение root (где это поддерживается образом).
#cloud-config
hostname: vds-node
manage_etc_hosts: true
users:
- name: ops
gecos: Operations
groups: [sudo]
shell: /bin/bash
sudo: ["ALL=(ALL) NOPASSWD:ALL"]
lock_passwd: true
ssh_authorized_keys:
- "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAI..."
ssh_pwauth: false
disable_root: true
package_update: true
package_upgrade: true
packages:
- curl
- ca-certificates
- git
- vim
- htop
growpart:
mode: auto
devices: ["/"]
resize_rootfs: true
final_message: "cloud-init finished in $UPTIME seconds"
После первого старта зафиксируйте результат: снимок/backup или «золотой» образ, если тиражируете одинаковые VDS. Это ускоряет развёртывание и упрощает поддержку.
Итоги
Cloud-init — это не «магия», а инженерный инструмент: datasource даёт исходные параметры, а ваш cloud-config доводит систему до нужного состояния. На практике стоит начать с трёх вещей: пользователи и SSH-ключи, аккуратная работа с сетью и автоматическое расширение диска через growpart и resize_rootfs. Дальше вы постепенно превращаете ручной сетап в воспроизводимый provisioning-пайплайн.


