cloud-init による初期設定の自動化
cloud-init は、サーバーの初回起動時にホスト名・ユーザー・SSH 鍵・ネットワーク・パッケージの導入といった初期設定を自動で行う仕組みです。Ubuntu のクラウドイメージには標準で組み込まれており、クラウドや仮想環境でサーバーを用意するときの初期設定はこれで行います。
手作業で行う初期設定については、ホスト名の変更やユーザー作成と sudo 権限の設定などの各記事で解説しています。本記事では、同じ設定を起動時に済ませる方法を扱います。
本記事の実行例は、cloud-init 26.1 を搭載した Ubuntu 26.04 LTS Server で採取したものです。
与える設定(user-data)
cloud-init に渡す設定は user-data と呼ばれ、#cloud-config で始まる YAML で記述します。本記事では次の設定でサーバーを起動しています。
#cloud-config
hostname: sv2
manage_etc_hosts: true
timezone: Asia/Tokyo
users:
- name: kazulog
groups: [sudo]
shell: /bin/bash
sudo: "ALL=(ALL) NOPASSWD:ALL"
lock_passwd: false
ssh_authorized_keys:
- ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAA... kazulog@client
chpasswd:
expire: false
users:
- name: kazulog
password: kazulog
type: text
package_update: true
packages:
- git
- nginx
write_files:
- path: /etc/motd
content: |
このサーバーは cloud-init で構成されています。
- path: /var/www/html/index.html
content: "<h1>provisioned by cloud-init</h1>\n"
runcmd:
- [systemctl, restart, nginx]
- [sh, -c, "echo provisioned at $(date -Is) >> /var/log/provision.log"]主な項目は次のとおりです。
| 項目 | 内容 |
|---|---|
hostname | ホスト名。manage_etc_hosts: true を併記すると /etc/hosts も更新される |
timezone | タイムゾーン |
users | 作成するユーザー。groups に sudo を指定すると管理コマンドを実行できる。sudo は sudoers に書く内容、ssh_authorized_keys は公開鍵 |
chpasswd | パスワードの設定。expire: false を付けないと初回ログイン時に変更を求められる |
package_update | true で apt update 相当を実行する |
packages | 導入するパッケージの一覧 |
write_files | 作成するファイル。path と content を指定する |
runcmd | 最後に実行するコマンド。リスト形式で書くとシェルを介さず実行される |
users を指定すると、イメージの既定ユーザー(Ubuntu のクラウドイメージでは ubuntu)は作成されません。SSH の公開鍵かパスワードのどちらかを必ず設定してください。 どちらも無いとログインできないサーバーができあがります。user-data をサーバーに渡す方法
cloud-init は、起動時に決まった場所を探して user-data を読み込みます。この置き場所をデータソースと呼び、渡し方は環境によって次の3通りに分かれます。
| 環境 | 渡し方 | データソース |
|---|---|---|
| クラウド(AWS / GCP / Azure など) | インスタンス作成時の「ユーザーデータ」欄に #cloud-config を貼り付ける | 各クラウドのメタデータサービス |
| 仮想基盤(Proxmox / VMware / CML など) | 管理画面の cloud-init 設定欄に貼り付ける。基盤側が ISO イメージを作って仮想 CD-ROM として接続する | NoCloud |
| 手元の仮想環境 | cloud-localds コマンドで ISO イメージ(seed ISO)を自分で作り、クラウドイメージと一緒に起動する | NoCloud |
本記事の実行例は3番目と同じ仕組みで、後述の cloud-init query subplatform が config-disk (/dev/sr0) を返しているのは、この ISO イメージから読み込んだことを示しています。
手元の仮想環境で試す
クラウドや仮想基盤を使わずに試す場合は、次の3つを用意します。
- Ubuntu のクラウドイメージ(通常のインストーラ用 ISO ではなく、インストール済みのディスクイメージ)
- seed ISO(user-data を収めた ISO イメージ)
- それらを起動する仮想化ソフト(qemu / KVM、libvirt など)
seed ISO の作成には cloud-image-utils パッケージの cloud-localds コマンドを使います。
sudo apt install -y cloud-image-utilskazulog@sv1:~$ which cloud-localds qemu-system-x86_64
/usr/bin/cloud-localds
/usr/bin/qemu-system-x86_64user-data を書いたファイルを用意し、cloud-localds に渡します。
cloud-localds [ISO] [USER-DATA]| 入力箇所 | 入力内容 |
|---|---|
| [ISO] | 作成する ISO イメージのファイル名 |
| [USER-DATA] | #cloud-config で始まる user-data のファイル |
kazulog@sv1:~$ cat user-data.yaml | head -6
#cloud-config
hostname: web01
manage_etc_hosts: true
timezone: Asia/Tokyo
users:
- name: kazulog
kazulog@sv1:~$ cloud-localds seed.iso user-data.yaml
kazulog@sv1:~$ ls -lh seed.iso
-rw-rw-r-- 1 kazulog kazulog 366K Sep 10 21:19 seed.isoネットワーク設定も渡す場合は -N で network-config のファイルを指定します。
cloud-localds -N [NETWORK-CONFIG] [ISO] [USER-DATA]kazulog@sv1:~$ cloud-localds -N network-config.yaml seed.iso user-data.yaml
kazulog@sv1:~$ sudo mkdir -p /mnt/seed && sudo mount -o loop,ro seed.iso /mnt/seed
kazulog@sv1:~$ ls -l /mnt/seed
total 0
-rw-rw-r-- 1 kazulog kazulog 33 Sep 10 21:20 meta-data
-rw-r--r-- 1 kazulog kazulog 48 Sep 10 21:20 network-config
-rw-r--r-- 1 kazulog kazulog 478 Sep 10 21:20 user-data
kazulog@sv1:~$ sudo umount /mnt/seedISO の中には user-data と meta-data、-N を指定した場合は network-config が入ります。meta-data は cloud-localds が自動で作ります。
あとは、クラウドイメージをディスクとして、seed ISO をもう1台のディスクとして仮想マシンを起動します。Ubuntu 26.04 のクラウドイメージは UEFI で起動するため、OVMF(UEFI ファームウェア)を指定します。
wget https://cloud-images.ubuntu.com/releases/26.04/release/ubuntu-26.04-server-cloudimg-amd64.imgqemu-system-x86_64 -enable-kvm -m 1024 -smp 1 -nographic \
-drive if=pflash,format=raw,readonly=on,file=/usr/share/OVMF/OVMF_CODE_4M.fd \
-drive if=pflash,format=raw,file=OVMF_VARS.fd \
-drive file=ubuntu-26.04-server-cloudimg-amd64.img,format=qcow2,if=virtio \
-drive file=seed.iso,format=raw,if=virtio \
-net nic -net userOVMF_VARS.fd は /usr/share/OVMF/OVMF_VARS_4M.fd をコピーして使います(UEFI の設定を保存する領域で、仮想マシンごとに1つ必要です)。ovmf と qemu-system-x86 パッケージを導入しておきます。
起動すると cloud-init が seed ISO を読み込み、user-data の内容が適用されます。次はコンソールの出力で、user-data に書いた web01 がホスト名になっていること、データソースとして seed ISO(/dev/vdb)が使われたことが確認できます。
Ubuntu 26.04 LTS web01 ttyS0
web01 login: [ 444.354111] sh[1072]: Completed socket interaction for boot stage config
[ 462.016133] cloud-init[943]: Cloud-init v. 26.1-0ubuntu3~26.04.1 running 'modules:final' at Thu, 10 Sep 2026 12:57:00 +0000. Up 461.65 seconds.[ 465.718178] cloud-init[943]: Cloud-init v. 26.1-0ubuntu3~26.04.1 finished at Thu, 10 Sep 2026 12:57:04 +0000. Datasource DataSourceNoCloud [seed=/dev/vdb]. Up 465.66 secondsqemu-img resize で拡張したものをそのまま起動すると、起動に失敗します。 ディスクの末尾にある GPT の予備ヘッダーが元の位置に残るため、/boot のパーティションが認識されず緊急モードになります(実際に確認しました)。容量を増やす場合は virt-resize などパーティションテーブルまで面倒を見るツールを使うか、拡張せずにそのまま起動してください。設定が反映されたかの確認
起動後、cloud-init status で実行結果を確認します。--long を付けると詳細が表示されます。
cloud-init status --longkazulog@sv2:~$ cloud-init --version
/usr/bin/cloud-init 26.1-0ubuntu3~26.04.1
kazulog@sv2:~$ cloud-init status --long
status: done
extended_status: done
boot_status_code: enabled-by-generator
last_update: Thu, 01 Jan 1970 00:00:27 +0000
detail: DataSourceNoCloud [seed=/dev/sr0]
errors: []
recoverable_errors: {}
kazulog@sv2:~$| 項目 | 内容 |
|---|---|
status | done(完了)/running(実行中)/error/disabled |
boot_status_code | cloud-init が有効かどうか |
detail | 使用したデータソース(後述) |
recoverable_errors | 処理は続行できたが問題があった項目 |
status は done のままで extended_status が degraded done になり、内容が recoverable_errors に入ります。cloud-init status だけでは気づけないため、--long で確認してください。パスワードを設定した場合は、passwd -S の2列目が P になっていることで確認できます。
kazulog@sv2:~$ sudo passwd -S kazulog
kazulog P 2026-09-10 0 99999 7 -1設定した内容が反映されているかは、それぞれのコマンドで確認します。
kazulog@sv2:~$ hostname
sv2
kazulog@sv2:~$ id kazulog
uid=1000(kazulog) gid=1000(kazulog) groups=1000(kazulog),27(sudo)
kazulog@sv2:~$ cat /etc/motd
このサーバーは cloud-init で構成されています。
kazulog@sv2:~$ curl -s localhost | head -2
<h1>provisioned by cloud-init</h1>
kazulog@sv2:~$ cat /var/log/provision.log
provisioned at 2026-09-10T19:37:39+09:00
provisioned at 2026-09-10T19:42:01+09:00
kazulog@sv2:~$ dpkg -l git nginx | grep '^ii'
ii git 1:2.53.0-1ubuntu1 amd64 fast, scalable, distributed revision control system
ii nginx 1.28.3-2ubuntu1.10 amd64 small, powerful, scalable web/proxy server
kazulog@sv2:~$ timedatectl | head -3
Local time: Thu 2026-09-10 19:42:26 JST
Universal time: Thu 2026-09-10 10:42:26 UTC
RTC time: Thu 2026-09-10 10:42:26
kazulog@sv2:~$ ls -l ~/.ssh/authorized_keys
-rw------- 1 kazulog kazulog 110 Sep 10 19:41 /home/kazulog/.ssh/authorized_keysホスト名、ユーザーと所属グループ、write_files で置いたファイル、packages で導入したパッケージ、runcmd の実行結果、タイムゾーン、公開鍵のすべてが反映されています。
データソースの確認
cloud-init が どこから user-data を読んだか をデータソースと呼びます。クラウドではメタデータサービス、仮想環境では ISO イメージ(NoCloud)が使われます。
cloud-init query platform
cloud-init query subplatformkazulog@sv2:~$ cloud-init query platform
nocloud
kazulog@sv2:~$ cloud-init query subplatform
config-disk (/dev/sr0)
kazulog@sv2:~$上の例では、nocloud というデータソースが /dev/sr0(仮想的な CD-ROM)から設定を読み込んでいます。
実際に読み込まれた user-data は /var/lib/cloud/instance/user-data.txt に保存されています。サーバーがどの設定で作られたかを後から確認できます。
kazulog@sv2:~$ sudo head -11 /var/lib/cloud/instance/user-data.txt
#cloud-config
hostname: sv2
manage_etc_hosts: true
timezone: Asia/Tokyo
users:
- name: kazulog
groups: [sudo]
shell: /bin/bash
sudo: "ALL=(ALL) NOPASSWD:ALL"
lock_passwd: false
ssh_authorized_keys:
kazulog@sv2:~$実行時間とログの確認
どの処理にどれだけ時間がかかったかは cloud-init analyze blame で確認できます。起動が遅い場合の調査に使います。
sudo cloud-init analyze blamekazulog@sv2:~$ sudo cloud-init analyze blame | head -10
-- Boot Record 01 --
07.24200s (modules-final/config-package_update_upgrade_install)
00.75900s (modules-config/config-apt_configure)
00.23800s (init-local/search-NoCloud)
00.15500s (modules-final/config-keys_to_console)
00.13400s (init-network/config-set_passwords)
00.11000s (modules-final/config-scripts_user)
00.10500s (init-network/config-growpart)
00.10200s (init-network/config-ssh)
00.07300s (modules-final/config-ssh_authkey_fingerprints)
kazulog@sv2:~$パッケージの導入(config-package_update_upgrade_install)が大部分を占めていることが分かります。
ログは2種類あります。
| ファイル | 内容 |
|---|---|
/var/log/cloud-init.log | cloud-init 自身の詳細なログ |
/var/log/cloud-init-output.log | 実行したコマンドの出力(apt の出力など) |
kazulog@sv2:~$ sudo ls -l /var/log/cloud-init.log /var/log/cloud-init-output.log
-rw-r----- 1 root adm 4059 Sep 10 19:42 /var/log/cloud-init-output.log
-rw-r----- 1 syslog adm 109119 Sep 10 19:42 /var/log/cloud-init.log
kazulog@sv2:~$書式の検証
user-data に誤りがあると、設定の一部が反映されないままサーバーが起動します。投入前に cloud-init schema で検証してください。
sudo cloud-init schema --config-file [FILE]| 入力箇所 | 入力内容 |
|---|---|
| [FILE] | 検証する cloud-config ファイル |
kazulog@sv2:~$ sudo cloud-init schema --config-file /tmp/good.yaml
Valid schema /tmp/good.yaml
kazulog@sv2:~$ sudo cloud-init schema --config-file /tmp/bad.yaml
Invalid user-data /tmp/bad.yaml
Error: Cloud config schema errors: packages: 'git' is not of type 'array', runcmds: Additional properties are not allowed ('runcmds' was unexpected)
Error: Invalid schema: user-data
kazulog@sv2:~$誤りがある場合は、項目名と理由が表示されます。上の例では packages に文字列を書いている点(リストである必要がある)と、runcmd を runcmds と誤記している点が報告されています。
適用済みの設定を検証する場合は --system を使います。
kazulog@sv2:~$ sudo cloud-init schema --system | tail -3
2. network-config at /var/lib/cloud/instances/ubuntu/network-config.json:
Valid schema network-config設定をやり直す
cloud-init は初回起動時にのみ実行され、2回目以降の起動では設定を適用しません。検証環境で設定を作り込む場合は、状態を消してから再起動すると初回起動としてやり直せます。
sudo cloud-init clean --logs
sudo rebootkazulog@sv2:~$ sudo cloud-init clean --logs
kazulog@sv2:~$ ls /var/lib/cloud/
seed
kazulog@sv2:~$再起動後は user-data が再度読み込まれ、runcmd も実行し直されます。
kazulog@sv2:~$ uptime -p
up 0 minutes
kazulog@sv2:~$ cloud-init status
status: done
kazulog@sv2:~$ cat /var/log/provision.log
provisioned at 2026-09-10T19:37:39+09:00
provisioned at 2026-09-10T19:42:01+09:00
provisioned at 2026-09-10T19:43:35+09:00
kazulog@sv2:~$ cat /etc/motd
このサーバーは cloud-init で構成されています。runcmd が再度実行され、provision.log に新しい行が追加されています。
cloud-init clean は稼働中のサーバーで実行しないでください。ユーザーやネットワークの設定が再適用され、意図しない状態になることがあります。cloud-init を無効にする
構築が終わったサーバーで cloud-init を動かしたくない場合は、無効化用のファイルを作成します。
sudo touch /etc/cloud/cloud-init.disabledkazulog@sv2:~$ sudo touch /etc/cloud/cloud-init.disabled
kazulog@sv2:~$ ls -l /etc/cloud/cloud-init.disabled
-rw-r--r-- 1 root root 0 Sep 10 19:44 /etc/cloud/cloud-init.disabled
kazulog@sv2:~$再起動後は status が disabled になり、user-data は読み込まれません。
kazulog@sv2:~$ cloud-init status --long
status: disabled
extended_status: disabled
boot_status_code: disabled-by-marker-file
detail: Cloud-init disabled by /etc/cloud/cloud-init.disabled
errors: []
recoverable_errors: {}
kazulog@sv2:~$detail に無効化の理由が表示されます。元に戻す場合はこのファイルを削除します。
ネットワークの設定について
ネットワークの設定は user-data とは別に network-config として渡します。書式は Netplan と同じで、/etc/netplan/50-cloud-init.yaml として書き出されます。詳細はUbuntu 26.04 LTS Server のネットワーク設定で解説しています。
検証環境と実行ログ
本記事の実行例は、CML 上の Ubuntu 26.04 LTS Server(cloud-init 26.1)で採取したものです。各手順の実行ログを以下からダウンロードできます。
| 手順 | 実行ログ |
|---|---|
| seed ISO の作成(cloud-localds) | log |
| ネットワーク設定を含めた ISO の確認 | log |
| cloud-localds と qemu の導入 | log |
| seed ISO から起動した仮想マシンのコンソール | log |
| 実行結果とデータソースの確認 | log |
| 実行時間とパスワードの確認 | log |
| 設定内容の反映確認 | log |
| ログの確認 | log |
| 書式の検証 | log |
| 状態の消去と再起動 | log |
| 再起動後の確認 | log |
| 無効化の設定 | log |
| 無効化の確認 | log |
参考リンク
関連記事
- Ubuntu 26.04 LTS Server のホスト名の変更(hostnamectl)
- Ubuntu 26.04 LTS Server のパッケージ更新(apt update / upgrade)
- Ubuntu 26.04 LTS Server のタイムゾーン設定と時刻同期
- Ubuntu 26.04 LTS Server のユーザー作成と sudo 権限の設定
- Ubuntu 26.04 LTS Server の SSH サーバー設定(公開鍵認証)
- Ubuntu 26.04 LTS Server のサービス管理(systemctl)とログ確認(journalctl)
- Ubuntu 26.04 LTS Server の自動更新設定(unattended-upgrades)
- Ubuntu 26.04 LTS Server の初期設定を自動化する(cloud-init)
- Ubuntu 26.04 LTS Server のカーネルパラメータ設定(sysctl)
- Ubuntu 26.04 LTS Server のネットワーク設定(Netplan)
- Ubuntu 26.04 LTS Server の名前解決設定(systemd-resolved)
- Ubuntu 26.04 LTS Server の NTP 同期先の変更(chrony)
- Ubuntu 26.04 LTS Server のスタティックルート設定(Netplan)
- neovim 公式サイトから最新バージョンをインストールする手順 Ubuntu 26.04 LTS Server