Ubuntu
Table of Contents
uv system wise
sudo sh -c 'curl -LsSf https://astral.sh/uv/install.sh | UV_INSTALL_DIR=/usr/local/bin sh'disks secure erase
For Hetznnerโs recovery image - erase.sh:
curl -fsSL https://raw.githubusercontent.com/PotemkinCo/toolbelt/refs/heads/main/nvme_erase.sh | bash
quickly test ports
nmap -T4 -p- --open --min-rate=1000 --min-parallelism=100 -Pn 198.51.100.1fscrypt setup
# Install dependenciessudo apt update && sudo apt install -y fscrypt libpam-fscrypt
# Enable encryption on the file system# IMPORTANT: Replace '/dev/sda2' with your actual /home partition (check with: df -h /home)sudo tune2fs -O encrypt /dev/sda2
# Initialize fscrypt configurationsudo fscrypt setup # choose N on 'Allow users other than root to create fscrypt metadata on the root filesystem?'
# Enable PasswordAuthentication and PAM in sshd_configsudo sed -i 's/^#PasswordAuthentication.*/PasswordAuthentication yes/' /etc/ssh/sshd_configsudo sed -i 's/^PasswordAuthentication no/PasswordAuthentication yes/' /etc/ssh/sshd_configsudo sed -i 's/^#UsePAM.*/UsePAM yes/' /etc/ssh/sshd_config
# Restart SSH servicesudo systemctl restart ssh
# Ensure PAM module is active (Select "Identity management for fscrypt" if prompted)sudo pam-auth-update
USER_NAME="newuser"
# 1. Create the usersudo adduser $USER_NAME
# 2. Move skeleton files out (directory must be empty to encrypt)sudo mv /home/$USER_NAME /home/$USER_NAME.baksudo mkdir /home/$USER_NAMEsudo chown $USER_NAME:$USER_NAME /home/$USER_NAME
# 3. Encrypt the directory# Select option "1 - Your login passphrase (pam_passphrase)"# Enter the NEW USER'S password when promptedsudo fscrypt encrypt /home/$USER_NAME --user=$USER_NAME
# 4. Restore skeleton files# Unlock first to verify accesssudo fscrypt unlock /home/$USER_NAME --user=$USER_NAME# Copy files backsudo cp -a /home/$USER_NAME.bak/. /home/$USER_NAME/# Cleanupsudo rm -rf /home/$USER_NAME.bakManually change encryption password:
fscrypt status /# Replace 5e2d... with your actual hashfscrypt metadata change-passphrase --protector=/:5e2d14...CPU performance
by default is powersave, list CPU modes available:
cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_available_governorsChange mode:
echo performance | sudo tee /sys/devices/system/cpu/cpu*/cpufreq/scaling_governorAlso use taskset -c 0-5 to bind some task to a specific CPU cores
And a permanent option:
sudo apt update && sudo apt -y install cpufrequtilsecho 'GOVERNOR="performance"' | sudo tee /etc/default/cpufrequtilssudo systemctl restart cpufrequtilssudo systemctl disable ondemand # may fail safelyGPU & iGPU
lspci -nnk | grep -A3 VGA shows (i)GPU connected to the system.
NVidia GPU
That is a summary of vLLM & NVidia GPU, vllm ref:
# GPU drivers and toolsubuntu-drivers devices | grep recommended | grep -oP nvidia-driver-[0-9]* | awk '{sub(/driver-/,""); print}' | { read -d '' x; echo "sudo apt install -y linux-modules-$x-$(uname -r)"; } | bashsudo apt install nvtop nvidia-utils-$(dpkg -l | awk '/nvidia-kernel-common-[0-9]+/ {print $2}' | sed -E 's/.*-([0-9]+)$/\1/')sudo apt install nvtop nvidia-driver-$(dpkg -l | awk '/nvidia-kernel-common-[0-9]+/ {print $2}' | sed -E 's/.*-([0-9]+)$/\1/')Intelโs iGPU
If sudo lspci -v -s $(lspci | grep VGA | grep Intel | cut -d" " -f 1) gives no kernel module / GPU, then enable it:
ls -la /dev/dri # expected to fail -> iGPU is disabledvi /etc/modprobe.d/blacklist-hetzner.conf # comment out (disabled) i915 & i915_bdwvi /etc/default/grub.d/hetzner.cfg # at GRUB_CMDLINE_LINUX_DEFAULT, 'nomodeset' to be removedsudo grub-mkconfig -o /boot/grub/grub.cfgsudo shutdown -r nowls -la /dev/dri # shall give devices listsudo lspci -v -s $(lspci | grep VGA | grep Intel | cut -d" " -f 1) # shall contain 'Kernel driver in use: i915'sudo apt install intel-gpu-toolssudo intel_gpu_top# modprobe i915 if top failsDocker & GPU support
# Docker & lazydockerwget -O - https://get.docker.com | sudo bashcurl https://raw.githubusercontent.com/jesseduffield/lazydocker/master/scripts/install_update_linux.sh | bashecho 'PATH=$PATH:~/.local/bin' >> ~/.bashrc# GPU for Dockercurl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey | sudo gpg --dearmor -o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg \ && curl -s -L https://nvidia.github.io/libnvidia-container/stable/deb/nvidia-container-toolkit.list | \ sed 's#deb https://#deb [signed-by=/usr/share/keyrings/nvidia-container-toolkit-keyring.gpg] https://#g' | \ sudo tee /etc/apt/sources.list.d/nvidia-container-toolkit.listapt update && apt install -y nvidia-container-toolkitnvidia-ctk runtime configure --runtime=dockersystemctl restart dockerllama.cpp
Vanilla
# vulcansudo apt install vulkan-tools libvulkan1 libnvidia-gl-580curl -s https://api.github.com/repos/ggml-org/llama.cpp/releases/latest | grep 'vulkan' | grep 'ubuntu' | tail -1 | cut -d '"' -f 4 | wget -qi -# ubuntu# curl -s https://api.github.com/repos/ggml-org/llama.cpp/releases/latest | grep -v 'vulkan' | grep 'ubuntu' | tail -1 | cut -d '"' -f 4 | wget -qi -apt install -y unzipunzip *.zip && cd build/bin./llama-server --list-devices./llama-server -hf ggml-org/gpt-oss-120b-GGUF --ctx-size 32768 --jinja -ub 2048 -b 2048 --n-cpu-moe 28llama-server.service file:
[Unit]Description=Llama-Server GPT InferenceAfter=network.target
[Service]# Run as root or change to your userUser=rootGroup=root
# Always restart if it crashesRestart=alwaysRestartSec=5
# The command to runExecStart=/root/llama-server/bin/llama-server -hf ggml-org/gpt-oss-120b-GGUF --ctx-size 32768 --jinja -ub 2048 -b 2048 --n-cpu-moe 28
[Install]WantedBy=multi-user.targetFollowed by activation:
sudo systemctl daemon-reloadsudo systemctl enable llama-server.servicesudo systemctl start llama-server.serviceAnd monitoring via nvtop.
Adding protection with key file:
llm.example.com { log @unauthorized not header Authorization "Bearer <api-bearer-token>" respond @unauthorized "No chatter for you!"
reverse_proxy 127.0.0.1:8080}And caddy install, as per caddy web servr - NON DOCKER!
Docker
docker run --runtime nvidia --gpus all -v ./llama_data:/root -p 8000:8000 ghcr.io/ggml-org/llama.cpp:server-cuda -hf unsloth/gemma-3-27b-it-qat-GGUF --port 8000 --host 0.0.0.0The compose.yml file:
services: llama-server: restart: unless-stopped image: ghcr.io/ggml-org/llama.cpp:server-cuda-b6602 runtime: nvidia deploy: resources: reservations: devices: - capabilities: [gpu] environment: - NVIDIA_VISIBLE_DEVICES=all volumes: - ./llama_data:/root command: > -hf unsloth/gemma-3-27b-it-qat-GGUF --port 8000 --host 0.0.0.0vLLM
docker run --runtime nvidia --gpus all -v ./vllm:/root/.cache/huggingface -p 8000:8000 --ipc=host vllm/vllm-openai:v0.10.2-x86_64 --model unsloth/gemma-3-27b-it# the later didn't work out for me on Hetzner's lowest requirements GPU machineTest
localhost
time curl http://localhost:8000/v1/chat/completions -H "Content-Type: application/json" -d '{"model": "gpt-4", "messages": [{"role": "system", "content": "You are a helpful assistant."}, {"role": "user", "content": "Hello, write me a story"} ], "temperature": 0.7 }'remote
time curl https://llm.example.com/v1/chat/completions \ -H "Content-Type: application/json" \ -H "Authorization: Bearer <api-bearer-token>" \ -d '{ "model": "gpt-4", "messages": [ {"role": "system", "content": "You are a helpful assistant."}, {"role": "user", "content": "Hello, write me a story"} ], "temperature": 0.7 }'Disk space usage
One of
sudo apt install ncdusudo apt install gdusudo snap install duaWiFi doesnโt connect - needs re-connecting
sudo tee /etc/systemd/system/wifi-resume.service << 'EOF'
[Unit]
Description=Re-enable WiFi after resume
After=suspend.target[Service]
Type=oneshot
ExecStart=/bin/bash -c 'sleep 3 && nmcli radio wifi off && sleep 1 && nmcli radio wifi on'[Install]
WantedBy=suspend.target
EOF
(and after that)
sudo systemctl daemon-reload
sudo systemctl enable wifi-resume.service
(Now you are set. Test it. If it fails, provide the log using these commands)
systemctl status wifi-resume.servicejournalctl -u wifi-resume.service -bThunderbird via deb
sudo su followed by:
add-apt-repository ppa:mozillateam/ppaecho 'Package: *Pin: release o=LP-PPA-mozillateamPin-Priority: 1001
Package: thunderbirdPin: version 2:1snap*Pin-Priority: -1' | sudo tee /etc/apt/preferences.d/thunderbirdsnap remove thunderbirdapt install thunderbirdecho 'Unattended-Upgrade::Allowed-Origins:: "LP-PPA-mozillateam:${distro_codename}";' | sudo tee /etc/apt/apt.conf.d/51unattended-upgrades-thunderbirdreplace root on login
Login via root:
adduser --disabled-password --gecos "" ubuntuusermod -aG sudo ubuntuinstall -d -m 700 -o ubuntu -g ubuntu /home/ubuntu/.ssh && \install -m 600 -o ubuntu -g ubuntu /root/.ssh/authorized_keys /home/ubuntu/.ssh/authorized_keysecho '%sudo ALL=(ALL) NOPASSWD: ALL' > /etc/sudoers.d/sudo-nopasswdThen, once logged in:
sudo cp /etc/ssh/sshd_config /etc/ssh/sshd_config-backupsudo sed -i 's/^#*PermitRootLogin.*/PermitRootLogin no/' /etc/ssh/sshd_configsudo sed -i 's/^#*PasswordAuthentication.*/PasswordAuthentication no/' /etc/ssh/sshd_configgrep -Ei 'permitrootlogin|passwordauthentication' /etc/ssh/sshd_config | grep -v "^#"sudo systemctl restart ssh ssh.socketsudo systemctl status sshRetry the ssh.
change ssh port on Ubuntu 24.04
sudo systemctl daemon-reload followed by sudo systemctl restart ssh.socket (source)
update / verify grub
sudo update-grub
Make it display menu
vi /etc/default/grub # GRUB_TIMEOUT_STYLE=menusudo update-grubclean up fill disk space (very quickly)
yes abcdefghijklmnopqrstuvwxyz0123456789 > largefilesync; syncsudo fstrim -avrm largefilea (much) slower approach
sudo apt update && sudo apt install secure-deletenohup sudo sfill -v / &or
nohup sudo sfill -llv /nopasswd on sudo
%sudo ALL=(ALL) NOPASSWD: ALL
creating new filesystem
lsblkfdisk /dev/vdb # n => p => wlsblkmkfs.ext4 /dev/vdb1blkid | grep vdb1 # note UUIDvi /etc/fstabjq
Print file structure (src):
cat server.json | jq '[paths|(map(if type == "number" then "[]" else ".\(.)" end)|join(""))]|unique'benchmark
CPU
sudo apt-get install sysbenchsysbench cpu runsysbench --threads="$(nproc)" cpu runI/O
(ref)
sysbench fileio --file-test-mode=seqwr --time=600 runI/O / disk / ssd system information
cat /proc/mdstatvmstat 1 10sudo apt install smartmontoolslsblksmartctl -a /dev/...fantastic load monitor
atop -B
Install SpeedTest CLI
curl -s https://packagecloud.io/install/repositories/ookla/speedtest-cli/script.deb.sh | sudo bashsudo apt-get install speedtestRemove snap
snap listsnap remove --purge pkg_namesystemctl disable snapdsystemctl mask snapdsudo apt-mark hold snapdLet shared disk work on VirtualBox
sudo adduser $USER vboxsfsudo shutdown -r nowcrontab for reboot
30 4 * * MON-THU /bin/sh -c '[ -f /var/run/reboot-required ] && sudo shutdown -r now'check IP address
curl checkip.amazonaws.compro subscription
Java & Maven:
wget https://download.java.net/java/GA/jdk20.0.2/6e380f22cbe7469fa75fb448bd903d8e/9/GPL/openjdk-20.0.2_linux-x64_bin.tar.gz https://dlcdn.apache.org/maven/maven-3/3.9.4/binaries/apache-maven-3.9.4-bin.tar.gztar -xvf openjdk*.tar.gztar -xvf apache-maven-*-bin.tar.gzmv jdk-* /opt/mv apache-maven-* /opt/vi .profileAdd the following lines there:
JAVA_HOME='/opt/jdk-20.0.2'PATH="$JAVA_HOME/bin:$PATH"M2_HOME='/opt/apache-maven-3.9.4'PATH="$M2_HOME/bin:$PATH"export PATHand execute:
source .profilejava -versionmvn -versiondisable service autostart
systemctl disable <service>enable journald size limit
as per this article
journalctl --disk-usage # to check the usage
journalctl --vacuum-size=200M #to clean things up
grep SystemMaxUse /etc/systemd/journald.conf #parameter to support logsSystemMaxUse=50Malternative for cron - systemd
from here
systemctl list-timersDisable IPv6 in Ubuntu
cat <<EOF >> /etc/sysctl.d/99-no_ipv6.confnet.ipv6.conf.all.disable_ipv6 = 1net.ipv6.conf.default.disable_ipv6 = 1EOFservice procps force-reloadAND disable it in netplan config: add link-local: [ ipv4 ] at the same level, as dhcp instruction
Laptop battery status
upower -eupower -i <battery_path>
cd /sys/class/power_supply/BAT0ls #enjoy the choicecat cycle_countAdding Swap file
Adding swap file to the system (on the root filesystem), as per this article.
sudo swapon --show #shows if swap is available and usedfree --giga -h #shows the RAMsudo fallocate -l 2G /swapfilesudo chmod 600 /swapfilesudo mkswap /swapfilesudo swapon /swapfilesudo swapon --showecho "/swapfile none swap sw 0 0" >> /etc/fstabShell constructs
$(output=($(host $(hostname)));echo ${output[3]}) #give external IP address of the machine configured hostname
$ echo $(output=($(host $(hostname)));echo ${output[3]})IP
$ hostnamedomain.com
$ host domain.comdomain.com has address IP
$ echo $(output=($(host $(hostname)));echo ${output[3]})IPLifehacks
How to download dpkg files for the computer, that doesnโt have internet connection (as per this article) - on the โvictimโ machine do:
apt-get --print-uris --yes install <my_package_name> | grep ^\' | cut -d\' -f2 >downloads.listThen on the machine having internet access:
wget --input-file myurilistfdisk -l and mount /dev/sda1 /mnt are other useful commands in command line interface without GUI.
Search for the file in all of the packages (you have the file and donโt know which one you need):
apt-file search file -> search for the package where this file is inOne liners
ls -l /var/run/reboot-required #check if reboot is required, after the apt upgradesudo update-alternatives --config editor #change default editorOR
echo 'SELECTED_EDITOR="/usr/bin/vim"' > .selected_editorsudo dpkg-reconfigure tzdata #change timezonesudo netstat -tulpn #show open portssystemd-resolve --status #show DNS configuration#fixing GPG โNO_PUBKEYโ errorsudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys MISSING_KEYubuntu-distro-info --supported | grep `lsb_release -a 2>/dev/null | grep -i codename | awk '{print $2}'` | wc -l # checking if Ubuntu is supported (1 - if yes; 0 - if not)Creating users
NEWUSER='ubuntu'sudo adduser $NEWUSERsudo usermod -aG sudo $NEWUSER #add user to sudo group, to enable passwordless sudorsync --archive --chown=$NEWUSER:$NEWUSER ~/.ssh /home/$NEWUSER #copy SSH keys# ORsudo su - $UserNamemkdir .ssh && cd .sshtouch authorized_keys && vi authorized_keys && chmod 600 authorized_keyslsof -nP -iTCP -sTCP:LISTEN #MacOS X - find which apps listen which portNTP
systemctl status systemd-timesyncd # get the servers listcat /etc/systemd/timesyncd.conf # configtimedatectltimedatectl timesync-statustimedatectl show-timesyncsystemctl restart systemd-timesyncd # to restartHostname change (Ubuntu)
sudo hostnamectl set-hostname $NewHostnamesudo vi /etc/hostshostnamectl # to verifyMount another disk on Ubuntu (useful for recovery)
sudo lsblk -o NAME,FSTYPE,SIZE,MOUNTPOINT,LABELsudo mount /dev/... /mntRecover encrypted disk
sudo cryptsetup luksOpen /dev/nvme0n1p3 ssdsudo vgchange -yasudo mount /dev/vgmint/root /mnt...sudo umount /dev/mapper/cryptdevsudo vgchange -a nsudo cryptsetup close cryptdevRecover grub
sudo mkdir /mnt/efisudo mount /dev/nvme0n1p1 /mnt/efisudo grub-install --root-directory=/mnt/efi /dev/nvme0n1p1 # not sure about the destinationNetworking on Ubuntu
unbound troubleshooting
systemctl stop unboundunbound -dd -vvvvvDNS
DNS is failing for some reason on Ubuntu behind firewall:
# sudo systemctl status systemd-resolved # to see if it's failing# sudo systemctl start systemd-resolved # to start it upsudo resolvectl status # shows DNS servers in useThe content below is only for Ubuntu 18.04 and higher (the network has been changed dramatically in 18.04).
iptables port forward
iptables -t nat -A PREROUTING -p tcp --dport 443 -j DNAT --to-destination <destination-ip>:27619iptables -t nat -A POSTROUTING -j MASQUERADEAND - make it persistent!
iptables perstistency
vi /etc/network/if-pre-up.d/iptablesload && chmod +x /etc/network/if-pre-up.d/iptablesload
#!/bin/shiptables-restore < /etc/iptables.rulesexit 0To save, do:
iptables-save > /etc/iptables.rulesUFW on top of Docker (as per the doc)
sudo ufw default allow incomingsudo ufw default allow outgoingsudo ufw enable
sudo wget -O /usr/local/bin/ufw-docker \ https://github.com/chaifeng/ufw-docker/raw/master/ufw-dockersudo chmod +x /usr/local/bin/ufw-dockersudo ufw-docker installsudo systemctl restart ufwsudo ufw-docker checkFirewall check
ufw status verboseiptables -Siptables -nvLiptables -L -n -t nat # NATiptables cleanup
as per
iptables -P INPUT ACCEPTiptables -P FORWARD ACCEPTiptables -P OUTPUT ACCEPTiptables -t nat -Fiptables -t mangle -Fiptables -Fiptables -XBasic firewall rules** (ref)
sudo ufw allow sshsudo ufw allow proto tcp from any to any port 80,443 #HTTP & HTTPSsudo ufw default deny incomingsudo ufw default allow outgoingsudo ufw show added #shows rules, without enabling firewallsudo ufw status numbered #shows rules, numberedsudo ufw delete 2 #remove the rulesudo ufw enablesudo ufw disableEnabling NAT (as per article)
as per ref, 2025-02-22:
sudo apt -y install ufwsudo sed -i 's/DEFAULT_FORWARD_POLICY="DROP"/DEFAULT_FORWARD_POLICY="ACCEPT"/' /etc/default/ufwsudo sed -i 's/DEFAULT_INPUT_POLICY="DROP"/DEFAULT_INPUT_POLICY="ACCEPT"/' /etc/default/ufwgrep FORWARD_POLICY /etc/default/ufwgrep INPUT_POLICY /etc/default/ufwsudo sed -i 's|#net/ipv4/ip_forward=1|net/ipv4/ip_forward=1|' /etc/ufw/sysctl.confgrep ip_forward /etc/ufw/sysctl.conf
echo '# NAT table rules*nat:POSTROUTING ACCEPT [0:0]
# CHANGE ME: IP address of LAN and interface of WAN-A POSTROUTING -s 172.17.1.2/30 -o eth0 -j MASQUERADE
COMMIT' | cat - /etc/ufw/before.rules > /tmp/before_rules.full && cp /tmp/before_rules.full /etc/ufw/before.rules
sudo ufw show added #check status
sudo ufw disable && sudo ufw enableLimiting an access for a specific network interface (ref)
By default, ufw will apply rules to all available interfaces. To limit this, specify DIRECTION on INTERFACE, where DIRECTION is one of in or out (interface aliases are not supported). For example, to allow all new incoming http connections on eth0, use:
ufw allow in on eth0 to any port 80 proto tcp
ufw allow in on eth1 to [eth1 ip addr] port 80 proto tcpSo, denying everything on public port and only enabling specific things would looks like the following:
sudo ufw default deny incoming on ens3 #default to public denysudo ufw allow in on ens4 from any to any #allow everything on LANsudo ufw allow out on ens4 from any to any #allow everything on LANsudo ufw enable #do disk snapshot first!
sudo ufw allow proto tcp on ens3 from any to any port 22sudo ufw allow proto tcp from 10.66.66.0/24 to any port 80,443 #allow traffic via LAN
sudo ufw allow from 192.168.168.0/24 #allow from internal networkBasic security features
Move SSHd to another port
sudo vi /etc/sshd/sshd_config #change Port's 22 to anything you likesudo service sshd restart #it shall be available on a new portTroubleshooting connections (good doc here)
sudo tcpdump -vvv -n host IPsudo tcpdump -vvv -i ens3 host IP port 22sudo tcpdump -n -vvv -i ens3 'host IP and port 22'pwru as well:
/pwru 'src host <source-ip>'Configuring second network interface
Ubuntu 18.04 (ref)
cd /etc/netplan && lssudo vi 50-cloud-init.yaml
#file name starts with 50 something; it won't be overridden, as it's generated during the first initHere is the resulting example file:
network: ethernets: ens3: addresses: [] dhcp4: true optional: true **ens4: addresses: [192.168.168.10/24, ]**
version: 2sudo netplan --debug applyUbuntu 20.04 (ref)
cd /etc/netplan && lssudo vi 01-netcfg.yamlsudo netplan trysudo netplan --debug applyHere is the resulting example file:
network: ethernets: ens4: dhcp4: no addresses: - 192.168.168.1/24
version: 2Setting up routing for the second interface (18.04; ref)
Temporary one:
sudo route add -net 10.0.0.0/8 gw 192.168.1.1 eth0sudo route del -host IP gw 192.168.168.1 ens4sudo route add -host IP gw 192.168.168.10 ens4sudo route add default gw 192.168.168.1 ens4Permanent one:
sudo vi /etc/netplan/50-cloud-init.yamlAnd add routes directive:
ens4: addresses: [192.168.168.10/24, ] routes: - to: 10.0.0.0/8 via: 192.168.168.1Check kernel level packets forward:
cat /proc/sys/net/ipv4/ip_forwardSingle interface configuration netplan:
network: ethernets: ens3: addresses: [192.168.168.22/24, ] gateway4: 192.168.168.1 routes: - to: 10.0.0.0/8 via: 192.168.168.1 version: 2One liners
Extract external IP address and add 1 at the end - which is usually a gateway.
ip addr show ens3 | awk '/inet/ {print $2}' | cut -d/ -f1 | head -1 | awk -F '.' '{ print $1"."$2"."$3"."1;}or use
sudo apt-get install sipcalcsipcalc -I ens3that gives first server in the network (which is usually a gateway)
sipcalc -I ens3 -i | grep "Usable range" | cut -d "-" -f 2 | xargsTemporary setup default router
sudo route add default gw `sipcalc -I ens3 -i | grep "Usable range" | cut -d "-" -f 2 | xargs` ens3Revoke routing back
sudo route del default gw `sipcalc -I ens3 -i | grep "Usable range" | cut -d "-" -f 2 | xargs` ens3Ubuntu - Remap your mouse button(s)
Used it to map third button for โExposeโ like.
sudo apt install git python3-setuptools gettext git clone https://github.com/sezanzeb/key-mapper.git cd key-mapper && ./scripts/build.sh sudo apt install ./dist/key-mapper-1.2.1.deb
keyboard โ shortcuts โ show the window selection screen โ map some key (F7 in my case), then add desired button (middle mouse button in my case) to F7 - enjoy!