Ubuntu26.04の正式リリースが目前に迫っているので、ここで一度セットアップの流れを確認してみました。
4月13日版のISOを利用しVM上で動作確認しています。

Tailscale
# アップデート
sudo apt update
sudo apt upgrade -y
# Tailscale
sudo apt install curl
curl -fsSL https://tailscale.com/install.sh | sh
sudo tailscale up
SSH
sudo apt install -y openssh-server
以降はSSHで。
SSH接続時にエラーの場合
下記の表示でエラーが出た場合。何度も接続先をやり直しているとローカルにある情報と違うための警告が表示されることがあります。WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!
この場合、ローカルにある情報を削除して接続すれば繋がるはず。
ssh-keygen -R ホスト名やIPアドレス
Ubuntu Desktopをサーバにする場合
設定画面で「リモートデスクトップ」-「デスクトップ共有」を有効化。キーリングは空のまま保存。
自動画面ロックを無効化
# 自動画面ロックを無効化
gsettings set org.gnome.desktop.screensaver lock-enabled false
# 自動スリープ(画面オフ)も無効にする場合
gsettings set org.gnome.desktop.session idle-delay 0
ロック時でもデスクトップ共有
デスクトップ共有の場合、ロックされてもアクセス出来るように。
まずGNOMEのシェル拡張機能をインストール。
sudo apt install gnome-shell-extension-manager
「探す」で「Allow locked Remote Desktop」を検索してインストールすれば有効に。
Allow locked Remote Desktop
ノートPCで画面が閉じてもスリープしない設定
sudo sed -i 's/#HandleLidSwitch=suspend/HandleLidSwitch=ignore/' /etc/systemd/logind.conf
sudo reboot
Taildrop
送信用
sudo tailscale set --operator=$USER && sudo mkdir -p /opt/script && sudo tee /opt/script/taildrop-send.sh << 'EOF'
#!/usr/bin/env bash
# taildrop-send.sh — Taildropでファイルを送信するスクリプト
set -euo pipefail
BOLD='\033[1m'
CYAN='\033[1;36m'
GREEN='\033[1;32m'
YELLOW='\033[1;33m'
RED='\033[1;31m'
DIM='\033[2m'
RESET='\033[0m'
info() { echo -e "${CYAN}${BOLD}→${RESET} $*"; }
success() { echo -e "${GREEN}${BOLD}✓${RESET} $*"; }
warn() { echo -e "${YELLOW}${BOLD}!${RESET} $*"; }
error() { echo -e "${RED}${BOLD}✗${RESET} $*" >&2; }
sep() { echo -e "${DIM}$(printf '─%.0s' {1..52})${RESET}"; }
check_dependencies() {
if ! command -v tailscale &>/dev/null; then
error "tailscale コマンドが見つかりません。"
echo " インストール: https://tailscale.com/download/linux"
exit 1
fi
if ! tailscale status &>/dev/null; then
error "Tailscale が起動していません。"
echo " 起動: sudo systemctl start tailscaled && sudo tailscale up"
exit 1
fi
}
select_file() {
echo ""
echo -e "${BOLD}📁 現在のフォルダのファイル一覧${RESET} ${DIM}$(pwd)${RESET}"
sep
mapfile -t FILES < <(find . -maxdepth 1 -type f ! -name '.*' | sed 's|^\./||' | sort)
if [[ ${#FILES[@]} -eq 0 ]]; then
error "ファイルが見つかりません: $(pwd)"
exit 1
fi
local i=1
for f in "${FILES[@]}"; do
local size
size=$(du -sh "$f" 2>/dev/null | cut -f1)
printf " ${CYAN}%3d${RESET} %-42s ${DIM}%s${RESET}\n" "$i" "$f" "$size"
((i++))
done
sep
echo ""
while true; do
read -rp "$(echo -e "送信するファイルの番号を入力 ${DIM}[1-${#FILES[@]}]${RESET}: ")" file_choice
if [[ "$file_choice" =~ ^[0-9]+$ ]] && \
(( file_choice >= 1 && file_choice <= ${#FILES[@]} )); then
SELECTED_FILE="${FILES[$((file_choice - 1))]}"
success "選択したファイル: ${BOLD}${SELECTED_FILE}${RESET}"
break
else
warn "無効な番号です。1〜${#FILES[@]} の範囲で入力してください。"
fi
done
}
select_target() {
echo ""
echo -e "${BOLD}💻 Tailnetのデバイス一覧${RESET}"
sep
local self_ip
self_ip=$(tailscale ip -4 2>/dev/null || echo "SELF")
mapfile -t STATUS_LINES < <(
tailscale status 2>/dev/null \
| awk 'NF>=2 && $1!~/^#/ && !/^$/' \
| grep -v "^${self_ip}" || true
)
declare -a PEER_NAMES=()
declare -a PEER_IPS=()
declare -a PEER_STATUS=()
for line in "${STATUS_LINES[@]}"; do
local ip hostname rest
ip=$(echo "$line" | awk '{print $1}')
hostname=$(echo "$line" | awk '{print $2}')
rest=$(echo "$line" | cut -d' ' -f3-)
local flag
if echo "$rest" | grep -qwi 'offline'; then
flag="オフライン"
else
flag="オンライン"
fi
PEER_NAMES+=("$hostname")
PEER_IPS+=("$ip")
PEER_STATUS+=("$flag")
done
if [[ ${#PEER_NAMES[@]} -eq 0 ]]; then
error "接続可能なデバイスが見つかりません。"
exit 1
fi
local i=1
for idx in "${!PEER_NAMES[@]}"; do
local color="${DIM}"
[[ "${PEER_STATUS[$idx]}" == "オンライン" ]] && color="${GREEN}"
printf " ${CYAN}%3d${RESET} %-30s ${DIM}%-20s${RESET} ${color}%s${RESET}\n" \
"$i" "${PEER_NAMES[$idx]}" "${PEER_IPS[$idx]}" "${PEER_STATUS[$idx]}"
((i++))
done
sep
echo ""
while true; do
read -rp "$(echo -e "送信先の番号を入力 ${DIM}[1-${#PEER_NAMES[@]}]${RESET}: ")" target_choice
if [[ "$target_choice" =~ ^[0-9]+$ ]] && \
(( target_choice >= 1 && target_choice <= ${#PEER_NAMES[@]} )); then
SELECTED_TARGET="${PEER_NAMES[$((target_choice - 1))]}"
SELECTED_IP="${PEER_IPS[$((target_choice - 1))]}"
success "送信先: ${BOLD}${SELECTED_TARGET}${RESET} (${SELECTED_IP})"
break
else
warn "無効な番号です。1〜${#PEER_NAMES[@]} の範囲で入力してください。"
fi
done
}
send_file() {
echo ""
sep
echo -e " ${BOLD}送信内容の確認${RESET}"
sep
printf " %-10s ${BOLD}%s${RESET}\n" "ファイル:" "${SELECTED_FILE}"
printf " %-10s ${BOLD}%s${RESET} ${DIM}(%s)${RESET}\n" "送信先:" "${SELECTED_TARGET}" "${SELECTED_IP}"
sep
echo ""
read -rp "$(echo -e "送信しますか? ${DIM}[y/N]${RESET}: ")" confirm
[[ "$confirm" =~ ^[yY] ]] || { echo ""; warn "キャンセルしました。"; exit 0; }
echo ""
info "送信中..."
if tailscale file cp "$SELECTED_FILE" "${SELECTED_TARGET}:" 2>/tmp/_taildrop_err; then
echo ""
success "送信完了: ${BOLD}${SELECTED_FILE}${RESET} → ${BOLD}${SELECTED_TARGET}${RESET}"
else
local errmsg
errmsg=$(cat /tmp/_taildrop_err 2>/dev/null || echo "")
if echo "$errmsg" | grep -qi "access denied\|permission denied"; then
warn "権限エラー検出 → sudo で再試行します..."
if sudo tailscale file cp "$SELECTED_FILE" "${SELECTED_TARGET}:"; then
echo ""
success "送信完了: ${BOLD}${SELECTED_FILE}${RESET} → ${BOLD}${SELECTED_TARGET}${RESET}"
else
echo ""
error "送信に失敗しました(sudo でも失敗)。"
echo "$errmsg"
exit 1
fi
else
echo ""
error "送信に失敗しました。"
echo "$errmsg"
exit 1
fi
fi
echo ""
}
main() {
echo ""
echo -e " ${BOLD}${CYAN}Taildrop ファイル送信ツール${RESET}"
echo ""
check_dependencies
select_file
select_target
send_file
}
main "$@"
EOF
sudo chmod +x /opt/script/taildrop-send.sh && echo "セットアップ完了"
あとは、送信したいファイルがあるフォルダで下記を実施します。
bash /opt/script/taildrop-send.sh
受信用
受け取ったデータを/opt/lxd-data/taildropに保存することで、どのコンテナからも参照出来ます。サービス化で再起動しても自動実行されます。
TARGET_DIR="/opt/lxd-data/taildrop"
# ディレクトリが存在しない場合は作成
sudo mkdir -p "$TARGET_DIR"
# サービスファイル作成(sudo tee を使う)
sudo tee /etc/systemd/system/taildrop.service > /dev/null <<EOF
[Unit]
Description=Tailscale File Receiver (Taildrop)
After=network.target tailscaled.service
Requires=tailscaled.service
[Service]
Type=simple
ExecStart=tailscale file get --loop --conflict=rename $TARGET_DIR
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target
EOF
# 有効化・起動
sudo systemctl daemon-reload
sudo systemctl enable taildrop
sudo systemctl start taildrop
# 確認(Ctrl+Cで終了)
sudo systemctl status taildrop
LXD
LXD-UI
まずはLXD-UIセットアップ部分を。
#!/bin/bash
set -e
# LXD インストール
sudo snap install lxd --channel=latest/stable
# 初期化
sudo lxd init --minimal
# HTTPS API を有効化
sudo lxc config set core.https_address :8443
# UI 有効化
sudo snap set lxd ui.enable=true
sudo systemctl reload snap.lxd.daemon
# ユーザーを lxd グループに追加
sudo usermod -aG lxd $USER
# マジックDNS名を取得してアクセス先を表示
echo ""
echo "=========================================="
echo " LXD-UI セットアップ完了"
echo "=========================================="
# ホスト自身のマジックDNS名を取得(Multipass環境の場合 .local、通常はhostname)
HOSTNAME=$(hostname)
MAGIC_DNS="${HOSTNAME}"
echo " アクセス先:"
echo " https://${MAGIC_DNS}:8443"
echo "=========================================="
echo ""
echo "※ グループ変更を反映するため、一度ログアウト&再ログインしてください"
sudo reboot
6個のスクリプトを一度に作成して保存するスクリプト
コピーボタンでまとめてコピーして、ターミナルに貼り付けて実行するだけです。
実行後、以下の6ファイルが /opt/script/lxd/ に作成され、実行権限も付与されます。
生成される6つのスクリプトの内容:
| ファイル名 | 内容 |
|---|---|
minimal-lxd-base-create.sh | コンテナ作成・マウントディレクトリチェック付き |
first-setup-minimal-lxd-base.sh | apt update/upgrade・curl・Tailscaleインストール後シャットダウン |
docker-lxd-base-create.sh | lxd-base-minimalをコピーしてDocker環境構築・シャットダウン |
copy-lxd-create.sh | 既存コンテナをコピーして新規作成 |
snapshot-lxd.sh | スナップショット作成 |
enter-lxd-container.sh | コンテナ選択して入る |
#!/bin/bash
set -euo pipefail
sudo mkdir -p /opt/script/lxd
cat > /tmp/minimal-lxd-base-create.sh << 'EOF'
#!/bin/bash
set -euo pipefail
CONTAINER="lxd-base-minimal"
MOUNT_PATH="/opt/lxd-data"
echo "=== lxd-base-minimal コンテナを作成 ==="
lxc launch ubuntu:24.04 "$CONTAINER"
echo "=== $MOUNT_PATH が存在しない場合は作成 ==="
if [ ! -d "$MOUNT_PATH" ]; then
sudo mkdir -p "$MOUNT_PATH"
if [ ! -d "$MOUNT_PATH" ]; then
echo "エラー: $MOUNT_PATH の作成に失敗しました"
exit 1
fi
echo " $MOUNT_PATH を作成しました"
else
echo " $MOUNT_PATH は既に存在します"
fi
echo "=== ホストの $MOUNT_PATH をコンテナに同じパスでマウント ==="
lxc config device add "$CONTAINER" opt-lxd-data disk source="$MOUNT_PATH" path="$MOUNT_PATH"
echo "=== ID マッピング設定を適用 ==="
lxc config set "$CONTAINER" raw.idmap "both 1000 1000"
echo "=== コンテナを再起動します ==="
lxc restart "$CONTAINER"
echo "=== 完了しました ==="
EOF
cat > /tmp/first-setup-minimal-lxd-base.sh << 'EOF'
#!/bin/bash
set -euo pipefail
CONTAINER="lxd-base-minimal"
echo "==> コンテナ '${CONTAINER}' に接続してセットアップを開始します..."
lxc exec "${CONTAINER}" -- bash -euo pipefail << 'INNER'
echo "==> apt update"
sudo apt update
echo "==> apt upgrade"
sudo apt upgrade -y
echo "==> curl インストール"
sudo apt install -y curl
echo "==> Tailscale インストール"
curl -fsSL https://tailscale.com/install.sh | sh
echo "==> セットアップ完了"
INNER
echo "==> コンテナをシャットダウンします..."
lxc stop "${CONTAINER}"
echo "==> 完了"
EOF
cat > /tmp/docker-lxd-base-create.sh << 'EOF'
#!/bin/bash
set -euo pipefail
SRC="lxd-base-minimal"
NEW="lxd-base-docker"
MOUNT_PATH="/opt/lxd-data"
echo "=== コンテナをコピー: $SRC → $NEW ==="
lxc copy "$SRC" "$NEW"
echo "=== コンテナを停止します(設定適用のため) ==="
lxc stop "$NEW" 2>/dev/null || true
echo "=== raw.idmap を設定 ==="
lxc config set "$NEW" raw.idmap "both 1000 1000"
echo "=== Nesting を Allow に設定 ==="
lxc config set "$NEW" security.nesting true
echo "=== コンテナを起動 ==="
lxc start "$NEW"
echo "=== マウントディレクトリの権限設定(ホスト側) ==="
sudo chown 1000:1000 "$MOUNT_PATH"
sudo chmod 775 "$MOUNT_PATH"
echo "=== 設定反映のため再起動 ==="
lxc restart "$NEW"
echo "=== ネットワーク疎通を待機中 ==="
for i in $(seq 1 30); do
if lxc exec "$NEW" -- curl -fsSL --max-time 3 https://get.docker.com -o /dev/null 2>/dev/null; then
echo " ネットワーク疎通確認 (${i}秒)"
break
fi
echo " 待機中... (${i}/30秒)"
sleep 1
if [ "$i" -eq 30 ]; then
echo "エラー: ネットワークが30秒以内に疎通しませんでした" >&2
exit 1
fi
done
echo "=== コンテナ内でセットアップを実行 ==="
lxc exec "$NEW" -- bash -euo pipefail << 'INNER'
echo "--- Docker インストール ---"
curl -fsSL https://get.docker.com | sh
echo "--- /opt/docker ディレクトリのセットアップ ---"
mkdir -p /opt/docker
if getent group docker > /dev/null 2>&1; then
chown -R "${USER:-root}:docker" /opt/docker
chmod -R 775 /opt/docker
chmod -R g+s /opt/docker
if [ "${USER:-root}" != "root" ]; then
usermod -aG docker "$USER"
echo "グループ変更を反映するには、newgrp docker を実行してください(再起動不要)"
else
echo "rootユーザーのため usermod はスキップしました"
fi
else
chown -R "${USER:-root}:${USER:-root}" /opt/docker
chmod -R 755 /opt/docker
echo "docker グループが存在しないため、オーナー権限のみ設定しました"
fi
echo "/opt/docker のセットアップ完了"
INNER
echo "=== コンテナをシャットダウン ==="
lxc stop "$NEW"
echo "=== 完了: $NEW ==="
EOF
cat > /tmp/copy-lxd-create.sh << 'EOF'
#!/bin/bash
set -euo pipefail
containers=($(lxc list -c n --format csv))
echo "=== コピー元のコンテナを選択してください ==="
i=1
for c in "${containers[@]}"; do
echo "$i) $c"
((i++))
done
read -p "番号を入力: " index
if ! [[ "$index" =~ ^[0-9]+$ ]]; then
echo "エラー: 数字を入力してください" >&2
exit 1
fi
index=$((index - 1))
if [ "$index" -lt 0 ] || [ "$index" -ge "${#containers[@]}" ]; then
echo "エラー: 不正な番号です" >&2
exit 1
fi
SRC="${containers[$index]}"
echo "選択されたコピー元: $SRC"
read -p "新しいコンテナ名を入力: " NEW
if [ -z "$NEW" ]; then
echo "エラー: コンテナ名が空です" >&2
exit 1
fi
echo "=== コピー開始: $SRC → $NEW ==="
lxc copy "$SRC" "$NEW"
echo "=== 一旦コンテナを停止します(ID マップ適用のため) ==="
lxc stop "$NEW" 2>/dev/null || true
echo "=== raw.idmap を設定します ==="
lxc config set "$NEW" raw.idmap "both 1000 1000"
echo "=== コンテナを起動します ==="
lxc start "$NEW"
echo "=== 設定反映のため再起動 ==="
lxc restart "$NEW"
echo "=== コンテナに入ります: $NEW ==="
lxc exec "$NEW" -- bash
EOF
cat > /tmp/snapshot-lxd.sh << 'EOF'
#!/bin/bash
set -euo pipefail
echo "=== LXD コンテナ一覧 ==="
# コンテナ名一覧を配列に格納
containers=($(lxc list -c n --format csv))
# 一覧表示
i=1
for c in "${containers[@]}"; do
echo "$i) $c"
((i++))
done
# 番号選択
read -p "番号を入力: " index
# 数字チェック
if ! [[ "$index" =~ ^[0-9]+$ ]]; then
echo "エラー: 数字を入力してください" >&2
exit 1
fi
index=$((index - 1))
# 範囲チェック
if [ "$index" -lt 0 ] || [ "$index" -ge "${#containers[@]}" ]; then
echo "エラー: 不正な番号です" >&2
exit 1
fi
TARGET="${containers[$index]}"
echo "選択されたコンテナ: $TARGET"
# コメント入力
read -p "スナップショットのコメントを入力(任意・空可): " COMMENT
# スペースをハイフンに変換
COMMENT="${COMMENT// /-}"
# スナップショット名(コメントがあれば付加)
SNAP="snap-$(date +%Y%m%d-%H%M%S)"
if [ -n "$COMMENT" ]; then
SNAP="${SNAP}-${COMMENT}"
fi
echo "=== コンテナ停止: $TARGET ==="
lxc stop "$TARGET" 2>/dev/null || true
echo "=== スナップショット作成: $TARGET/$SNAP ==="
lxc snapshot "$TARGET" "$SNAP"
echo "=== コンテナ起動: $TARGET ==="
lxc start "$TARGET"
echo "=== 完了しました ==="
echo "作成されたスナップショット: $TARGET/$SNAP"
EOF
cat > /tmp/enter-lxd-container.sh << 'EOF'
#!/bin/bash
set -euo pipefail
echo "=== LXD コンテナ一覧 ==="
containers=($(lxc list -c n --format csv))
i=1
for c in "${containers[@]}"; do
echo "$i) $c"
((i++))
done
read -p "番号を入力: " index
if ! [[ "$index" =~ ^[0-9]+$ ]]; then
echo "エラー: 数字を入力してください" >&2
exit 1
fi
index=$((index - 1))
if [ "$index" -lt 0 ] || [ "$index" -ge "${#containers[@]}" ]; then
echo "エラー: 不正な番号です" >&2
exit 1
fi
TARGET="${containers[$index]}"
echo "選択されたコンテナ: $TARGET"
STATUS=$(lxc info "$TARGET" | awk '/Status:/ {print tolower($2)}')
if [ "$STATUS" != "running" ]; then
echo "=== コンテナが停止中です。起動します ==="
lxc start "$TARGET"
else
echo "=== コンテナは既に起動中です ==="
fi
echo "=== コンテナに入ります: $TARGET ==="
lxc exec "$TARGET" -- bash
EOF
sudo mv /tmp/minimal-lxd-base-create.sh /opt/script/lxd/
sudo mv /tmp/first-setup-minimal-lxd-base.sh /opt/script/lxd/
sudo mv /tmp/docker-lxd-base-create.sh /opt/script/lxd/
sudo mv /tmp/copy-lxd-create.sh /opt/script/lxd/
sudo mv /tmp/snapshot-lxd.sh /opt/script/lxd/
sudo mv /tmp/enter-lxd-container.sh /opt/script/lxd/
sudo chmod +x /opt/script/lxd/*.sh
sudo chown user:user /opt/script/lxd/*.sh
echo "=== 完了 ==="
ls -la /opt/script/lxd/
スクリプトを利用してベースコンテナを作成するなら次のコマンドで。
cd /opt/script/lxd/
./minimal-lxd-base-create.sh
作成したコンテナ内をアップデートしてTailscaleをインストール。
./first-setup-minimal-lxd-base.sh
そのコンテナをコピーしDocker環境のコンテナを作成。
./docker-lxd-base-create.sh
以後は、いずれかのコンテナをコピーして利用します。
./copy-lxd-create.sh
Cockpit
VMが必要ならCockpitが便利なのでホストにインストール。Tailscale Serveで、Chrome利用時でもパスワードが保存されるようにしています。
#!/bin/bash
set -e
# --- Cockpit + Tailscale セットアップ ---
sudo apt install -y nano cockpit
# 仮想マシンプラグイン
sudo apt install -y cockpit-machines
# --- Tailscale Serve 設定 ---
sudo tailscale serve --bg http://localhost:9090
# Tailscaleのフルドメイン名を自動取得
TAILSCALE_DOMAIN=$(tailscale status --json | python3 -c "
import json, sys
data = json.load(sys.stdin)
self = data.get('Self', {})
dns = self.get('DNSName', '').rstrip('.')
print(dns)
")
# CockpitのCSPにTailscaleドメインを許可(HTTPSアクセス対応)
sudo mkdir -p /etc/cockpit
sudo tee /etc/cockpit/cockpit.conf << EOF
[WebService]
AllowUnencrypted=true
Origins = https://${TAILSCALE_DOMAIN}
EOF
sudo systemctl restart cockpit
echo ""
echo "======================================"
echo " セットアップ完了!"
echo "======================================"
echo " アクセス先: https://${TAILSCALE_DOMAIN}"
echo "======================================"
デフォルトの場所(/var/lib/libvirt/images)よりもアクセスしやすいように変更。
Cockpitにアクセスし、仮想マシンページを開いたあとに下記を実行。
# ディレクトリ作成
sudo mkdir -p /opt/vm
# グループをlibvirtに統一
sudo chown root:libvirt /opt/vm
# setgidビット付与
sudo chmod 2775 /opt/vm
# 自分をlibvirtグループに追加
sudo usermod -aG libvirt $USER
# 再起動後も自動起動されるよう登録
sudo systemctl enable libvirtd
一度再起動。
sudo reboot
再ログイン後に実行。
# デフォルトプールとして登録
virsh pool-define-as default dir --target /opt/vm
virsh pool-build default
virsh pool-start default
virsh pool-autostart default
ISOをコピー
mv ~/iso/*.iso /opt/vm
Windows 11の場合
マイクロソフトのサイトからダウンロードしたWin11_25H2_Japanese_x64_v2.isoが/opt/vmにある想定。
## Windows 11 ISOの再パッケージ化
# 依存パッケージのインストール
sudo apt update
sudo apt install -y libhivex-bin libwin-hivex-perl wimtools genisoimage \
golang-go git make
## distrobuilderのビルド
# ソースをクローン&ビルド
git clone https://github.com/lxc/distrobuilder
cd distrobuilder
make
# バイナリをコピー
sudo cp ~/go/bin/distrobuilder /usr/local/bin/distrobuilder
# 確認
distrobuilder --version
# virtioドライバ統合のためISOを再パッケージ
cd /opt/vm
# virtio-win ISOをダウンロード
wget https://fedorapeople.org/groups/virt/virtio-win/direct-downloads/stable-virtio/virtio-win.iso
# virtioドライバ統合済みISOを再パッケージ
sudo distrobuilder repack-windows Win11_25H2_Japanese_x64_v2.iso Win11-v.iso \
--drivers=/opt/vm/virtio-win.iso
virtioドライバ統合ISOを使用してセットアップ
#!/bin/bash
VM_NAME="Win11"
ISO_PATH="/opt/vm/Win11-v.iso"
DISK_PATH="/opt/vm/Win11.qcow2"
DISK_SIZE="64"
RAM="4096"
VCPUS="4"
qemu-img create -f qcow2 "$DISK_PATH" "${DISK_SIZE}G"
virt-install \
--name "$VM_NAME" \
--ram "$RAM" \
--vcpus "$VCPUS" \
--cpu host-passthrough \
--os-variant win11 \
--disk path="$DISK_PATH",format=qcow2,bus=virtio \
--cdrom "$ISO_PATH" \
--network network=default,model=virtio \
--graphics vnc,listen=127.0.0.1 \
--video vga \
--boot uefi,cdrom,hd \
--features smm=on \
--clock hypervclock_present=yes \
--tpm backend.type=emulator,backend.version=2.0,model=tpm-crb \
--noautoconsole
echo "VM '$VM_NAME' を作成しました。"
virtio-win.isoが無いなら、サイトからゲストエージェントをダウンロード(現時点ではvirtio-win-guest-tools.exe)。
パスワードを設定した場合の自動ログイン設定
コマンドプロンプトを管理者で実行してレジストリに登録。
# レジストリに登録
reg add "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\PasswordLess\Device" /v DevicePasswordLessBuildVersion /t REG_DWORD /d 0 /f
# パスワードを指定
control userpasswords2
qcow2ディスクの拡張
sudo qemu-img resize /opt/vm/Win11.qcow2 +60G
Ubuntu 26.04の場合
ISOイメージダウンロード
cd /opt/vm
デスクトップ版の場合:(現時点はまだベータ版なので下記URL)
wget https://cdimage.ubuntu.com/daily-live/current/resolute-desktop-amd64.iso
サーバ版の場合:(現時点はまだベータ版なので下記URL)
wget https://cdimage.ubuntu.com/ubuntu-server/daily-live/current/resolute-live-server-amd64.iso
下記で作成(–os-variantは26.04がまだ登録されていないため)
#!/bin/bash
VM_NAME="Ubuntu2604"
ISO_PATH="/opt/vm/resolute-desktop-amd64.iso"
DISK_PATH="/opt/vm/Ubuntu2604.qcow2"
DISK_SIZE="60"
RAM="6144"
VCPUS="4"
qemu-img create -f qcow2 "$DISK_PATH" "${DISK_SIZE}G"
virt-install \
--name "$VM_NAME" \
--ram "$RAM" \
--vcpus "$VCPUS" \
--cpu host-passthrough \
--os-variant ubuntu25.10 \
--disk path="$DISK_PATH",format=qcow2,bus=virtio \
--cdrom "$ISO_PATH" \
--network network=default,model=virtio \
--graphics vnc,listen=127.0.0.1 \
--video virtio \
--boot uefi \
--noautoconsole
echo "VM '$VM_NAME' を作成しました。"
Ubuntu Serverの場合。
#!/bin/bash
VM_NAME="Ubuntu2604sv"
ISO_PATH="/opt/vm/resolute-live-server-amd64.iso"
DISK_PATH="/opt/vm/Ubuntu2604sv.qcow2"
DISK_SIZE="60"
RAM="6144"
VCPUS="4"
qemu-img create -f qcow2 "$DISK_PATH" "${DISK_SIZE}G"
virt-install \
--name "$VM_NAME" \
--ram "$RAM" \
--vcpus "$VCPUS" \
--cpu host-passthrough \
--os-variant ubuntu25.10 \
--disk path="$DISK_PATH",format=qcow2,bus=virtio \
--cdrom "$ISO_PATH" \
--network network=default,model=virtio \
--graphics vnc,listen=127.0.0.1 \
--video virtio \
--boot uefi \
--noautoconsole
echo "VM '$VM_NAME' を作成しました。"
ゲストがUbuntuの場合は次のコマンドでゲストエージェントをインストール。
sudo apt install -y qemu-guest-agent
sudo systemctl enable --now qemu-guest-agent
# エージェントが実行中か確認
systemctl status qemu-guest-agent
ファイラー Navigator プラグイン
sudo apt install -y git
git clone https://github.com/45Drives/cockpit-navigator.git
cd cockpit-navigator
git checkout v0.5.8
sudo apt install -y make
sudo make install
cd ..
rm -rf cockpit-navigator
Samba を Cockpit で管理
sudo apt install -y samba
wget https://github.com/45Drives/cockpit-file-sharing/releases/download/v4.5.3-4/cockpit-file-sharing_4.5.3-4jammy_all.deb
sudo apt install -y ./cockpit-file-sharing_4.5.3-4jammy_all.deb
rm cockpit-file-sharing_4.5.3-4jammy_all.deb
virt-managerもインストール
sudo apt install -y virt-manager

