openSUSE Guide
Covering openSUSE Tumbleweed KDE
Philosophy: bleeding-edge rolling release with enterprise-grade testing (openQA), Btrfs snapshots for instant rollback, and modern tooling to maintain a fast-moving yet stable system. Tumbleweed updates ship as snapshots (batches of updates). (doc.opensuse.org)
Thanks to Snapper + Btrfs, you can roll back to an earlier system state if a snapshot introduces trouble—so it's worth becoming comfortable with Snapper early. (doc.opensuse.org)
0 — The Tumbleweed Golden Rule
- Always use
zypper dup(dist-upgrade) for updates on Tumbleweed—it tracks the rolling "distribution state" by adding/removing packages as required. (doc.opensuse.org) - For major upgrades (e.g., big KDE/graphics stack changes), run
zypper dupfrom a TTY (Ctrl+Alt+F1–F6), not inside the desktop session. - Monitor the openSUSE Factory mailing list for announcements about major updates requiring special handling (like the KDE Plasma 6 transition).
1 — System Setup & Repositories
1.1 — Verify default repositories
zypper lr -u
Standard Tumbleweed repos typically include:
- repo-oss: Main open source software repository
- repo-non-oss: Proprietary but freely distributable software
- repo-update: Emergency security fixes (often empty unless an emergency fix exists)
1.2 — Install OPI (OBS Package Installer)
OPI is a helper tool that simplifies installation from OBS (Open Build Service) and handles complex repository management automatically.
sudo zypper install opi
OPI makes installing codecs, drivers, and third-party software much easier with automatic repository handling.
1.3 — First system update (download in GUI, apply in TTY — safest)
Tumbleweed guidance explicitly supports "download in DE, install in TTY" workflows. (en.opensuse.org)
Why download-only in GUI?
- Major updates (like KDE Plasma, graphics drivers, or kernel) can cause issues if applied while the desktop is running
- Downloaded packages are cached and ready for safe installation in TTY
- Critical updates like Plasma 6 explicitly require TTY installation
Download packages (in GUI):
sudo zypper ref && sudo zypper dup -d
Apply safely (TTY):
- Switch to a TTY:
Ctrl+Alt+F1–F6 - Log in
- Run:
sudo zypper ref && sudo zypper dup
sudo reboot
Resilience tip: For long-running updates over SSH or unstable connections, use tmux or screen to prevent interruption if your session disconnects. (en.opensuse.org)
A reboot isn't always required—Zypper will tell you when it is. (en.opensuse.org)
Returning to GUI:
After completing updates in TTY, return to the graphical desktop by pressing Ctrl+Alt+F2 (or try F1, F7, F8—the exact key varies by system). (en.opensuse.org)
1.4 - Install zsh
sudo zypper install zsh && chsh -s $(which zsh)
2 — Essential Software Installation
2.1 — Development tools pattern
openSUSE uses "patterns" (meta-packages):
Verify the existence of patterns using:
zypper search -t pattern
Recommended Patterns:
sudo zypper install -t pattern 32bit apparmor base container_runtime devel_C_C++ devel_mono devel_python3 devel_web enhanced_base fonts imaging kde lamp_server multimedia office yast2_basis
2.2 — Application installation (TTY recommended for big installs)
sudo zypper install -y \
7zip \
bat \
bind-utils \
bottom \
broot \
chromium \
cmake \
curl \
dolphin-plugins \
earlyoom \
exfatprogs \
eza \
fastfetch \
fd \
ffmpeg \
ffmpegthumbs \
firewalld \
flatpak \
fnm \
fwupd \
fzf \
gdu \
git \
gnu_parallel \
gping \
gwenview \
httpie \
hwinfo \
ImageMagick \
inkscape \
iproute2 \
jq \
kate \
kde-cli-tools \
kdegraphics-thumbnailers \
kdenlive \
kdesvn \
kio-extras \
kompare \
krename \
krita \
lazygit \
libreoffice \
libwebp \
libwebp-tools \
lshw \
micro \
MozillaFirefox \
mpv \
mtr \
musescore \
ntfs-3g \
obs-studio \
okular \
opi \
pandoc \
partitionmanager \
pavucontrol \
php-composer \
php8 \
php8-cli \
pkgconf \
podman \
python3-pipx \
qbittorrent \
qt6-imageformats \
ripgrep \
smartmontools \
svgpart \
telegram-desktop \
vulkan-tools \
wayland-utils \
wget \
wine-staging \
winetricks \
wl-clipboard \
xdg-desktop-portal \
xdg-desktop-portal-kde \
xorg-x11-fonts \
yast2-firewall \
zoxide \
zsh
Note: It is recommended to
sudo rebootafter installing all these packages.
2.3 — Node.js Management Strategy
This guide uses system-managed Node.js via zypper for simplicity:
# Already installed in 2.2:
# nodejs-default npm-default
# Update Node.js:
sudo zypper dup # Updates all system packages including Node.js
Benefits of system packages:
- Automatic updates via zypper
- Integrated with system security updates
- No separate version manager needed
- Consistent across system
Alternative (not covered here): If you need multiple Node.js versions per project, consider NVM (Node Version Manager) or containerized development environments.
3 — Multimedia Codecs
3.1 — The Easy Way: opi codecs
Note: opi adds the Full Packman repository. While easiest, this increases the chance of dependency conflicts in the future compared to the "Essentials" method below.
opi codecs
What
opi codecsdoes: Adds Packman repository, performs a distribution upgrade from Packman with vendor changes allowed, and installs all necessary codecs. This is equivalent to the manual process but automated.
This automatically:
- Adds the appropriate Packman repository
- Switches relevant packages to Packman versions
- Handles vendor changes safely
- Installs all common multimedia codecs
That's it. Skip to Section 4 unless you want manual control.
Note: Packman is a frequent source of issues with updates. If you experience update conflicts, temporarily disable Packman before running zypper dup, then re-enable it afterward (see Section 12.3).
3.2 — Manual Method: Packman Repository (Advanced Users)
Why manual? If you want explicit control over repositories and vendor switching, or if opi fails.
Professional reality check: Third-party repos are not officially supported. Packman is commonly used for codecs but is a frequent source of update/vendor conflicts. The openSUSE SDB recommends using the Essentials repository when possible. (en.opensuse.org)
Option A (recommended): Packman Essentials — Minimal Conflict Risk
sudo zypper addrepo -cfp 90 \
'https://ftp.gwdg.de/pub/linux/misc/packman/suse/openSUSE_Tumbleweed/Essentials/' \
packman-essentials
sudo zypper refresh
Install codec set from Essentials:
sudo zypper install --allow-vendor-change --from packman-essentials \
ffmpeg gstreamer-plugins-{good,bad,ugly,libav} libavcodec-full vlc-codecs
Option B (power users): Full Packman — Higher Feature Set, Higher Conflict Risk
sudo zypper addrepo -cfp 90 \
'https://ftp.gwdg.de/pub/linux/misc/packman/suse/openSUSE_Tumbleweed/' \
packman
sudo zypper refresh
sudo zypper dup --from packman --allow-vendor-change
3.3 — Vendor Equivalence (Advanced)
Do not globally disable vendor protection; libzypp warns it can "easily damage your system." (opensuse.github.io)
Instead, define vendor equivalence:
sudo mkdir -p /etc/zypp/vendors.d
sudoedit /etc/zypp/vendors.d/99-vendors.conf
Add:
[main]
vendors = suse,opensuse,obs://build.opensuse.org/multimedia:libs,packman
This tells zypper these vendors can be considered equivalent.
4 — Fonts
4.1 — System fonts
sudo zypper install -y adobe-sourcecodepro-fonts dejavu-fonts fantasque-sans-mono-fonts fira-code-fonts fontawesome-fonts google-carlito-fonts google-droid-fonts google-noto-coloremoji-fonts google-noto-sans-fonts google-noto-serif-fonts google-noto-sans-cjk-fonts google-opensans-fonts google-roboto-fonts ibm-plex-fonts jetbrains-mono-fonts liberation-fonts liberation2-fonts mozilla-fira-fonts powerline-fonts ubuntu-fonts && fc-cache -fv
4.2 — Microsoft Core Fonts
sudo zypper install -y fetchmsttfonts && sudo /usr/sbin/fetchmsttfonts
4.3 — Nerd Fonts (manual installation)
Download from: https://www.nerdfonts.com/font-downloads
Install to:
- User:
~/.local/share/fonts/ - System-wide:
/usr/share/fonts/
Rebuild cache after installation:
fc-cache -fv
5 — System Protection
5.1 — OOM protection
earlyoom prevents system freezes by terminating memory-hogging processes before complete unresponsiveness:
sudo systemctl enable --now earlyoom
5.2 — Firewall (firewalld — openSUSE native)
Principle: Use openSUSE's native, integrated firewall solution.
firewalld is the default firewall direction on openSUSE (SuSEfirewall2 is deprecated). (en.opensuse.org)
Enable and configure:
# Enable firewall
sudo systemctl enable --now firewalld
# Check status
sudo firewall-cmd --state
sudo firewall-cmd --get-active-zones
sudo firewall-cmd --zone=public --list-all
Common firewall rules
SSH (optional, for remote access):
sudo firewall-cmd --permanent --add-service=ssh
sudo firewall-cmd --reload
KDE Connect (phone integration):
sudo firewall-cmd --permanent --add-port=1714-1764/tcp
sudo firewall-cmd --permanent --add-port=1714-1764/udp
sudo firewall-cmd --reload
Verify changes:
sudo firewall-cmd --zone=public --list-all
GUI Management (Recommended for Beginners)
Use YaST Firewall for graphical configuration:
# Launch YaST Firewall
sudo yast2 firewall
# Or from KDE menu: YaST → Security and Users → Firewall
Note: firewall-cmd uses runtime rules by default; use --permanent + reload for persistent changes. (doc.opensuse.org)
6 — Flatpak & Firmware
6.1 — Flatpak (Flathub)
Add Flathub repository for access to thousands of applications:
sudo flatpak remote-add --if-not-exists flathub https://dl.flathub.org/repo/flathub.flatpakrepo
flatpak update
Manage Flatpaks graphically via Plasma Discover (KDE's software center).
6.2 — Firmware updates
Keep hardware firmware up-to-date via fwupd:
sudo fwupdmgr refresh || true
sudo fwupdmgr get-updates || true
sudo fwupdmgr update || true
7 — CPU Microcode & Firmware
CPU microcode updates provide critical security patches and bug fixes.
7.1 — Intel systems
sudo zypper install -y ucode-intel kernel-firmware-intel
sudo reboot
7.2 — AMD systems
sudo zypper install -y ucode-amd kernel-firmware-amdgpu
sudo reboot
7.3 — Additional firmware (as needed)
# Install all firmware (recommended for most users)
sudo zypper install -y kernel-firmware-all
# Or specific firmware:
# kernel-firmware-iwlwifi (Intel WiFi)
# kernel-firmware-realtek (Realtek network/WiFi)
# kernel-firmware-atheros (Atheros WiFi)
Important: Reboot after microcode installation to load the new microcode.
8 — Understanding Snapper & Btrfs Snapshots
Important: Snapshots are automatically enabled for the root partition
/if it is larger than approximately 16 Gigabytes. If your root partition is smaller, snapshots may not be enabled by default to prevent filling the disk.
By default, Tumbleweed uses Btrfs and enables snapshots for / (if / > 16 GB). (doc.opensuse.org)
Critical: /home is not snapshotted by default—Snapper is not a backup solution for personal files. (doc.opensuse.org)
8.1 — Verify Snapper integration (recommended)
These packages aren't always installed by default: (en.opensuse.org)
rpm -q snapper snapper-zypp-plugin grub2-snapper-plugin yast2-snapper
If missing, install:
sudo zypper install snapper snapper-zypp-plugin grub2-snapper-plugin yast2-snapper
8.2 — View snapshots
sudo snapper list
Output shows:
- Snapshot numbers
- Type (Single, Pre, Post)
- Date/time
- Description
- Important flag
8.3 — How snapshots work
On Tumbleweed, pre/post snapshots for zypper/YaST are the main default. Timeline snapshots may be disabled by default; confirm in /etc/snapper/configs/root (TIMELINE_CREATE=).
- Pre/Post pairs: Created automatically for zypper/YaST operations (doc.opensuse.org)
- Important snapshots: Marked when critical components (kernel, etc.) are updated
- Timeline snapshots: Hourly (kept 10 hours), daily (kept 10 days), monthly (kept 10 months)
- Automatic cleanup: Configured by default to prevent disk space issues
Excluded from snapshots (default layout): /home, /opt, /usr/local, /srv, /tmp, /var (doc.opensuse.org)
You can verify your actual Btrfs subvolume layout with:
sudo btrfs subvolume list /
8.4 — Rollback from boot menu (recommended method)
Safest rollback method:
- Reboot your system
- At GRUB menu: select "Start Bootloader from a read-only snapshot"
- Choose the desired snapshot (most recent listed first)
- Boot normally and log in
- Execute rollback:
sudo snapper rollback
sudo reboot
If the GRUB snapshot menu is missing, verify grub2-snapper-plugin is installed. (en.opensuse.org)
sudo grub2-mkconfig -o /boot/grub2/grub.cfg
8.5 — Rollback from running system
# List snapshots to find the target
sudo snapper list
# Rollback to specific snapshot
sudo snapper rollback <snapshot-number>
# Reboot to apply
sudo reboot
8.6 — Compare snapshots
See what changed between snapshots:
# Compare two snapshots
sudo snapper diff <old-snapshot>..<new-snapshot>
# Compare current system to a snapshot
sudo snapper diff <snapshot-number>..0
8.7 — Manage disk space
Monitor and clean up snapshots:
# Check Btrfs usage
sudo btrfs filesystem usage /
df -h /
# List snapshots
sudo snapper list
# Delete specific snapshot
sudo snapper delete <snapshot-number>
# Delete range
sudo snapper delete <start>-<end>
# Trigger automatic cleanup
sudo snapper cleanup timeline
sudo snapper cleanup number
Important: When manually deleting, delete oldest snapshots first—they typically occupy more space due to accumulated changes. Never delete the currently running snapshot or snapshot 0.
9 — Manual Software Installation
9.1 — Development Tools
Visual Studio Code:
sudo rpm --import https://packages.microsoft.com/keys/microsoft.asc
sudo zypper addrepo https://packages.microsoft.com/yumrepos/vscode vscode
sudo zypper refresh
sudo zypper install code
- .NET SDK: https://learn.microsoft.com/en-us/dotnet/core/install/linux-opensuse
- GDevelop: https://gdevelop.io/download
- Google Antigravity: https://antigravity.google/download/linux
- JetBrains Toolbox: https://www.jetbrains.com/toolbox-app/
- Unity Hub: https://docs.unity3d.com/hub/manual/InstallHub.html#install-hub-linux
9.2 - Other Tools
- Quarto: https://quarto.org/docs/get-started/
- GitFiend: https://gitfiend.com/
- RustDesk: https://github.com/rustdesk/rustdesk/releases/
10 — ZSH Configuration (openSUSE Tumbleweed)
Comprehensive ZSH configuration with safe update workflows, system diagnostics, web/dev utilities, and Tumbleweed-specific optimizations.
Append the following to ~/.zshrc:
# ~/.zshrc
# ============================================================================
# openSUSE Tumbleweed (KDE Plasma) - Zsh Configuration
# ============================================================================
# Custom configuration for openSUSE Tumbleweed with:
# - Safe update workflows (zypper dup + Snapper-aware)
# - System diagnostics and verification
# - Web/dev utilities (Docusaurus + Astro.js + Node.js tooling)
# - WordPress development (WP-CLI integration)
#
# All custom commands/functions prefixed with 'x' for easy discovery.
#
# PHILOSOPHY:
# - Tumbleweed is rolling: always use `zypper dup`
# - Python is externally managed: use venvs or pipx
# - Prefer openSUSE-native tools (firewalld, YaST, zypper)
# - Node.js via system packages (nodejs-default/npm-default)
# ============================================================================
# ============================================================================
# QUICK REFERENCE - ALL AVAILABLE COMMANDS
# ============================================================================
#
# DNS MANAGEMENT
# dns_status [options] - Comprehensive DNS configuration checker
# dns_s - Quick DNS status check
# dns_v - Verbose DNS status
# dns_l - DNS status with leak tests
#
# SYSTEM UPDATES & MAINTENANCE
# xupdate - Safe system update (GUI-aware: download-only in GUI)
# xdup - Quick alias: sudo zypper ref && sudo zypper dup
# xps - Show processes needing restart (zypper ps -s)
# xneedreboot - Check if reboot recommended (kernel/services check)
# xclean - Clean zypper caches + show unneeded packages
# xclean_zsh - Clear Zsh history file
# xverify - System health check (Snapper, firewall, services, Btrfs)
# xmaid - Update + Clean
# xsupermaid - Update + Clean + Clear History
# xuoff - Update + Clean + Poweroff
#
# SNAPSHOTS (BTRFS/SNAPPER)
# xsnapshots - List Btrfs snapshots
# xrollback <num> - Roll back to snapshot (then reboot to apply)
# xsnapdiff <a> <b> - Show differences between snapshots
#
# BTRFS MAINTENANCE
# xscrub - Check Btrfs filesystem for errors
# xbalance - Balance Btrfs (reclaim space, use cautiously)
#
# FIREWALL (firewalld)
# xfw_status - Show firewall status and rules
# xfw_kdeconnect - Open KDE Connect ports (1714-1764)
# xfw_ssh - Open SSH service permanently
#
# FILE & EDITOR OPERATIONS
# xlock <file> - Make file immutable (chattr +i)
# xunlock <file> - Make file mutable (chattr -i)
# xedit <file(s)> - Smart editor (auto-uses sudoedit if needed)
# xedit_hosts - Edit /etc/hosts with sudoedit
# xedit_zsh - Edit ~/.zshrc
# xcombine_md_rec - Combine Markdown files recursively
# xconvert_md - Convert Markdown using Quarto
# xhr - Print horizontal rule separator
# xclear - Full terminal reset
#
# PACKAGE MANAGEMENT (zypper)
# xinstall <pkgs...> - Install packages
# xreinstall <pkgs...> - Force reinstall packages
# xremove <pkgs...> - Remove packages (use --clean-deps for deps)
# xsearch <term> - Search packages (installed + available)
# xrepos - Show repositories with priorities
#
# IMAGE OPTIMIZATION
# xoptimize_img - Optimize images and convert to webp
# xoptimize_rec_img - Optimize images recursively
#
# DEVELOPMENT & WEB
# xwget <url> - Mirror/download entire website
# xserve [dir] [port] - Serve static files (auto-detects build/dist)
# xserver [port] - Python HTTP server (default: 8080)
# xpyenv_activate - Create/activate Python venv (./venv)
# xenv - Create/activate Python venv (quick alias)
# xpipx_install <pkg> - Install Python CLI app via pipx
# xbun_build - Update + build JS/TS bun bundled project (full workflow)
# xnpm_update - Update npm packages (uses npm-check-updates)
# jcurl - Pretty-print JSON from curl using jq
#
# ASTRO.JS WORKFLOWS
# xastro_dev - Start dev server (no clean/build)
# xastro_live - Clean + upgrade + build + dev server
# xastro_preview - Start preview server (no build)
# xastro_build - Clean + upgrade + install + lint + typecheck + build + preview
# xastro_clean - Clean build artifacts (.astro, dist)
# xastro_update - Update dependencies with npm-check-updates
# xastro_check - Project health check & outdated packages
# xastro_quick - Update + install + lint + typecheck + build + preview
# xastro_nuke - Nuclear: delete all + reinstall + lint + typecheck + build + preview
#
# WORDPRESS DEVELOPMENT
# xwp - WP-CLI wrapper (checks for local wp or system wp)
# xwp_update - Update WP-CLI to latest version
#
# SYSTEM DIAGNOSTICS
# xinfo - Comprehensive system info (hardware, graphics, snapshots)
# xbaloo_reset - Reset KDE Baloo file indexer
#
# OPTIONAL APP LAUNCHERS
# xlampp - Launch XAMPP manager (if installed)
#
# ============================================================================
# ============================================================================
# INITIAL CONFIGURATION
# ============================================================================
# Enable extended pattern matching (globbing) features
setopt extendedglob
# ============================================================================
# ENVIRONMENT & PATH
# ============================================================================
# Ensure unique PATH entries (Zsh native)
typeset -U path PATH
# Add user bin directories
path=(
"$HOME/.local/bin"
"$HOME/bin"
$path
)
# Remove non-existent directories from PATH
# Zsh glob qualifier: (N-/) = Null_glob, directories only
path=(${^path}(N-/))
# bat pager configuration
export PAGER='bat'
# ============================================================================
# BUN INITIALIZATION
# ============================================================================
# bun
export BUN_INSTALL="${BUN_INSTALL:-$HOME/.bun}"
export PATH="$BUN_INSTALL/bin:$PATH"
# bun completions
[ -s "$BUN_INSTALL/_bun" ] && source "$BUN_INSTALL/_bun"
# ============================================================================
# HELPER FUNCTIONS (INTERNAL USE)
# ============================================================================
# _xget_editor - Detect available editor (respects $VISUAL/$EDITOR)
# Returns editor command as array via Zsh's $reply variable
_xget_editor() {
emulate -L zsh
local user_ed="${VISUAL:-${EDITOR:-}}"
# User-configured editor
if [[ -n "$user_ed" ]]; then
reply=(${(z)user_ed})
return 0
fi
# Fallback editor detection (KDE/general purpose)
local e
for e in kate micro nano vim vi; do
if (( $+commands[$e] )); then
reply=("$e")
return 0
fi
done
return 1
}
# _xrequire_cmd - Check if command exists, fail gracefully if not
_xrequire_cmd() {
emulate -L zsh
if (( ! $+commands[$1] )); then
print -u2 "✗ Missing command: $1"
return 1
fi
}
# xhr - Print horizontal rule separator (adapts to terminal width)
xhr() {
emulate -L zsh
print -r -- ${(l:${COLUMNS:-60}::─:)}
}
# ============================================================================
# DNS MANAGEMENT
# ============================================================================
dns_status() {
local interface=""
local verbose=false
local run_leak_test=false
# Parse arguments
while [[ $# -gt 0 ]]; do
case "$1" in
-i|--interface)
interface="$2"
shift 2
;;
-v|--verbose)
verbose=true
shift
;;
-l|--leak-test)
run_leak_test=true
shift
;;
-h|--help)
print "Usage: dns-status [OPTIONS]"
print ""
print "Options:"
print " -i, --interface IFACE Specify network interface for tcpdump tests"
print " -v, --verbose Show detailed output"
print " -l, --leak-test Run tcpdump leak tests (requires sudo)"
print " -h, --help Show this help message"
print ""
print "Examples:"
print " dns-status # Basic status check"
print " dns-status -v # Verbose output"
print " dns-status -l -i enp42s0 # Run leak tests on specific interface"
return 0
;;
*)
print "Unknown option: $1"
print "Use -h for help"
return 1
;;
esac
done
# Colors for output
local RED='\033[0;31m'
local GREEN='\033[0;32m'
local YELLOW='\033[0;33m'
local BLUE='\033[0;34m'
local CYAN='\033[0;36m'
local BOLD='\033[1m'
local NC='\033[0m' # No Color
# Helper functions
_header() {
print ""
print "${BOLD}${BLUE}══════════════════════════════════════════════════════════════${NC}"
print "${BOLD}${BLUE} $1${NC}"
print "${BOLD}${BLUE}══════════════════════════════════════════════════════════════${NC}"
}
_subheader() {
print ""
print "${BOLD}${CYAN}▶ $1${NC}"
print "${CYAN}──────────────────────────────────────────${NC}"
}
_success() {
print "${GREEN}✓${NC} $1"
}
_warning() {
print "${YELLOW}⚠${NC} $1"
}
_error() {
print "${RED}✗${NC} $1"
}
_info() {
print "${BLUE}ℹ${NC} $1"
}
# Auto-detect interface if not specified
if [[ -z "$interface" ]]; then
interface=$(ip route get 1.1.1.1 2>/dev/null | grep -oP 'dev \K\S+' | head -1)
fi
#═══════════════════════════════════════════════════════════════
# SECTION 1: systemd-resolved Status
#═══════════════════════════════════════════════════════════════
_header "1. SYSTEMD-RESOLVED STATUS"
_subheader "Service Status"
if systemctl is-active --quiet systemd-resolved; then
_success "systemd-resolved is running"
else
_error "systemd-resolved is NOT running"
print " Run: sudo systemctl enable --now systemd-resolved"
fi
_subheader "Stub Resolver Configuration"
local resolv_target=$(readlink -f /etc/resolv.conf 2>/dev/null)
print " /etc/resolv.conf → $resolv_target"
if [[ "$resolv_target" == "/run/systemd/resolve/stub-resolv.conf" ]]; then
_success "Correctly configured (stub resolver)"
elif [[ "$resolv_target" == "/run/systemd/resolve/resolv.conf" ]]; then
_warning "Using resolv.conf (apps may bypass DoT)"
print " Recommended: sudo ln -sf /run/systemd/resolve/stub-resolv.conf /etc/resolv.conf"
else
_error "Non-standard configuration (DoT may be bypassed)"
print " Run: sudo ln -sf /run/systemd/resolve/stub-resolv.conf /etc/resolv.conf"
fi
_subheader "Resolver Details"
resolvectl status 2>/dev/null | head -20
if $verbose; then
_subheader "Full Resolver Status"
resolvectl status
fi
#═══════════════════════════════════════════════════════════════
# SECTION 2: DNS Configuration Analysis
#═══════════════════════════════════════════════════════════════
_header "2. DNS CONFIGURATION ANALYSIS"
_subheader "Current DNS Servers"
local dns_servers=$(resolvectl status 2>/dev/null | grep -i "DNS Server" | head -5)
if [[ -n "$dns_servers" ]]; then
print "$dns_servers"
# Check for hostname suffix (required for DoT)
if print "$dns_servers" | grep -q '#'; then
_success "DoT hostname suffix detected (certificate validation enabled)"
else
_warning "No DoT hostname suffix found"
print " Example format: 1.1.1.1#cloudflare-dns.com"
fi
else
_error "No DNS servers configured"
fi
_subheader "DNS Security Protocols"
local protocols=$(resolvectl status 2>/dev/null | grep -iE "^\s*(DNS Over TLS|DNSSEC|Protocols)" | head -5)
if [[ -n "$protocols" ]]; then
print "$protocols"
# Check DNSOverTLS
if print "$protocols" | grep -qi "DNSOverTLS.*yes\|+DNSOverTLS"; then
_success "DNS over TLS (DoT) is ENABLED"
else
_warning "DNS over TLS may not be enabled"
fi
# Check DNSSEC
if print "$protocols" | grep -qi "DNSSEC.*yes\|DNSSEC.*supported"; then
_success "DNSSEC validation is ENABLED"
else
_warning "DNSSEC may not be enabled"
fi
fi
_subheader "DNS Domain Routing"
local domains=$(resolvectl status 2>/dev/null | grep -i "DNS Domain" | head -3)
if [[ -n "$domains" ]]; then
print "$domains"
if print "$domains" | grep -q '~\.'; then
_success "All DNS routed through configured servers (~.)"
fi
fi
_subheader "Configuration File"
local config_file="/etc/systemd/resolved.conf.d/secure-dns.conf"
if [[ -f "$config_file" ]]; then
_success "Drop-in config exists: $config_file"
if $verbose; then
print ""
cat "$config_file"
fi
elif [[ -f "/etc/systemd/resolved.conf" ]]; then
_info "Using main config: /etc/systemd/resolved.conf"
if $verbose; then
print ""
grep -v '^#' /etc/systemd/resolved.conf | grep -v '^$'
fi
fi
#═══════════════════════════════════════════════════════════════
# SECTION 3: Network Configuration
#═══════════════════════════════════════════════════════════════
_header "3. NETWORK CONFIGURATION"
_subheader "Active Network Interfaces"
if [[ -n "$interface" ]]; then
_info "Detected/specified interface: $interface"
fi
ip -brief addr show 2>/dev/null | grep -E "^(en|eth|wl)" | head -5
_subheader "IPv6 Status"
local ipv6_disabled=$(cat /proc/sys/net/ipv6/conf/all/disable_ipv6 2>/dev/null)
if [[ "$ipv6_disabled" == "0" ]]; then
_success "IPv6 is ENABLED"
# Check for IPv6 addresses
local ipv6_addrs=$(ip -6 addr show scope global 2>/dev/null | grep inet6 | head -2)
if [[ -n "$ipv6_addrs" ]]; then
_success "Global IPv6 address(es) found"
if $verbose; then
print "$ipv6_addrs"
fi
else
_warning "No global IPv6 addresses (ISP may not support IPv6)"
fi
else
_info "IPv6 is DISABLED"
fi
_subheader "NetworkManager Status"
if systemctl is-active --quiet NetworkManager; then
_success "NetworkManager is running"
print ""
print " Active connections:"
nmcli -t connection show --active 2>/dev/null | while IFS=: read -r name uuid type device; do
print " • $name ($type) on $device"
done
# Check NetworkManager DNS mode
local nm_dns_mode=$(grep -r "dns=" /etc/NetworkManager/conf.d/ 2>/dev/null | head -1)
if [[ -n "$nm_dns_mode" ]]; then
if print "$nm_dns_mode" | grep -q "systemd-resolved"; then
_success "NetworkManager configured to use systemd-resolved"
else
_info "NetworkManager DNS mode: $nm_dns_mode"
fi
else
_warning "No explicit NetworkManager DNS configuration found"
print " Consider adding: /etc/NetworkManager/conf.d/10-dns-systemd-resolved.conf"
fi
if $verbose; then
print ""
print " Connection DNS settings:"
local active_conn=$(nmcli -t -f NAME connection show --active | head -1)
if [[ -n "$active_conn" ]]; then
nmcli connection show "$active_conn" 2>/dev/null | grep -iE "(dns|ignore-auto-dns)" | head -10
fi
fi
elif systemctl is-active --quiet wicked; then
_info "Using wicked (not NetworkManager)"
else
_warning "Neither NetworkManager nor wicked detected"
fi
#═══════════════════════════════════════════════════════════════
# SECTION 4: DNS Resolution Tests
#═══════════════════════════════════════════════════════════════
_header "4. DNS RESOLUTION TESTS"
_subheader "Basic Resolution Test"
resolvectl flush-caches 2>/dev/null
local test_domain="cloudflare.com"
local query_result=$(resolvectl query "$test_domain" 2>&1)
if print "$query_result" | grep -qE "^$test_domain"; then
_success "DNS resolution working for $test_domain"
print "$query_result" | head -5
else
_error "DNS resolution FAILED for $test_domain"
print "$query_result"
fi
_subheader "DNSSEC Validation Test"
local dnssec_result=$(resolvectl query cloudflare.com 2>&1)
if print "$dnssec_result" | grep -qi "authenticated.*yes"; then
_success "DNSSEC validation working (authenticated: yes)"
else
_warning "DNSSEC authentication not confirmed"
if $verbose; then
print "$dnssec_result" | grep -i auth
fi
fi
_subheader "Encrypted Transport Check"
local transport_result=$(resolvectl query duckduckgo.com 2>&1)
if print "$transport_result" | grep -qi "encrypted.*yes\|local or encrypted.*yes"; then
_success "DNS queries using encrypted transport"
else
_info "Encrypted transport status unclear from output"
fi
if $verbose; then
_subheader "DNSSEC Failure Test (should fail)"
print " Testing dnssec-failed.org (intentionally broken DNSSEC)..."
local fail_result=$(resolvectl query dnssec-failed.org 2>&1)
if print "$fail_result" | grep -qiE "SERVFAIL|failed|error"; then
_success "Correctly rejected invalid DNSSEC"
else
_warning "Invalid DNSSEC was not rejected"
fi
print "$fail_result" | head -3
fi
#═══════════════════════════════════════════════════════════════
# SECTION 5: Port 53 Listener Check
#═══════════════════════════════════════════════════════════════
_header "5. LOCAL DNS LISTENER CHECK"
_subheader "Processes Listening on Port 53"
local port53=$(sudo ss -tulpn 2>/dev/null | grep ':53' || ss -tulpn 2>/dev/null | grep ':53')
if [[ -n "$port53" ]]; then
print "$port53"
if print "$port53" | grep -q "systemd-resolve"; then
_success "Only systemd-resolved listening on port 53"
elif print "$port53" | grep -qE "dnsmasq|unbound|bind|named"; then
_warning "Other DNS service detected (may conflict)"
fi
else
_info "Could not check port 53 listeners (may need sudo)"
fi
#═══════════════════════════════════════════════════════════════
# SECTION 6: Leak Tests (Optional)
#═══════════════════════════════════════════════════════════════
if $run_leak_test; then
_header "6. DNS LEAK TESTS (tcpdump)"
if [[ -z "$interface" ]]; then
_error "No interface detected. Specify with -i <interface>"
return 1
fi
if ! command -v tcpdump &>/dev/null; then
_error "tcpdump not installed"
print " Install: sudo zypper install tcpdump"
return 1
fi
_subheader "Plaintext DNS Leak Test (Port 53)"
_info "Monitoring for unencrypted DNS on $interface..."
resolvectl flush-caches 2>/dev/null
# Run tcpdump in background
local tcpdump_out=$(mktemp)
sudo timeout 8 tcpdump -ni "$interface" 'port 53' -c 10 2>&1 > "$tcpdump_out" &
local tcpdump_pid=$!
sleep 2
resolvectl query www.cloudflare.com &>/dev/null
resolvectl query github.com &>/dev/null
wait $tcpdump_pid 2>/dev/null
local packets=$(grep -oP '\d+(?= packets captured)' "$tcpdump_out" | head -1)
packets=${packets:-0}
if [[ "$packets" -eq 0 ]]; then
_success "No plaintext DNS leaks detected (0 packets on port 53)"
else
_error "PLAINTEXT DNS DETECTED! ($packets packets on port 53)"
cat "$tcpdump_out"
fi
rm -f "$tcpdump_out"
_subheader "Encrypted DNS Test (Port 853)"
_info "Monitoring for DoT traffic on $interface..."
resolvectl flush-caches 2>/dev/null
local tcpdump_out2=$(mktemp)
sudo timeout 8 tcpdump -ni "$interface" 'tcp port 853' -c 10 2>&1 > "$tcpdump_out2" &
local tcpdump_pid2=$!
sleep 2
resolvectl query www.cloudflare.com &>/dev/null
resolvectl query duckduckgo.com &>/dev/null
wait $tcpdump_pid2 2>/dev/null
local packets2=$(grep -oP '\d+(?= packets captured)' "$tcpdump_out2" | head -1)
packets2=${packets2:-0}
if [[ "$packets2" -gt 0 ]]; then
_success "Encrypted DoT traffic detected ($packets2 packets on port 853)"
else
_warning "No DoT traffic captured (may be cached or using different path)"
fi
rm -f "$tcpdump_out2"
fi
#═══════════════════════════════════════════════════════════════
# SECTION 7: Summary
#═══════════════════════════════════════════════════════════════
_header "7. QUICK SUMMARY"
print ""
print " ${BOLD}Service Status${NC}"
systemctl is-active --quiet systemd-resolved && print " systemd-resolved: ${GREEN}running${NC}" || print " systemd-resolved: ${RED}stopped${NC}"
systemctl is-active --quiet NetworkManager && print " NetworkManager: ${GREEN}running${NC}" || print " NetworkManager: ${YELLOW}not running${NC}"
print ""
print " ${BOLD}Security Features${NC}"
resolvectl status 2>/dev/null | grep -qi "DNSOverTLS.*yes\|+DNSOverTLS" && print " DNS over TLS: ${GREEN}enabled${NC}" || print " DNS over TLS: ${YELLOW}check config${NC}"
resolvectl status 2>/dev/null | grep -qi "DNSSEC.*yes\|DNSSEC.*supported" && print " DNSSEC: ${GREEN}enabled${NC}" || print " DNSSEC: ${YELLOW}check config${NC}"
print ""
print " ${BOLD}Configuration${NC}"
[[ "$(readlink -f /etc/resolv.conf)" == "/run/systemd/resolve/stub-resolv.conf" ]] && print " Stub resolver: ${GREEN}correct${NC}" || print " Stub resolver: ${RED}incorrect${NC}"
[[ -f "/etc/systemd/resolved.conf.d/secure-dns.conf" ]] && print " Drop-in config: ${GREEN}present${NC}" || print " Drop-in config: ${YELLOW}not found${NC}"
print ""
print " ${BOLD}Detected Interface${NC}: ${interface:-unknown}"
if ! $run_leak_test; then
print ""
_info "Run with -l flag to perform tcpdump leak tests"
print " Example: dns-status -l -i $interface"
fi
print ""
}
# ============================================================================
# FILE MANAGEMENT
# ============================================================================
# xlock - Make file immutable (prevents deletion/modification)
xlock() {
emulate -L zsh
if [[ -z "${1:-}" ]]; then
print -u2 "Usage: xlock <file>"
return 1
fi
[[ -e "$1" ]] || { print -u2 "xlock: file not found: $1"; return 1; }
sudo chattr +i -- "$1"
print "✓ Locked: $1"
}
# xunlock - Make file mutable (allows deletion/modification)
xunlock() {
emulate -L zsh
if [[ -z "${1:-}" ]]; then
print -u2 "Usage: xunlock <file>"
return 1
fi
[[ -e "$1" ]] || { print -u2 "xunlock: file not found: $1"; return 1; }
sudo chattr -i -- "$1"
print "✓ Unlocked: $1"
}
# xedit - Smart editor (auto-detects if sudo is needed)
# Checks file permissions and location to determine if sudoedit is required
xedit() {
emulate -L zsh
if (( ! $# )); then
print -u2 "Usage: xedit <file> [more files...]"
return 1
fi
local -a ed
_xget_editor || { print -u2 "✗ No editor found. Set EDITOR or VISUAL."; return 1; }
ed=("${reply[@]}")
# Check if any file requires sudo
local use_sudoedit=0 f
for f in "$@"; do
# Files in /etc always need sudo
if [[ "$f" == /etc/* ]]; then
use_sudoedit=1
break
fi
# Existing files that aren't writable
if [[ -e "$f" && ! -w "$f" ]]; then
use_sudoedit=1
break
fi
# New files in non-writable directories
if [[ ! -e "$f" && "$f" == */* && ! -w "${f:h}" ]]; then
use_sudoedit=1
break
fi
done
if (( use_sudoedit )); then
SUDO_EDITOR="${(j: :)${(q)ed[@]}}" sudoedit -- "$@"
else
"${ed[@]}" -- "$@"
fi
}
# xedit_hosts - Edit /etc/hosts with sudoedit
xedit_hosts() {
emulate -L zsh
local -a ed
_xget_editor || { print -u2 "✗ No editor found. Set EDITOR or VISUAL."; return 1; }
ed=("${reply[@]}")
SUDO_EDITOR="${(j: :)${(q)ed[@]}}" sudoedit /etc/hosts
}
# xedit_zsh - Edit ~/.zshrc with detected editor
xedit_zsh() {
emulate -L zsh
local -a ed
_xget_editor || { print -u2 "✗ No editor found. Set EDITOR or VISUAL."; return 1; }
ed=("${reply[@]}")
"${ed[@]}" "$HOME/.zshrc"
}
# xcombine_md_rec - Combine Markdown files recursively
xcombine_md_rec() {
find . -name "*.md" -sort | xargs pandoc -s -o combined.md
}
# xconvert_md - Convert Markdown using Quarto
xconvert_md() {
local files=( *.qmd(N) )
(( ${#files} )) || {
print "No .qmd files found"
return 1
}
for f in "${files[@]}"; do
print "Rendering $f"
quarto render "$f" "$@"
done
}
# ============================================================================
# PACKAGE MANAGEMENT (zypper)
# ============================================================================
# xrepos - Show repositories with priorities and URLs
xrepos() {
emulate -L zsh
zypper lr -p -u
}
# xsearch - Search packages (shows installed + available)
xsearch() {
emulate -L zsh
if [[ -z "${1:-}" ]]; then
print -u2 "Usage: xsearch <term>"
return 1
fi
zypper se -si -- "$1"
}
# xinstall - Install packages (skips if already installed)
xinstall() {
emulate -L zsh
(( $# )) || { print -u2 "Usage: xinstall <pkgs...>"; return 1; }
sudo zypper install -y -- "$@"
}
# xreinstall - Force reinstall packages
xreinstall() {
emulate -L zsh
(( $# )) || { print -u2 "Usage: xreinstall <pkgs...>"; return 1; }
sudo zypper install -y -f -- "$@"
}
# xremove - Remove packages
# Tip: add --clean-deps to also remove unneeded dependencies
xremove() {
emulate -L zsh
if (( ! $# )); then
print -u2 "Usage: xremove [--clean-deps] <pkgs...>"
return 1
fi
sudo zypper remove -- "$@"
}
# xdup - Quick distribution upgrade
xdup() {
emulate -L zsh
sudo zypper ref && sudo zypper dup
}
# xps - Show processes that need restart after updates
xps() {
emulate -L zsh
sudo zypper ps -s
}
# xneedreboot - Check if reboot is recommended
# Checks: zypper ps output + kernel version mismatch
xneedreboot() {
emulate -L zsh
local -i need=0
# Check zypper ps for reboot recommendation
if sudo zypper ps -s 2>/dev/null | grep -qi "reboot"; then
need=1
fi
# Check if running kernel != newest installed kernel
local running newest
running="$(uname -r)"
newest="$(
print -rl -- /boot/vmlinuz-*(N:t) \
| sed 's/^vmlinuz-//' \
| command sort -V \
| tail -n 1
)"
if [[ -n "$newest" && "$newest" != "$running" ]]; then
need=1
fi
if (( need )); then
print "⚠ Reboot recommended."
print " Running kernel: $running"
[[ -n "$newest" ]] && print " Newest kernel: $newest"
return 0
fi
print "✓ No reboot required (heuristic check)."
return 1
}
# xupdate - Safe system update with GUI/TTY awareness
#
# BEHAVIOR:
# GUI/Desktop session: Download-only mode (safer for DE updates)
# TTY/Console: Full upgrade + Flatpak + Firmware + WP-CLI
#
# This prevents updating critical desktop components while using them.
xupdate() {
emulate -L zsh
setopt err_return pipe_fail
# Detect graphical session
local -i gui_active=0
[[ -n "${DISPLAY:-}" || -n "${WAYLAND_DISPLAY:-}" ]] && gui_active=1
# Ensure sudo access
sudo -v || return 1
# GUI MODE: Download only
if (( gui_active )); then
xhr
print -P "%F{yellow}GRAPHICAL SESSION DETECTED - DOWNLOAD ONLY MODE%f"
xhr
print ""
sudo zypper ref
sudo zypper dup -d
print ""
xhr
print "✓ Downloads complete."
print ""
print "To apply safely:"
print " 1. Press Ctrl+Alt+F1 (or F2-F6) to switch to TTY"
print " 2. Log in"
print " 3. Run: xupdate"
print " 4. Reboot if prompted"
print " 5. Press Ctrl+Alt+F2 to return to GUI (or try F1, F7, F8)"
print ""
print "Alternative (less safe for major updates):"
print " sudo zypper dup"
xhr
return 0
fi
# TTY MODE: Full system upgrade
xhr
print -P "%F{green}TTY/CONSOLE MODE - FULL SYSTEM UPGRADE%f"
xhr
print ""
# System packages
print "Refreshing repositories..."
sudo zypper ref
print "\nPerforming distribution upgrade..."
sudo zypper dup
# Flatpak updates
if (( $+commands[flatpak] )) && [[ -n "$(flatpak remotes 2>/dev/null)" ]]; then
print "\nUpdating Flatpak packages..."
flatpak update -y || print -u2 "⚠ Flatpak update had issues (non-fatal)."
fi
# Firmware updates
if (( $+commands[fwupdmgr] )); then
print "\nChecking firmware updates..."
sudo fwupdmgr refresh >/dev/null 2>&1 || true
sudo fwupdmgr update 2>/dev/null || print "✓ No firmware updates available."
fi
# WP-CLI update (if installed)
if (( $+commands[wp] )); then
print "\nUpdating WP-CLI..."
if wp cli update --allow-root --yes >/tmp/wpcli-update.log 2>&1; then
print "✓ WP-CLI updated."
else
if grep -q -E "already up to date|package manager" /tmp/wpcli-update.log 2>/dev/null; then
print "✓ WP-CLI already up to date."
else
print -u2 "⚠ WP-CLI update failed (check /tmp/wpcli-update.log)."
fi
fi
fi
# Summary
print ""
xhr
print -P "%F{green}UPDATE COMPLETE%f"
xhr
print ""
# Check if reboot needed
xneedreboot
}
# ============================================================================
# SYSTEM MAINTENANCE
# ============================================================================
# xclean - Clean package caches and show unneeded packages
xclean() {
emulate -L zsh
xhr
print "Cleaning zypper package caches..."
sudo zypper clean --all
xhr
print "Checking for unneeded packages..."
sudo zypper packages --unneeded 2>/dev/null || true
xhr
print "✓ Cleanup complete."
print ""
print "To remove unneeded packages:"
print " sudo zypper remove --clean-deps <package-name>"
}
# xclean_zsh - Clear Zsh history file
xclean_zsh() {
emulate -L zsh
local hf="${HISTFILE:-$HOME/.zsh_history}"
print "Clearing Zsh history file ($hf)..."
: >| "$hf" || { print -u2 "✗ Failed to truncate $hf"; return 1; }
fc -R "$hf" || { print -u2 "✗ Failed to reload history"; return 1; }
print "✓ Zsh history cleared."
}
# xverify - System health check
xverify() {
emulate -L zsh
xhr
print -P "%BSystem Health Check%b"
xhr
# Snapper integration
print "\n%B1. Snapper Integration:%b"
local -a missing_snapper=()
local pkg
for pkg in snapper snapper-zypp-plugin grub2-snapper-plugin yast2-snapper; do
if rpm -q "$pkg" >/dev/null 2>&1; then
print " ✓ $pkg"
else
print " ✗ $pkg (missing)"
missing_snapper+=("$pkg")
fi
done
if (( ${#missing_snapper} )); then
print "\n To install missing packages:"
print " sudo zypper install ${(j: :)missing_snapper}"
fi
# Firewall status
print "\n%B2. Firewall Status:%b"
if (( $+commands[firewall-cmd] )); then
if sudo systemctl is-active firewalld >/dev/null 2>&1; then
print " ✓ firewalld is active"
else
print " ✗ firewalld is inactive"
print " Enable: sudo systemctl enable --now firewalld"
fi
else
print " ✗ firewalld not installed"
fi
# Failed systemd services
print "\n%B3. Failed Services:%b"
local failed_services
failed_services="$(systemctl --failed --no-pager --no-legend 2>/dev/null)"
if [[ -z "$failed_services" ]]; then
print " ✓ No failed services"
else
print " ⚠ Failed services detected:"
print "$failed_services" | sed 's/^/ /'
fi
# Btrfs health
print "\n%B4. Btrfs Health:%b"
if sudo btrfs device stats / >/dev/null 2>&1; then
local btrfs_errors
btrfs_errors="$(sudo btrfs device stats / 2>/dev/null | grep -v '\[.*\]\..*\s0$' || true)"
if [[ -z "$btrfs_errors" ]]; then
print " ✓ No Btrfs errors detected"
else
print " ⚠ Btrfs errors detected:"
print "$btrfs_errors" | sed 's/^/ /'
print " Consider running: xscrub"
fi
else
print " ⚠ Root filesystem is not Btrfs (or check failed)"
fi
# Btrfs quota check
print "\n%B4a. Btrfs Quota (if enabled):%b"
if sudo btrfs qgroup show / >/dev/null 2>&1; then
print " ✓ Btrfs quotas are enabled (Snapper disk usage tracking active)"
print " Run: sudo btrfs qgroup show -p / for details"
else
print " ⓘ Btrfs quotas not enabled (optional, used for snapshot size tracking)"
print " Enable with: sudo snapper setup-quota"
fi
# Disk space
print "\n%B5. Disk Space:%b"
local root_usage
root_usage="$(df -h / | awk 'NR==2 {print $5}' | sed 's/%//')"
if (( root_usage < 80 )); then
print " ✓ Root partition: ${root_usage}% used"
elif (( root_usage < 90 )); then
print " ⚠ Root partition: ${root_usage}% used (getting full)"
else
print " ✗ Root partition: ${root_usage}% used (critically full)"
print " Consider: xclean, deleting old snapshots, or expanding partition"
fi
# Recent snapshots
print "\n%B6. Recent Snapshots:%b"
if (( $+commands[snapper] )); then
local snapshot_count
snapshot_count="$(sudo snapper list 2>/dev/null | wc -l)"
((snapshot_count -= 2)) # Subtract header lines
print " ✓ Total snapshots: $snapshot_count"
print " Recent snapshots:"
sudo snapper list 2>/dev/null | tail -n 5 | sed 's/^/ /'
else
print " ✗ Snapper not available"
fi
xhr
print "✓ Health check complete."
xhr
}
# Combined maintenance operations
xmaid() { emulate -L zsh; xupdate && xclean; }
xsupermaid() { emulate -L zsh; xupdate && xclean_zsh && xclean; }
xuoff() { emulate -L zsh; xmaid && sudo poweroff; }
# ============================================================================
# BTRFS SNAPSHOT MANAGEMENT
# ============================================================================
# xsnapshots - List all snapshots
xsnapshots() {
emulate -L zsh
sudo snapper list
}
# xrollback - Rollback to a specific snapshot
xrollback() {
emulate -L zsh
if [[ -z "${1:-}" ]]; then
print -u2 "Usage: xrollback <snapshot-number>"
print "\nAvailable snapshots:"
sudo snapper list
return 1
fi
sudo snapper rollback "$1"
print ""
print "✓ Rollback prepared to snapshot $1."
print "⚠ Reboot to apply:"
print " sudo reboot"
}
# xsnapdiff - Show differences between snapshots
xsnapdiff() {
emulate -L zsh
if (( $# != 2 )); then
print -u2 "Usage: xsnapdiff <old-snapshot> <new-snapshot>"
return 1
fi
sudo snapper diff "$1".."$2"
}
# ============================================================================
# BTRFS MAINTENANCE
# ============================================================================
# xscrub - Check Btrfs filesystem for errors
xscrub() {
emulate -L zsh
print "Starting Btrfs scrub on / (reads all data to detect corruption)..."
print "This may take a while depending on disk size and usage."
print ""
sudo btrfs scrub start / || return 1
print ""
print "Scrub started. Check status with:"
print " sudo btrfs scrub status /"
print ""
print "Monitoring scrub progress (Ctrl+C to exit monitoring)..."
while sudo btrfs scrub status / | grep -q "running"; do
sudo btrfs scrub status /
sleep 10
print ""
done
print "Scrub complete. Final status:"
sudo btrfs scrub status /
}
# xbalance - Balance Btrfs filesystem (reclaim space)
# WARNING: Can be time-consuming and I/O intensive
xbalance() {
emulate -L zsh
print "═══════════════════════════════════════════════════════════"
print "BTRFS BALANCE"
print "═══════════════════════════════════════════════════════════"
print "This operation:"
print " • Rebalances data blocks across devices"
print " • Can reclaim space from deleted snapshots"
print " • Is I/O intensive and time-consuming"
print " • Should NOT be run on nearly-full filesystems"
print ""
print "Current Btrfs usage:"
sudo btrfs filesystem usage / 2>/dev/null || sudo btrfs filesystem df /
print ""
print "═══════════════════════════════════════════════════════════"
print -n "Continue with balance (usage=50)? (y/N): "
local ans
read -r ans
[[ "$ans" == [Yy] ]] || { print "Cancelled."; return 0; }
print "\nStarting Btrfs balance (this will take time)..."
sudo btrfs balance start -dusage=50 / || return 1
print "\n✓ Balance complete."
print "\nNew Btrfs usage:"
sudo btrfs filesystem usage / 2>/dev/null || sudo btrfs filesystem df /
}
# ============================================================================
# FIREWALL MANAGEMENT (firewalld)
# ============================================================================
# xfw_status - Show firewall status and rules
xfw_status() {
emulate -L zsh
print "Firewall service status:"
sudo systemctl status firewalld --no-pager
print ""
print "Active zones:"
sudo firewall-cmd --get-active-zones
print ""
print "Public zone rules:"
sudo firewall-cmd --zone=public --list-all
}
# xfw_kdeconnect - Open KDE Connect ports
xfw_kdeconnect() {
emulate -L zsh
sudo firewall-cmd --permanent --add-port=1714-1764/tcp
sudo firewall-cmd --permanent --add-port=1714-1764/udp
sudo firewall-cmd --reload
print "✓ KDE Connect ports (1714-1764 TCP/UDP) opened in public zone."
}
# xfw_ssh - Open SSH service
xfw_ssh() {
emulate -L zsh
sudo firewall-cmd --permanent --add-service=ssh
sudo firewall-cmd --reload
print "✓ SSH service opened in public zone."
}
# ============================================================================
# IMAGE OPTIMIZATION
# ============================================================================
# xoptimize_img - Optimize images and convert to webp format in the same folder
xoptimize_img() {
for i in *.(png|jpg|jpeg|webp|tiff|bmp)(N); do
print "Processing $i..."
nice -n 19 cwebp -lossless -m 6 "$i" -o "${i%.*}.tmp.webp" && mv "${i%.*}.tmp.webp" "${i%.*}.webp"
done
}
# xoptimize_rec_img - Optimize images and convert to webp format recursively
xoptimize_rec_img() {
for i in **/*.(png|jpg|jpeg|webp|tiff|bmp)(N); do
print "Processing $i..."
nice -n 19 cwebp -lossless -m 6 -mt "$i" -o "${i%.*}.tmp.webp" && mv "${i%.*}.tmp.webp" "${i%.*}.webp"
done
}
# ============================================================================
# WEB & DOWNLOAD FUNCTIONS
# ============================================================================
# xwget - Mirror/download entire website
xwget() {
emulate -L zsh
if [[ -z "${1:-}" ]]; then
print -u2 "Usage: xwget <URL>"
return 1
fi
_xrequire_cmd wget || return 1
local url="$1"
local site_name output_base_dir output_dir
# Extract and sanitize site name from URL
output_base_dir="$HOME/Downloads/MirroredSites"
site_name="$(print -r -- "$url" | sed -E 's#^([a-zA-Z.+-]+:)?(//)?([^/?#]+).*#\3#' | sed 's/[^a-zA-Z0-9._-]/_/g')"
[[ -z "$site_name" ]] && site_name="unknown_site_$(date +%s)"
output_dir="$output_base_dir/$site_name"
mkdir -p "$output_dir" || return 1
print "Mirroring '$url' → '$output_dir' ..."
print ""
wget --mirror --convert-links --adjust-extension --page-requisites \
--no-parent --wait=1 --random-wait --limit-rate=200k \
--user-agent="Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/115.0" \
-P "$output_dir" "$url"
}
# xserve - Serve static files (auto-detects build/dist directories)
# Uses npx to avoid requiring global serve installation
xserve() {
emulate -L zsh
local serve_dir="${1:-.}"
local port="${2:-3000}"
_xrequire_cmd node || return 1
_xrequire_cmd npm || return 1
local actual="$serve_dir"
# Auto-detect common build output directories
if [[ "$serve_dir" == "." ]]; then
if [[ -d ./build ]]; then
actual="./build"
print "xserve: auto-detected ./build"
elif [[ -d ./dist ]]; then
actual="./dist"
print "xserve: auto-detected ./dist"
else
print "xserve: serving current directory"
fi
fi
print "Starting server: $actual on http://localhost:$port"
print "Press Ctrl+C to stop."
print ""
# Use global serve if available, otherwise use npx
if (( $+commands[serve] )); then
serve -s "$actual" -l "$port"
else
_xrequire_cmd npx || return 1
npx --yes serve -s "$actual" -l "$port"
fi
}
# xserver - Simple Python HTTP server
xserver() {
emulate -L zsh
local port="${1:-8080}"
_xrequire_cmd python3 || return 1
print "Starting Python HTTP server on http://127.0.0.1:$port"
print "Serving: $PWD"
print "Press Ctrl+C to stop."
print ""
python3 -m http.server "$port" --bind 127.0.0.1
}
# ============================================================================
# PYTHON DEVELOPMENT
# ============================================================================
# xenv - Create/activate Python virtual environment (quick version)
xenv() {
emulate -L zsh
_xrequire_cmd python3 || return 1
if [[ ! -d venv ]]; then
print "Creating Python virtual environment..."
python3 -m venv venv || { print -u2 "✗ Failed to create venv"; return 1; }
fi
print "Activating virtual environment..."
source venv/bin/activate || return 1
print "Upgrading pip tooling..."
python -m pip install -U pip setuptools wheel
print "✓ Virtual environment ready."
}
# xpyenv_activate - Create/activate venv with detailed logging
xpyenv_activate() {
emulate -L zsh
_xrequire_cmd python3 || return 1
local venv_dir="venv"
local log_file="venv-creation.log"
# Activate existing venv
if [[ -d "$venv_dir" && -f "$venv_dir/bin/activate" ]]; then
print "Activating existing virtual environment..."
source "$venv_dir/bin/activate"
return $?
fi
# Error: venv directory exists but activation script is missing
if [[ -d "$venv_dir" && ! -f "$venv_dir/bin/activate" ]]; then
print -u2 "✗ '$venv_dir' directory exists but activation script is missing."
print -u2 " Delete '$venv_dir' and re-run xpyenv_activate."
return 1
fi
# Create new venv with logging
print "Creating new virtual environment (logging to $log_file)..."
python3 -m venv "$venv_dir" 2> >(tee "$log_file" >&2) || return 1
print "Activating new virtual environment..."
source "$venv_dir/bin/activate"
}
# xpipx_install - Install Python CLI applications via pipx
xpipx_install() {
emulate -L zsh
(( $# )) || { print -u2 "Usage: xpipx_install <package> [more...]"; return 1; }
if ! (( $+commands[pipx] )); then
print -u2 "✗ pipx not found."
print -u2 " Install: sudo zypper install python3-pipx"
return 1
fi
# Ensure pipx path is configured
pipx ensurepath >/dev/null 2>&1 || true
# Install each package
local pkg
for pkg in "$@"; do
pipx install "$pkg"
done
}
# ============================================================================
# NODE.JS & NPM DEVELOPMENT
# ============================================================================
# xnpm_update - Update npm packages using npm-check-updates
xnpm_update() {
emulate -L zsh
setopt local_options err_return pipe_fail
[[ -f package.json ]] || { print -u2 "✗ No package.json in $PWD"; return 1; }
_xrequire_cmd npm || return 1
print "Checking for npm package updates..."
# Use global ncu if available, otherwise use npx
if (( $+commands[ncu] )); then
ncu -u
else
_xrequire_cmd npx || return 1
npx --yes npm-check-updates -u
fi
print "\nInstalling updated packages..."
npm install
print "\nRunning npm audit..."
npm audit || print -u2 "⚠ npm audit reported issues (non-fatal)."
print "\n✓ npm update complete."
}
# xbun_build - Update and build JS/TS bun bundled project
xbun_build() {
xhr
# 1. Check if Bun is installed
if ! (( $+commands[bun] )); then
print -u2 "xbun_build: Error: bun not found."
return 1
fi
# 2. Smart Version Check (Once per session)
if [[ -z "$_XBUN_UPDATE_CHECKED" ]]; then
autoload -Uz is-at-least
local current_ver="$(bun --version)"
print "Checking for Bun updates (Current: v$current_ver)..."
local latest_tag latest_ver
latest_tag=$(curl -fsS --max-time 2 \
https://api.github.com/repos/oven-sh/bun/releases/latest \
| sed -n 's/.*"tag_name":[[:space:]]*"\([^"]*\)".*/\1/p')
latest_ver="${latest_tag#bun-v}"
if [[ -n "$latest_ver" ]] && ! is-at-least "$latest_ver" "$current_ver"; then
print "➤ New version found ($latest_ver). Upgrading Bun..."
bun upgrade || { print -u2 "✗ Bun upgrade failed."; return 1; }
else
print "✓ Bun is up to date."
fi
export _XBUN_UPDATE_CHECKED=1
else
print "✓ Bun update check skipped (already checked this session)."
fi
# 3. Update dependencies with npm-check-updates
xhr
print "📦 Updating dependencies with npm-check-updates..."
bunx ncu -u || { print -u2 "✗ Dependency update failed."; return 1; }
# 4. Install updated dependencies
xhr
print "📥 Installing dependencies..."
bun install || { print -u2 "✗ Dependency install failed."; return 1; }
# 5. Lint code
xhr
print "🔍 Linting code..."
bun run lint || { print -u2 "✗ Lint failed."; return 1; }
# 6. Type checking
xhr
print "🔎 Type checking..."
bun run typecheck || { print -u2 "✗ Type check failed."; return 1; }
# 7. Build site
xhr
print "🏗️ Building site..."
bun run build || { print -u2 "✗ Build failed."; return 1; }
# 8. Start preview server
xhr
print "🚀 Starting preview server..."
bun run preview
}
# ============================================================================
# ASTRO.JS WORKFLOWS
# ============================================================================
# _xastro_detect_pm - Detect and set package manager
_xastro_detect_pm() {
emulate -L zsh
# Check for lockfiles to determine package manager
if [[ -f bun.lockb ]] && command -v bun >/dev/null 2>&1; then
PM="bun"
PMX="bunx"
LOCKFILE="bun.lockb"
elif [[ -f package-lock.json ]] || ! command -v bun >/dev/null 2>&1; then
if ! command -v npm >/dev/null 2>&1; then
print -u2 "✗ npm not found. Please install Node.js"
return 1
fi
PM="npm"
PMX="npx"
LOCKFILE="package-lock.json"
elif command -v bun >/dev/null 2>&1; then
# Default to bun if available and no lockfile exists
PM="bun"
PMX="bunx"
LOCKFILE="bun.lockb"
else
PM="npm"
PMX="npx"
LOCKFILE="package-lock.json"
fi
return 0
}
# _xastro_require - Check Astro.js prerequisites
_xastro_require() {
emulate -L zsh
_xastro_detect_pm || return 1
if [[ ! -f package.json ]]; then
print -u2 "✗ No package.json found in $PWD"
return 1
fi
print "Using package manager: $PM"
return 0
}
# _xastro_full_workflow - Complete build workflow
_xastro_full_workflow() {
emulate -L zsh
print "🔄 Updating package.json..."
$PMX ncu -u || return 1
print "🔄 Upgrading Astro..."
$PMX @astrojs/upgrade || return 1
print "📦 Installing dependencies..."
$PM install || return 1
print "🔍 Linting code..."
$PM run lint || return 1
print "🔎 Type checking..."
$PM run typecheck || return 1
print "🏗️ Building site..."
$PM run build || return 1
print "🚀 Starting preview server..."
$PM run preview
}
# xastro_update - Update dependencies only
xastro_update() {
emulate -L zsh
_xastro_require || return 1
print "📦 Updating dependencies with npm-check-updates..."
$PMX ncu -u || return 1
print "📥 Installing updated dependencies..."
$PM install || return 1
print "✓ Dependencies updated"
}
# xastro_build - Full build + preview
xastro_build() {
emulate -L zsh
_xastro_require || return 1
print "🧹 Cleaning build artifacts..."
rm -rf .astro dist
_xastro_full_workflow
}
# xastro_nuke - Nuclear option: delete everything + full rebuild
xastro_nuke() {
emulate -L zsh
_xastro_require || return 1
print "═══════════════════════════════════════════════════════════"
print "💣 NUCLEAR OPTION: Complete Astro.js Rebuild"
print "═══════════════════════════════════════════════════════════"
print "This will delete:"
print " • .astro/"
print " • dist/"
print " • node_modules/"
print " • $LOCKFILE"
print ""
print -n "Continue? (y/N): "
local ans
read -r ans
[[ "$ans" == [Yy] ]] || { print "❌ Cancelled."; return 0; }
print "\n🗑️ Deleting everything..."
rm -rf -- .astro dist node_modules bun.lockb package-lock.json
_xastro_full_workflow
}
# xastro_live - Quick dev: clean + upgrade + build + dev server
xastro_live() {
emulate -L zsh
_xastro_require || return 1
print "🧹 Cleaning build artifacts..."
rm -rf .astro dist
print "⬆ Upgrading Astro..."
$PMX @astrojs/upgrade
print "🏗️ Building site..."
$PM run build
print "🔥 Starting development server..."
$PM run dev
}
# xastro_check - Check project health
xastro_check() {
emulate -L zsh
_xastro_require || return 1
print "═══════════════════════════════════════════════════════════"
print "🏥 Astro.js Project Health Check"
print "═══════════════════════════════════════════════════════════"
print "\n📌 $PM version:"
$PM --version
print "\n📄 Package.json info:"
if [[ -f package.json ]]; then
if [[ "$PM" == "bun" ]]; then
bun -e 'const p=require("./package.json"); console.log(p.name, p.version)'
else
node -e 'const p=require("./package.json"); console.log(p.name, p.version)'
fi
fi
print "\n📦 Outdated packages:"
$PMX ncu || return 1
print "\n💾 Disk usage:"
command du -sh node_modules .astro dist 2>/dev/null || print " No build artifacts found"
print "\n🔍 Linting code..."
$PM run lint || print " ⚠️ Lint script not available"
print "\n🔎 Type checking..."
$PM run typecheck || print " ⚠️ Typecheck script not available"
print "\n✓ Astro check..."
$PM run astro check || print " ⚠️ Astro check not available"
}
# xastro_clean - Just clean, don't build
xastro_clean() {
emulate -L zsh
_xastro_require || return 1
print "🧹 Cleaning Astro build artifacts..."
rm -rf -- .astro dist
print "✓ Cleaned .astro/ and dist/"
}
# xastro_dev - Just run dev server (no clean/build)
xastro_dev() {
emulate -L zsh
_xastro_require || return 1
print "🔥 Starting development server..."
$PM run dev
}
# xastro_preview - Just run preview server
xastro_preview() {
emulate -L zsh
_xastro_require || return 1
print "🚀 Starting preview server..."
$PM run preview
}
# xastro_quick - Quick workflow: update + lint + typecheck + build + preview
xastro_quick() {
emulate -L zsh
_xastro_require || return 1
print "📦 Updating dependencies..."
$PMX ncu -u || return 1
print "📥 Installing dependencies..."
$PM install || return 1
print "🔍 Linting code..."
$PM run lint || return 1
print "🔎 Type checking..."
$PM run typecheck || return 1
print "🏗️ Building site..."
$PM run build || return 1
print "🚀 Starting preview server..."
$PM run preview
}
# ============================================================================
# SYSTEM DIAGNOSTICS
# ============================================================================
# xinfo - Comprehensive system information
xinfo() {
emulate -L zsh
xhr
print -P "%B═══ OS & Hardware ═══%b"
if (( $+commands[fastfetch] )); then
fastfetch
else
print "✗ fastfetch not found. Install: sudo zypper install fastfetch"
fi
xhr
print -P "%B═══ System Details ═══%b"
if (( $+commands[inxi] )); then
inxi -FGx
else
print "✗ inxi not found. Install: sudo zypper install inxi"
fi
xhr
print -P "%B═══ Graphics Stack ═══%b"
print "Kernel: $(uname -r)"
print "Session: ${XDG_SESSION_TYPE:-Unknown}"
print "Desktop: ${XDG_CURRENT_DESKTOP:-Unknown}"
# OpenGL information
if (( $+commands[glxinfo] )); then
print "\nOpenGL:"
glxinfo -B 2>/dev/null | grep -Ei "direct rendering|device|vendor|version|renderer" || true
fi
# Vulkan information
if (( $+commands[vulkaninfo] )); then
print "\nVulkan:"
vulkaninfo --summary 2>/dev/null | grep -E "deviceName|driverInfo|apiVersion" || true
fi
xhr
print -P "%B═══ Recent Snapshots ═══%b"
if (( $+commands[snapper] )); then
sudo snapper list | tail -n 10
else
print "✗ snapper not found."
fi
xhr
print -P "%B═══ Disk Usage ═══%b"
df -h / /home 2>/dev/null || df -h /
xhr
}
# xbaloo_reset - Reset KDE Baloo file indexer
xbaloo_reset() {
emulate -L zsh
if (( $+commands[balooctl6] )); then
print "Resetting Baloo (KDE Plasma 6)..."
balooctl6 suspend
sleep 2
balooctl6 purge
balooctl6 resume
sleep 1
balooctl6 check
print "✓ Baloo reset complete (KDE 6)."
elif (( $+commands[balooctl] )); then
print "Resetting Baloo (KDE Plasma 5)..."
balooctl disable
sleep 2
balooctl purge
balooctl enable
sleep 1
balooctl check
print "✓ Baloo reset complete (KDE 5)."
else
print -u2 "✗ Baloo control command not found."
return 1
fi
}
# ============================================================================
# OPTIONAL APP LAUNCHERS
# ============================================================================
# xlampp - Launch XAMPP manager (if installed)
xlampp() {
emulate -L zsh
local mgr="/opt/lampp/manager-linux-x64.run"
if [[ -x "$mgr" ]]; then
sudo "$mgr"
return 0
else
print -u2 "✗ XAMPP manager not found at: $mgr"
print -u2 " Install XAMPP from: https://www.apachefriends.org/"
return 1
fi
}
# ============================================================================
# COMMAND REPLACEMENTS & ENHANCEMENTS
# ============================================================================
# eza -> ls replacement
alias ls='eza --icons --group-directories-first'
alias ll='eza -lh --icons --group-directories-first'
alias la='eza -a --icons --group-directories-first'
alias tree='eza --tree --icons'
# bat -> cat replacement
alias cat='bat --paging=never'
# gdu -> disk usage tool (renamed to xdu to avoid conflicts)
alias xdu='gdu'
# bottom -> top replacement
alias top='btm'
# lazygit alias
alias lg='lazygit'
# jcurl - Pretty-print JSON from curl using jq
jcurl() {
curl -sL "$@" | jq
}
# ============================================================================
# SYSTEM ALIASES - UTILITIES
# ============================================================================
alias xclear='tput reset'
# ============================================================================
# SYSTEM ALIASES - DNS
# ============================================================================
alias dns_s='dns_status'
alias dns_v='dns_status -v'
alias dns_l='dns_status -l'
# ============================================================================
# SYSTEM ALIASES - FILE LISTING (requires eza)
# ============================================================================
if (( $+commands[eza] )); then
alias xls='eza --icons --group-directories-first'
alias xll='eza -lh --icons --group-directories-first'
alias xla='eza -a --icons --group-directories-first'
alias xlt='eza --tree --level=2 --icons'
fi
# ============================================================================
# ZSH CONFIGURATION - COMPLETION, HISTORY, OPTIONS
# ============================================================================
# Setup cache directory for completion dumps
ZSH_CACHE="${XDG_CACHE_HOME:-$HOME/.cache}/zsh"
mkdir -p "$ZSH_CACHE"
# Initialize completion system
autoload -Uz compinit
compinit -d "$ZSH_CACHE/zcompdump-$ZSH_VERSION"
# History settings
HISTFILE="${ZDOTDIR:-$HOME}/.zsh_history"
HISTSIZE=10000
SAVEHIST=10000
# Shell options
setopt HIST_IGNORE_ALL_DUPS # Remove older duplicate commands from history
setopt SHARE_HISTORY # Share history between all sessions
setopt CORRECT # Command spelling correction
setopt INTERACTIVE_COMMENTS # Allow comments in interactive shell
setopt PROMPT_SUBST # Enable parameter expansion in prompts
# ============================================================================
# PROMPT CONFIGURATION - Catppuccin Mocha Theme
# ============================================================================
autoload -Uz colors vcs_info add-zsh-hook
colors
# Configure VCS info (git branch only for performance)
zstyle ':vcs_info:*' enable git
zstyle ':vcs_info:git:*' formats '(%b)'
zstyle ':vcs_info:git:*' actionformats '(%b|%a)'
add-zsh-hook precmd vcs_info
# Prompt format:
# user@host ~/path (git-branch)
# ❯
PROMPT='%F{#cba6f7}%n%f@%F{#fab387}%m%f %F{#89b4fa}%~%f %F{#a6e3a1}${vcs_info_msg_0_}%f
%F{#a6e3a1}❯%f '
# ============================================================================
# PLUGIN LOADING - openSUSE System Locations
# ============================================================================
# zsh-autosuggestions: Fish-like command suggestions based on history
if [[ -f /usr/share/zsh-autosuggestions/zsh-autosuggestions.zsh ]]; then
source /usr/share/zsh-autosuggestions/zsh-autosuggestions.zsh
elif [[ -f /usr/share/zsh/site-functions/zsh-autosuggestions.zsh ]]; then
source /usr/share/zsh/site-functions/zsh-autosuggestions.zsh
fi
# zsh-syntax-highlighting: Fish-like syntax highlighting (load last)
if [[ -f /usr/share/zsh-syntax-highlighting/zsh-syntax-highlighting.zsh ]]; then
source /usr/share/zsh-syntax-highlighting/zsh-syntax-highlighting.zsh
elif [[ -f /usr/share/zsh/site-functions/zsh-syntax-highlighting.zsh ]]; then
source /usr/share/zsh/site-functions/zsh-syntax-highlighting.zsh
fi
# ============================================================================
# END OF CONFIGURATION
# ============================================================================
11 — Best Practices for Tumbleweed
11.1 — Update regularly
- Recommended cadence: Every few days to once a week
- Don't let the system go months without updates
- Regular small updates are safer than infrequent large ones
- Tumbleweed is designed to be updated frequently—this is when the system is most stable
11.2 — Always use zypper dup
Always use zypper dup (distribution upgrade) for Tumbleweed updates—this is the officially recommended method for tracking the rolling release state. Unlike zypper up, zypper dup can:
- Switch packages between vendors when necessary
- Remove packages no longer required
- Handle complex dependency changes
- Track distribution state evolution
11.3 — Major updates in TTY (safest approach)
Recommended workflow:
- Download in GUI:
sudo zypper dup --download-only - Switch to TTY: Ctrl+Alt+F1–F6
- Apply updates:
sudo zypper dup - Reboot if needed: Follow zypper's recommendations
Alternative (advanced): Minimal GUI interaction method:
sudo systemctl isolate multi-user.target
sudo zypper ref && sudo zypper dup
sudo systemctl isolate graphical.target
The Tumbleweed SDB mentions rescue.target as theoretically safest (fewest services loaded), but multi-user.target is the practical choice for most updates.
11.4 — Trust Snapper (but understand its limitations)
What Snapper does:
- Provides system-level rollback capability
- Allows you to recover from problematic updates
- Captures system state at package installation time
What Snapper does NOT do:
- Backup your personal files (
/homeis excluded by default) - Replace proper backup solutions
- Protect against hardware failures
Key principle: Snapper is a "time machine" for system configuration, not a backup solution for your data.
11.5 — Check for update announcements
For critical updates or known issues:
- openSUSE News: https://news.opensuse.org/
- openSUSE Factory mailing list: https://lists.opensuse.org/archives/list/factory@lists.opensuse.org/
- openSUSE Forums: https://forums.opensuse.org/
- r/openSUSE subreddit: https://www.reddit.com/r/openSUSE/
- openSUSE Planet (blogs): https://planet.opensuse.org/
Major changes (like kernel updates with known issues) are typically announced on these platforms.
11.6 — Disk space management
Monitor disk usage:
# Overall disk usage
df -h /
# Detailed Btrfs usage
sudo btrfs filesystem usage /
# Snapshot disk consumption
sudo btrfs subvolume list / -s
Manage snapshots:
When deleting snapshots manually, delete the oldest first—they typically reference more unique data due to accumulated changes.
# Delete specific snapshot
sudo snapper delete <snapshot-number>
# Delete range
sudo snapper delete <start>-<end>
# Trigger automatic cleanup
sudo snapper cleanup timeline
sudo snapper cleanup number
Note: Snapper's automatic cleanup policies handle most space management. Manual intervention is rarely needed unless disk space is critically low.
11.7 — Btrfs maintenance (long-term health)
Periodic maintenance helps prevent filesystem issues:
Monthly scrub (recommended):
xscrub
# or manually:
sudo btrfs scrub start /
sudo btrfs scrub status /
Balance (only when needed):
xbalance
# or manually:
sudo btrfs balance start -dusage=50 /
Warning: Only run balance if you're experiencing space issues or poor performance. It's I/O intensive and should not be run on nearly-full filesystems.
11.8 — Security best practices
Keep microcode updated:
# Check current microcode
grep microcode /proc/cpuinfo
# Update via system updates
sudo zypper dup
Verify package signatures:
# Check package signature
rpm --checksig <package.rpm>
# Verify installed package
rpm -V <package-name>
Monitor security updates:
# Check for security updates
zypper list-updates --category security
12 — Troubleshooting
12.1 — System won't boot after update
Solution: Boot from snapshot and rollback
- Reboot and access GRUB menu (hold Shift during boot if needed)
- Select: "Start Bootloader from a read-only snapshot"
- Choose a working snapshot (try the most recent Pre snapshot before the problematic update)
- Boot and verify system functionality
- Execute rollback:
sudo snapper rollback
sudo reboot
If GRUB snapshot menu is missing:
Verify grub2-snapper-plugin is installed:
sudo zypper install grub2-snapper-plugin
sudo grub2-mkconfig -o /boot/grub2/grub.cfg
12.2 — Partial update issues / vendor conflicts
Symptoms:
- Dependency conflicts during updates
- Vendor mismatch errors
- Packages refuse to install/update
Solution:
# Allow vendor changes for this update
sudo zypper dup --allow-vendor-change
# For stubborn conflicts
sudo zypper dup --allow-vendor-change --allow-downgrade
Best practice: Set up vendor equivalence (see Section 3.3) to avoid repeated conflicts.
12.3 — Packman conflicts
If using full Packman repository:
# Temporarily disable Packman during update
sudo zypper modifyrepo --disable packman # or use -d
# Update system
sudo zypper dup
# Re-enable Packman
sudo zypper modifyrepo --enable packman # or use -e
sudo zypper dup --from packman --allow-vendor-change
If using Packman Essentials:
# Disable during system update
sudo zypper modifyrepo --disable packman-essentials
sudo zypper dup
# Re-enable and reinstall codecs
sudo zypper modifyrepo --enable packman-essentials
sudo zypper install --allow-vendor-change --from packman-essentials \
ffmpeg gstreamer-plugins-{good,bad,ugly,libav} libavcodec-full vlc-codecs
Simplest solution: Most users should just use opi codecs which handles vendor switching automatically.
12.4 — Check what needs restart after updates
# Show services/processes needing restart
sudo zypper ps -s
# Restart specific service
sudo systemctl restart <service-name>
# For desktop applications, simply close and reopen them
If zypper ps indicates reboot is needed, it's best to reboot to ensure all components run updated versions.
12.5 — Firewall blocking needed service
Symptoms:
- Application can't connect to network
- Service works locally but not from network
- Port appears closed from outside
Diagnosis:
# Check firewall logs in real-time
sudo journalctl -u firewalld -f
# Check if port is open
sudo firewall-cmd --zone=public --list-all
Solution:
# Temporary (testing only - not persistent)
sudo firewall-cmd --add-port=XXXX/tcp
sudo firewall-cmd --add-port=XXXX/udp
# If that fixes it, make permanent
sudo firewall-cmd --permanent --add-port=XXXX/tcp
sudo firewall-cmd --permanent --add-port=XXXX/udp
sudo firewall-cmd --reload
12.6 — Btrfs errors or filesystem issues
Check for errors:
# Check device stats
sudo btrfs device stats /
# Run filesystem check (requires read-only mount or unmount)
# WARNING: Only run on unmounted or read-only filesystem
sudo btrfs check /dev/sdXY # DO NOT run on mounted filesystem
Run scrub to detect/repair corruption:
xscrub
# or manually:
sudo btrfs scrub start /
sudo btrfs scrub status /
If filesystem is critically damaged:
Boot from live USB and run:
sudo btrfs check --repair /dev/sdXY
Warning: btrfs check --repair should only be used as a last resort.
12.7 — KDE Baloo indexer causing high CPU/disk usage
Solution:
# Reset Baloo
xbaloo_reset
# Or disable file indexing entirely
balooctl6 disable # KDE 6
# or
balooctl disable # KDE 5
Configure indexing exclusions:
- System Settings → Search → File Search
- Add directories to exclude (e.g., large media folders, VMs, builds)
12.8 — System verification after issues
Run the health check function to identify problems:
xverify
This checks:
- Snapper integration
- Firewall status
- Failed systemd services
- Btrfs health
- Btrfs quota status
- Disk space
- Recent snapshots
13 — Quick Command Reference
System Updates
# Full update (recommended workflow)
sudo zypper ref && sudo zypper dup
# Safe method: download first, apply in TTY
sudo zypper dup --download-only
# Then: Ctrl+Alt+F1, login, sudo zypper dup
# Using custom function (GUI-aware)
xupdate
# Check what needs restart
sudo zypper ps -s
# Check if reboot needed
xneedreboot
Repository Management
# List repositories with priorities
zypper lr -u
xrepos
# Add repository
sudo zypper addrepo <URL> <alias>
# Remove repository
sudo zypper removerepo <alias>
# Enable/disable repository
sudo zypper modifyrepo --enable <alias>
sudo zypper modifyrepo --disable <alias>
# Refresh repositories
sudo zypper refresh
Package Management
# Search packages
zypper search <name>
xsearch <name>
# Get package info
zypper info <package>
# Install packages
sudo zypper install <package>
xinstall <package>
# Remove packages
sudo zypper remove <package>
sudo zypper remove --clean-deps <package> # Also remove deps
# Reinstall package
xreinstall <package>
# List installed packages
zypper search -i
# Check for updates
zypper list-updates
Snapshot Management
# List all snapshots
sudo snapper list
xsnapshots
# Rollback to snapshot
sudo snapper rollback <number>
sudo reboot
# Compare snapshots
sudo snapper diff <old>..<new>
xsnapdiff <old> <new>
# Delete snapshot
sudo snapper delete <number>
sudo snapper delete <start>-<end>
Btrfs Operations
# Check filesystem usage
sudo btrfs filesystem usage /
df -h /
# Check for errors
sudo btrfs device stats /
# Run scrub
xscrub
# or: sudo btrfs scrub start /
# Balance filesystem (cautiously)
xbalance
# or: sudo btrfs balance start -dusage=50 /
Firewall Management
# Check status
sudo firewall-cmd --state
xfw_status
# List active zones and rules
sudo firewall-cmd --get-active-zones
sudo firewall-cmd --zone=public --list-all
# Add service (permanent)
sudo firewall-cmd --permanent --add-service=<service>
sudo firewall-cmd --reload
# Add port (permanent)
sudo firewall-cmd --permanent --add-port=XXXX/tcp
sudo firewall-cmd --reload
# Quick helpers
xfw_ssh # Open SSH
xfw_kdeconnect # Open KDE Connect ports
System Information
# Comprehensive system info
xinfo
# System health check
xverify
# Kernel version
uname -r
# Distribution info
cat /etc/os-release
# Hardware info
inxi -FGx
lshw -short
lspci
lsusb
Codec Installation
# Easiest method (recommended)
opi codecs
# Manual Packman Essentials
sudo zypper ar -cfp 90 \
'https://ftp.gwdg.de/pub/linux/misc/packman/suse/openSUSE_Tumbleweed/Essentials/' \
packman-essentials
sudo zypper install --allow-vendor-change --from packman-essentials \
ffmpeg gstreamer-plugins-{good,bad,ugly,libav} libavcodec-full
14 — Additional Resources
Official Documentation
- openSUSE Portal: https://www.opensuse.org/
- openSUSE Wiki: https://en.opensuse.org/
- Tumbleweed Wiki: https://en.opensuse.org/Portal:Tumbleweed
- Zypper Documentation: https://en.opensuse.org/SDB:Zypper_usage
- Snapper Documentation: https://en.opensuse.org/openSUSE:Snapper_Tutorial
- Btrfs Wiki: https://en.opensuse.org/SDB:BTRFS
News & Development
- openSUSE News: https://news.opensuse.org/ (includes update announcements)
- Factory Mailing List: https://lists.opensuse.org/archives/list/factory@lists.opensuse.org/ (technical discussions)
Community Support
- openSUSE Forums: https://forums.opensuse.org/
- r/openSUSE subreddit: https://www.reddit.com/r/openSUSE/
- openSUSE Discord: https://discord.gg/opensuse
- IRC: #opensuse on irc.libera.chat
- Telegram: https://t.me/openSUSE
KDE Plasma Resources
- KDE Portal (openSUSE): https://en.opensuse.org/Portal:KDE
- KDE UserBase: https://userbase.kde.org/
- KDE Community: https://community.kde.org/
Package Search & OBS
- Software Portal: https://software.opensuse.org/
- Open Build Service: https://build.opensuse.org/
End of Guide — Welcome to openSUSE Tumbleweed! May your rolling release journey be smooth, stable, and snapshot-protected. 🦎💚