Last active
December 10, 2025 14:21
-
-
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)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #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