Last active
August 3, 2026 23:25
-
-
Save lazanet/dca0f48efc1b54901d29c3265f29d07f to your computer and use it in GitHub Desktop.
Script that sets up Ubuntu 26.04 to my liking
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
| #!/usr/bin/env -S bash -e | |
| set -o xtrace | |
| set -e | |
| WORK=${WORK-0} | |
| SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" | |
| # GraalVM CE version to install via sdkman (must be a full sdkman identifier). | |
| # Bump this as new releases land: `sdk list java | grep graalce`. | |
| JAVA_VERSION=25.0.2-graalce | |
| function set_gnome_configuration { | |
| # Configure power settings | |
| dconf write /org/gnome/desktop/session/idle-delay "uint32 0" | |
| dconf write /org/gnome/settings-daemon/plugins/power/sleep-inactive-battery-timeout 1800 | |
| dconf write /org/gnome/settings-daemon/plugins/power/sleep-inactive-ac-type "'nothing'" | |
| # Disable Gnome animations | |
| gsettings set org.gnome.desktop.interface enable-animations false | |
| # Dock settings | |
| gsettings set org.gnome.shell.extensions.dash-to-dock show-apps-at-top true | |
| dconf write /org/gnome/shell/extensions/dash-to-dock/dash-max-icon-size 24 | |
| dconf write /org/gnome/shell/extensions/dash-to-dock/multi-monitor true | |
| dconf write /org/gnome/shell/extensions/dash-to-dock/isolate-workspaces true | |
| dconf write /org/gnome/shell/extensions/dash-to-dock/isolate-monitors false | |
| dconf write /org/gnome/shell/extensions/tiling-assistant/enable-tiling-popup false | |
| # Add Sr1 EN Sr2 keyboard layouts | |
| dconf write /org/gnome/desktop/input-sources/sources "[('xkb', 'rs+latin'), ('xkb', 'us'), ('xkb', 'rs')]" | |
| # Use GB locale (uses metric + Monday is the first day of the week) | |
| # Generate it first; on a fresh 26.04 only C/POSIX exist, so update-locale would reject it | |
| sudo locale-gen en_GB.UTF-8 | |
| sudo update-locale LC_NUMERIC=en_GB.UTF-8 LC_TIME=en_GB.UTF-8 LC_MONETARY=en_GB.UTF-8 LC_PAPER=en_GB.UTF-8 LC_NAME=en_GB.UTF-8 LC_ADDRESS=en_GB.UTF-8 LC_TELEPHONE=en_GB.UTF-8 LC_MEASUREMENT=en_GB.UTF-8 LC_IDENTIFICATION=en_GB.UTF-8 | |
| # Enable Shift + Alt for switching keyboard layouts | |
| gsettings set org.gnome.desktop.wm.keybindings switch-input-source "['<Alt>Shift_L', 'XF86Keyboard']" | |
| gsettings set org.gnome.desktop.input-sources xkb-options "['grp_led:scroll', 'grp:alt_shift_toggle']" | |
| gsettings set org.gnome.desktop.wm.preferences button-layout ":minimize,maximize,close" | |
| # Change background | |
| sudo wget https://github.com/twinysam/ubuntu-wallpapers/raw/applied/ubuntu/artful/ubuntu_by_arman1992.jpg -O /usr/share/backgrounds/ubuntu_by_arman1992.jpg | |
| gsettings set org.gnome.desktop.background picture-uri "file:///usr/share/backgrounds/ubuntu_by_arman1992.jpg" | |
| gsettings set org.gnome.desktop.background picture-uri-dark "file:///usr/share/backgrounds/ubuntu_by_arman1992.jpg" | |
| # Enable dark theme | |
| gsettings set org.gnome.desktop.interface color-scheme 'prefer-dark' | |
| gsettings set org.gnome.desktop.interface gtk-theme 'Yaru-blue-dark' | |
| # Setup Ptyxis terminal theme (Ptyxis replaces GNOME Terminal as the default on 26.04) | |
| PROFILE=$(dconf read /org/gnome/Ptyxis/default-profile-uuid 2>/dev/null | tr -d "'") | |
| if [ -z "$PROFILE" ]; then | |
| PROFILE=$(uuidgen | tr -d '-') | |
| dconf write /org/gnome/Ptyxis/profile-uuids "['$PROFILE']" | |
| dconf write /org/gnome/Ptyxis/default-profile-uuid "'$PROFILE'" | |
| fi | |
| dconf write /org/gnome/Ptyxis/interface-style "'dark'" # force dark UI | |
| # Install and select a custom palette matching the old GNOME Terminal colors exactly | |
| # (bg #040504, fg #D3CFCD, classic GNOME Terminal ANSI colors) | |
| mkdir -p ~/.local/share/org.gnome.Ptyxis/palettes | |
| ptyxis --import-palette "$SCRIPT_DIR/gnome-terminal-classic.palette" \ | |
| || cp "$SCRIPT_DIR/gnome-terminal-classic.palette" ~/.local/share/org.gnome.Ptyxis/palettes/ | |
| dconf write /org/gnome/Ptyxis/Profiles/$PROFILE/palette "'gnome-terminal-classic'" | |
| dconf write /org/gnome/Ptyxis/Profiles/$PROFILE/opacity 0.8 # ~old 20% transparency | |
| # Setup gedit theme | |
| sudo apt -y install gedit | |
| gsettings set org.gnome.gedit.preferences.editor display-line-numbers true | |
| # gedit 48 (GTK4) replaced the single 'scheme' key with per-theme-variant keys | |
| gsettings set org.gnome.gedit.preferences.editor style-scheme-for-dark-theme-variant 'solarized-dark' | |
| gsettings set org.gnome.gedit.preferences.editor style-scheme-for-light-theme-variant 'solarized-light' | |
| dconf write /org/gnome/gedit/preferences/editor/tabs-size "uint32 4" | |
| # Setup gnome-text-editor theme | |
| dconf write /org/gnome/TextEditor/show-line-numbers true | |
| dconf write /org/gnome/TextEditor/style-variant "'follow'" | |
| dconf write /org/gnome/TextEditor/style-scheme "'solarized-dark'" | |
| dconf write /org/gnome/TextEditor/show-map true | |
| dconf write /org/gnome/TextEditor/restore-session true | |
| # Top bar tweaks | |
| gsettings set org.gnome.desktop.interface clock-show-seconds true | |
| gsettings set org.gnome.desktop.interface show-battery-percentage true | |
| dconf write /org/gnome/desktop/sound/allow-volume-above-100-percent true | |
| # Nautilus tweaks | |
| dconf write /org/gnome/nautilus/preferences/show-create-link true | |
| dconf write /org/gnome/nautilus/preferences/show-delete-permanently true | |
| dconf write /org/gnome/nautilus/preferences/date-time-format "'detailed'" | |
| # Configure workspaces | |
| gsettings set org.gnome.mutter dynamic-workspaces false | |
| gsettings set org.gnome.desktop.wm.preferences num-workspaces 2 | |
| gsettings set org.gnome.mutter workspaces-only-on-primary false | |
| gsettings set org.gnome.shell.extensions.dash-to-dock isolate-workspaces true | |
| gsettings set org.gnome.shell disable-extension-version-validation true | |
| # Needs a live gnome-shell session; don't abort the script if run headless | |
| gnome-extensions enable tiling-assistant@ubuntu.com || true | |
| } | |
| function gnome_extensions { | |
| sudo apt -y install curl git | |
| pushd $(mktemp -d) | |
| wget -O gnome-shell-extension-installer "https://github.com/brunelli/gnome-shell-extension-installer/raw/master/gnome-shell-extension-installer" | |
| chmod +x gnome-shell-extension-installer | |
| ./gnome-shell-extension-installer 1319 # https://extensions.gnome.org/extension/1319/gsconnect/ | |
| ./gnome-shell-extension-installer 4559 # https://extensions.gnome.org/extension/4559/quick-lang-switch/ | |
| popd | |
| # multi-monitors-add-on: clone once, update on re-run | |
| mkdir -p ~/.local/share/gnome-shell/extensions | |
| if [ ! -d ~/multi-monitors-add-on ]; then | |
| git clone https://github.com/lazanet/multi-monitors-add-on.git ~/multi-monitors-add-on | |
| else | |
| git -C ~/multi-monitors-add-on pull --ff-only || true | |
| fi | |
| ln -srfn ~/multi-monitors-add-on/multi-monitors-add-on@spin83 ~/.local/share/gnome-shell/extensions/multi-monitors-add-on@spin83 | |
| } | |
| function desnap { | |
| # Ubuntu 26.04 ships Firefox and the App Store as snaps. We want flatpak instead, | |
| # so ensure replacements exist (Firefox is installed in flatpak_packages, which runs | |
| # first), then remove every snap and pin snapd so it can't return. Idempotent: safe | |
| # to re-run after snapd is already gone. | |
| # Flatpak-friendly Software store, plus the firmware-update GUI. fwupd (the daemon | |
| # that actually applies firmware updates) is already a deb; only the "Firmware | |
| # Updater" GUI ships as a snap, so gnome-firmware keeps that capability. | |
| sudo apt -y install --install-suggests gnome-software | |
| sudo apt -y install gnome-software-plugin-flatpak gnome-firmware | |
| sudo apt -y purge gnome-software-plugin-snap || true | |
| # Pin snapd FIRST so it can never come back as a dependency. Writing the pin up | |
| # front means a later failure (e.g. a busy /snap mount making `rm` fail under | |
| # `set -e`) can't abort the run and leave the system UNpinned — where the next | |
| # `apt upgrade` could quietly pull snapd back via ubuntu-desktop's Recommends. | |
| printf 'Package: snapd\nPin: release a=*\nPin-Priority: -10\n' | sudo tee /etc/apt/preferences.d/nosnap.pref | |
| sudo apt-mark hold snapd || true | |
| # Remove the Firefox/Thunderbird transitional stub debs (they exist only to | |
| # install the corresponding snaps). | |
| sudo apt -y purge firefox thunderbird || true | |
| # Remove every installed snap (leaf snaps first, then bases/core/snapd). Bounded: | |
| # if a full pass removes nothing (a snap is genuinely stuck), break instead of | |
| # spinning forever. | |
| while [ -n "$(snap list 2>/dev/null | tail -n +2)" ]; do | |
| before=$(snap list 2>/dev/null | tail -n +2 | wc -l) | |
| for s in $(snap list 2>/dev/null | tail -n +2 | awk '{print $1}'); do | |
| sudo snap remove --purge "$s" || true | |
| done | |
| [ "$(snap list 2>/dev/null | tail -n +2 | wc -l)" -eq "$before" ] && break | |
| done | |
| sudo systemctl stop snapd.service snapd.socket 2>/dev/null || true | |
| sudo apt -y purge snapd || true | |
| sudo apt -y autoremove --purge | |
| # Unmount any leftover snap squashfs mounts before wiping the dirs, and guard the | |
| # rm with `|| true` so a stubborn mount can't abort the script (the pin is already | |
| # safely in place above). | |
| mount | awk '/\/snap|snapd/{print $3}' | sort -r | while read -r m; do sudo umount -l "$m" 2>/dev/null || true; done | |
| rm -rf ~/snap | |
| sudo rm -rf /snap /var/snap /var/lib/snapd /var/cache/snapd /usr/lib/snapd || true | |
| } | |
| function apt_packages { | |
| set -e | |
| sudo apt update | |
| sudo apt clean | |
| sudo apt -y autoremove | |
| # Enable multiverse (Steam lives there) and 32-bit architecture (Steam needs i386) | |
| sudo add-apt-repository -y multiverse | |
| sudo dpkg --add-architecture i386 | |
| # Add kisak-mesa repository | |
| # sudo add-apt-repository -y ppa:kisak/kisak-mesa | |
| # Add VSCode | |
| if ! command -v code >/dev/null 2>&1; then | |
| pushd $(mktemp -d) | |
| wget https://update.code.visualstudio.com/latest/linux-deb-x64/stable -O code.deb | |
| sudo apt -y install ./code.deb | |
| popd | |
| fi | |
| sudo apt update && sudo apt -y upgrade | |
| sudo apt -y install libgl1-mesa-dri:i386 mesa-vulkan-drivers mesa-vulkan-drivers:i386 | |
| sudo apt -y install curl gpg vlc gnome-snapshot fastfetch gnome-tweaks gnome-shell-extension-manager file-roller build-essential git code cifs-utils rar remmina sqlitebrowser baobab fzf mc ptyxis uuid-runtime | |
| if [ $WORK == "0" ]; then | |
| # Steam via Valve's official deb (sets up repo.steampowered.com + signing key | |
| # on install, so it self-updates; provides the real "Steam" launcher) | |
| if ! dpkg -s steam-launcher >/dev/null 2>&1; then | |
| pushd $(mktemp -d) | |
| wget https://repo.steampowered.com/steam/archive/stable/steam_latest.deb -O steam.deb | |
| sudo apt -y install ./steam.deb | |
| popd | |
| fi | |
| if ! dpkg -s teamviewer >/dev/null 2>&1; then | |
| pushd $(mktemp -d) | |
| wget https://download.teamviewer.com/download/linux/teamviewer_amd64.deb | |
| sudo apt -y install ./teamviewer_amd64.deb | |
| popd | |
| fi | |
| if ! dpkg -s megasync >/dev/null 2>&1; then | |
| pushd $(mktemp -d) | |
| wget https://mega.nz/linux/repo/xUbuntu_26.04/amd64/megasync-xUbuntu_26.04_amd64.deb -O mega.deb | |
| sudo apt -y install ./mega.deb | |
| popd | |
| fi | |
| fi | |
| if ! dpkg -s zoom >/dev/null 2>&1; then | |
| pushd $(mktemp -d) | |
| wget https://zoom.us/client/latest/zoom_amd64.deb | |
| sudo apt -y install ./zoom_amd64.deb | |
| popd | |
| fi | |
| } | |
| function flatpak_packages { | |
| flatpak remote-add --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo | |
| # Firefox as a flatpak (snap-free); installed before desnap removes the Firefox snap stub | |
| flatpak install -y flathub org.mozilla.firefox org.onlyoffice.desktopeditors com.jetbrains.IntelliJ-IDEA-Community | |
| if [ $WORK == "0" ]; then | |
| flatpak install -y com.jetbrains.PyCharm-Community com.google.AndroidStudio | |
| fi | |
| export XDG_DATA_DIRS="/var/lib/flatpak/exports/share:$XDG_DATA_DIRS" | |
| xdg-settings set default-web-browser org.mozilla.firefox.desktop || true | |
| } | |
| function set_term_configuration { | |
| if ! grep -qF "init_sys customizations" ~/.bashrc 2>/dev/null; then | |
| cat >> ~/.bashrc <<'EOF' | |
| # init_sys customizations | |
| export PS1="\[\033[01;34m\]\w\[\033[36m\]$(git branch 2> /dev/null | sed -e '/^[^*]/d' -e 's/* \(.*\)/ (\1)/')\[\033[00m\]$ " | |
| eval "$(fzf --bash)" | |
| fastfetch | |
| EOF | |
| fi | |
| } | |
| function add_dock_icons { | |
| # Add dock icons | |
| gsettings set org.gnome.shell favorite-apps "['org.mozilla.firefox.desktop', 'org.gnome.Nautilus.desktop', 'org.gnome.Software.desktop', 'org.gnome.Ptyxis.desktop', 'code.desktop', 'steam.desktop', 'com.teamviewer.TeamViewer.desktop', 'vlc.desktop', 'org.onlyoffice.desktopeditors.desktop', 'com.jetbrains.IntelliJ-IDEA-Community.desktop', 'com.jetbrains.PyCharm-Community.desktop', 'com.google.AndroidStudio.desktop']" | |
| } | |
| function install_sdkman { | |
| # Install sdkman! | |
| if [ ! -d "$HOME/.sdkman" ]; then | |
| curl -s "https://get.sdkman.io" | bash | |
| fi | |
| source "$HOME/.sdkman/bin/sdkman-init.sh" | |
| sdk install java "$JAVA_VERSION" | |
| sdk install maven | |
| sdk install gradle | |
| } | |
| apt_packages | |
| flatpak_packages | |
| desnap | |
| gnome_extensions | |
| set_gnome_configuration | |
| set_term_configuration | |
| add_dock_icons | |
| install_sdkman |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment