Skip to content

Instantly share code, notes, and snippets.

@Ostrichbeta
Last active December 10, 2025 14:21
Show Gist options
  • Select an option

  • Save Ostrichbeta/471c4112e6fe38588d530b1be792a190 to your computer and use it in GitHub Desktop.

Select an option

Save Ostrichbeta/471c4112e6fe38588d530b1be792a190 to your computer and use it in GitHub Desktop.
Ubuntu With GPU and Gnome Desktop Installation (Tested on Ubuntu 22.04 and 24.04)
#cloud-config
package_update: true
package_upgrade: true
packages:
- wget
- curl
- build-essential
- linux-headers-$(uname -r)
- linux-modules-extra-$(uname -r)
- dnsutils
- net-tools
- screen
- unzip
- zsh
- qemu-guest-agent
- ubuntu-drivers-common
- nvidia-driver-580-server
- network-manager
- ubuntu-desktop
write_files:
- path: /usr/local/bin/migrate-to-nm.sh
permissions: '0755'
content: |
#!/bin/bash
set -euo pipefail
### --- CONFIG ---
PRESERVE_NAME="yes" # whether to preserve interface name by MAC via .link
DISABLE_STUB="yes" # whether to disable systemd-resolved stub resolver
BACKUP_NETPLAN_DIR="/etc/netplan/backup-by-nmigr"
CLOUD_INIT_DISABLE="yes" # disable cloud-init network config
### --- End CONFIG ---
echo "=== Step 0: Detect current active IPv4 interface and its config ==="
CUR_IFACE=$(ip -o -4 addr show up primary scope global | awk '{print $2; exit}')
if [[ -z "$CUR_IFACE" ]]; then
echo "ERROR: Could not detect active IPv4 interface" >&2
exit 1
fi
echo "Detected interface: $CUR_IFACE"
CUR_MAC=$(cat /sys/class/net/"$CUR_IFACE"/address)
echo "MAC address: $CUR_MAC"
ADDR_CIDR=$(ip -o -4 addr show dev "$CUR_IFACE" | awk '{print $4; exit}')
if [[ -z "$ADDR_CIDR" ]]; then
echo "ERROR: No IPv4 address on $CUR_IFACE" >&2
exit 1
fi
echo "Address/CIDR: $ADDR_CIDR"
GW=$(ip route show default 0.0.0.0/0 dev "$CUR_IFACE" | awk '/default/ {print $3; exit}')
if [[ -z "$GW" ]]; then
GW=$(ip route show default | awk '/default/ {print $3; exit}')
fi
echo "Gateway: $GW"
# Detect DNS via resolvectl (preferred)
DNS=$(resolvectl status "$CUR_IFACE" 2>/dev/null | awk '/DNS Servers:/ {for(i=3;i<=NF;i++) printf "%s ", $i; exit}' | xargs echo) || true
if [[ -z "$DNS" ]]; then
# fallback to /etc/resolv.conf
DNS=$(grep -E "^nameserver" /etc/resolv.conf | awk '{print $2}' | xargs echo)
fi
echo "DNS servers: $DNS"
echo
echo "=== Step 1: Disable cloud-init network configuration (if used) ==="
if [[ "$CLOUD_INIT_DISABLE" == "yes" ]]; then
sudo touch /etc/cloud/cloud-init.disabled
echo "cloud-init globally disabled."
else
sudo mkdir -p /etc/cloud/cloud.cfg.d
sudo tee /etc/cloud/cloud.cfg.d/99-disable-network-config.cfg > /dev/null <<EOF
network: {config: disabled}
EOF
echo "cloud-init network-config disabled."
fi
echo "=== Step 2: Backup existing netplan configs + create minimal delegate to NetworkManager ==="
sudo mkdir -p "$BACKUP_NETPLAN_DIR"
sudo mv /etc/netplan/*.yaml "$BACKUP_NETPLAN_DIR/" 2>/dev/null || true
sudo tee /etc/netplan/01-nm-delegate.yaml > /dev/null <<EOF
network:
version: 2
renderer: NetworkManager
EOF
sudo netplan apply || true
sleep 2
if [[ "$PRESERVE_NAME" == "yes" ]]; then
echo "=== Step 3: Create .link to preserve interface name by MAC ==="
sudo mkdir -p /etc/systemd/network
sudo tee /etc/systemd/network/10-ifname-by-mac.link > /dev/null <<EOF
[Match]
MACAddress=$CUR_MAC
Type=ether
[Link]
Name=$CUR_IFACE
EOF
sudo udevadm control --reload
sudo udevadm trigger --subsystem-match=net --action add
sleep 1
echo "udev triggered; name-preservation rule applied."
fi
NEW_IFACE=$CUR_IFACE
echo "Final interface name for NM: $NEW_IFACE"
echo "=== Step 4: Configure NetworkManager static IPv4 on $NEW_IFACE ==="
sudo tee /etc/NetworkManager/conf.d/10-globally-managed-devices.conf > /dev/null <<EOF
[keyfile]
unmanaged-devices=none
EOF
sudo systemctl restart NetworkManager
CON_NAME="static-$NEW_IFACE"
sudo nmcli con delete "$CON_NAME" 2>/dev/null || true
sudo nmcli con add type ethernet ifname "$NEW_IFACE" con-name "$CON_NAME" \
ipv4.method manual ipv4.addresses "$ADDR_CIDR" ipv4.gateway "$GW" ipv4.dns "$DNS" \
connection.autoconnect yes
sudo nmcli con up "$CON_NAME"
# Disable old systemd wait for online service
sudo systemctl disable systemd-networkd-wait-online.service
sudo systemctl mask systemd-networkd-wait-online.service
if [[ "$DISABLE_STUB" == "yes" ]]; then
echo "=== Step 5: Disable systemd-resolved stub, re-link /etc/resolv.conf ==="
sudo mkdir -p /etc/systemd/resolved.conf.d
sudo tee /etc/systemd/resolved.conf.d/10-disable-stub.conf > /dev/null <<EOF
[Resolve]
DNSStubListener=no
EOF
sudo systemctl restart systemd-resolved
sudo ln -sf /run/systemd/resolve/resolv.conf /etc/resolv.conf
echo "/etc/resolv.conf now points to resolved’s upstream resolv.conf"
fi
echo "=== Migration complete ==="
echo "Backed up netplan config to $BACKUP_NETPLAN_DIR"
echo "Interface: $NEW_IFACE; IP: $ADDR_CIDR; Gateway: $GW; DNS: $DNS"
echo "You may reboot now. After reboot verify with:"
echo " ip link show"
echo " nmcli device status"
echo " ip a && ip r"
echo " resolvectl status"
- path: /usr/local/bin/setup-ohmyzsh.sh
permissions: '0755'
content: |
#!/bin/bash
set -e
echo "=== Installing Oh My Zsh for each user under /home using TUNA mirror ==="
for dir in /home/*; do
[ -d "$dir" ] || continue
usr="$(basename "$dir")"
if [ "$usr" = "root" ]; then continue; fi
HOME_DIR="$dir"
echo "--- user: $usr (home: $HOME_DIR)"
sudo -u "$usr" sh -c '
export RUNZSH=no
export CHSH=no
export REMOTE="https://mirrors.tuna.tsinghua.edu.cn/git/ohmyzsh.git"
sh -c "$(curl -fsSL https://install.ohmyz.sh/)" "" --unattended
'
ZSHRC="$HOME_DIR/.zshrc"
if [ -f "$ZSHRC" ]; then
sudo -u "$usr" sed -i "s/^ZSH_THEME=.*/ZSH_THEME=\"ys\"/" "$ZSHRC"
echo " Set ZSH_THEME=\"ys\" for $usr"
sudo usermod -s /bin/zsh $usr
else
echo " Warning: $ZSHRC not found — install might have failed"
fi
done
- path: /etc/update-motd.d/99-shell-recommendation
permissions: '0755'
content: |
#!/bin/bash
# /etc/update-motd.d/99-shell-recommendation
# Try to guess user from last ssh login (may return "username(uid=1000)")
_logged=$(grep 'sshd' /var/log/auth.log 2>/dev/null \
| awk '/session opened for user/ {print $11}' \
| tail -1)
if [ -n "$_logged" ]; then
# Extract just the username before the "("
USR=$(echo "$_logged" | sed 's/\(.*\)(.*/\1/')
else
USR=""
fi
# If we got a candidate user, get its login shell; else leave blank
if [ -n "$USR" ] && getent passwd "$USR" >/dev/null 2>&1; then
USR_SHELL=$(getent passwd "$USR" | cut -d: -f7)
else
USR_SHELL=""
fi
# ANSI color codes
GREEN='\033[0;32m'
CYAN='\033[0;36m'
YELLOW='\033[1;33m'
RESET='\033[0m'
echo "" # blank line before notice
case "$USR_SHELL" in
*/bash)
echo -e "${YELLOW}Still using Bash?${RESET} Switch to Zsh + Oh My Zsh for a richer terminal experience (auto-completion, syntax highlighting, plugins, etc.) — ${GREEN}you’ll still be able to use your existing commands and scripts as before.${RESET}"
echo -e "還在用 Bash?建議改用 Zsh + Oh My Zsh,讓終端更強大(自動補全、語法高亮、插件等功能),且${GREEN}原有指令/腳本依然可用。${RESET}"
echo ""
echo -e "To switch: ${CYAN}chsh -s /bin/zsh${RESET} (or ${CYAN}sudo usermod -s /bin/zsh $USR${RESET})"
echo -e "要切換 Shell,請執行:${CYAN}chsh -s /bin/zsh${RESET}(或 ${CYAN}sudo usermod -s /bin/zsh $USR${RESET})"
echo ""
;;
*/zsh)
echo -e "${YELLOW}Using Zsh + Oh My Zsh for better productivity.${RESET} (auto-completion, plugins, syntax highlighting, etc.) — ${GREEN}existing Bash commands/scripts will still work.${RESET}"
echo -e "建議使用 Zsh + Oh My Zsh(具備自動補全、插件、語法高亮等功能)提升效率 — ${GREEN}您原有的 Bash 命令/腳本仍可正常使用。${RESET}"
echo ""
echo -e "To revert to Bash: ${CYAN}chsh -s /bin/bash${RESET} (or ${CYAN}sudo usermod -s /bin/bash $USR${RESET})"
echo -e "若要回復 Bash,請執行:${CYAN}chsh -s /bin/bash${RESET}(或 ${CYAN}sudo usermod -s /bin/bash $USR${RESET})"
echo ""
;;
*)
# No message if shell not bash or zsh (or user unknown)
;;
esac
exit 0
runcmd:
- [ bash, "/usr/local/bin/migrate-to-nm.sh" ]
- [ bash, /usr/local/bin/setup-ohmyzsh.sh ]
- systemctl set-default graphical.target
- reboot
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment