Post

Linux Optimization & Security Manual (Zorin & Ubuntu-Based Systems)

Field-tested, reversible tweaks for Zorin OS 17.3 / 18 and Ubuntu-based systems: performance, power, and security.

Linux Optimization & Security Manual (Zorin & Ubuntu-Based Systems)

Manual icon

In late 2025, with Windows 10 reaching end-of-support and millions of otherwise capable computers being declared “obsolete” by design, many users are looking for sustainable, secure ways to keep their machines productive.

Zorin OS — a polished, Ubuntu-based Linux distribution — provides a bridge between familiarity and freedom. It allows users to escape vendor-locked ecosystems while continuing to use existing hardware with a supported, modern operating system.

This manual was written to help anyone — whether migrating from Windows or refining an existing Linux setup — understand and apply safe, meaningful optimizations. It reflects real-world experience with Zorin 17.3 and 18 (built on different Ubuntu bases), and most of the tweaks also apply to Ubuntu 22.04 / 24.04 LTS, Linux Mint, and other Ubuntu-based systems.

The goal is not to chase artificial benchmarks, but to balance performance, stability, and community values — so your system stays fast, reliable, secure, and transparent for years to come.

Sinisa Bozic · sbozich.github.io


Contents


Updated for 2025 — Zorin 17.3 & Ubuntu 24.04 LTS
This practical manual covers safe, reversible system optimizations for Zorin OS and other Ubuntu-based distros.

⚙️ 1. System Overview — (Scope & Safety Rules)

Target Environments

  • Zorin OS 17.3 / 18
  • Ubuntu 22.04 / 24.04 LTS
  • Linux Mint and derivatives
  • GNOME and Wayland assumed (most runs on Xorg too)

Guiding Principles

  1. Every tweak must be fully reversible.
  2. Never break printing, Bluetooth, or Wi-Fi.
  3. Use upstream-supported mechanisms only (systemd, UFW, APT).
  4. Keep automation transparent — no opaque cron jobs or hidden scripts.

🧰 2. Core System Maintenance — (Keep Base Layer Healthy)

2.1 Preserve APT Auto-Updates — (Kernel & Security Patching)

1
2
systemctl status unattended-upgrades
sudo systemctl enable --now unattended-upgrades

Why: ensures signed kernel and package updates remain in sync with Secure Boot.
Revert: sudo systemctl disable --now unattended-upgrades

RiskBenefit
Very LowEnsures automatic kernel and security patching

2.2 Daily Flatpak Auto-Update — (User-Level, GUI-Independent)

1
2
3
4
5
6
7
8
mkdir -p ~/bin
cat <<'EOF' > ~/bin/zorin-flatpak-auto-update.sh
#!/usr/bin/env bash
LOGFILE="$HOME/.local/share/flatpak-auto.log"
mkdir -p "$(dirname "$LOGFILE")"
{ echo; echo "=== $(date '+%Y-%m-%d %H:%M:%S') ==="; flatpak update -y; } >> "$LOGFILE" 2>&1
EOF
chmod +x ~/bin/zorin-flatpak-auto-update.sh

Create service and timer:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
mkdir -p ~/.config/systemd/user
cat <<'EOF' > ~/.config/systemd/user/zorin-flatpak-auto-update.service
[Unit]
Description=Automatic Flatpak updates (Zorin)
[Service]
Type=oneshot
ExecStart=%h/bin/zorin-flatpak-auto-update.sh
EOF

cat <<'EOF' > ~/.config/systemd/user/zorin-flatpak-auto-update.timer
[Unit]
Description=Run automatic Flatpak updates daily
[Timer]
OnCalendar=daily
Persistent=true
[Install]
WantedBy=default.target
EOF

systemctl --user daemon-reload
systemctl --user enable --now zorin-flatpak-auto-update.timer

Verify:

1
2
systemctl --user list-timers | grep flatpak
tail -n 10 ~/.local/share/flatpak-auto.log

Revert:

1
2
3
systemctl --user disable --now zorin-flatpak-auto-update.timer
rm ~/.config/systemd/user/zorin-flatpak-auto-update.*
rm ~/bin/zorin-flatpak-auto-update.sh
RiskBenefit
MinimalKeeps Flatpak apps current without GUI

⚡ 3. Performance & Responsiveness — (Reduce I/O and Background Load)

3.1 Lower Swappiness — (Reduce Disk Thrashing)

1
2
3
cat /proc/sys/vm/swappiness
echo 'vm.swappiness=10' | sudo tee /etc/sysctl.d/90-swappiness.conf
sudo sysctl -p /etc/sysctl.d/90-swappiness.conf

Why: with ≥8 GB RAM, reduces swapping and improves responsiveness.
Revert: sudo rm /etc/sysctl.d/90-swappiness.conf && sudo sysctl -p

RiskBenefit
LowFaster multitasking and reduced SSD wear

3.2 Enable Weekly SSD TRIM — (Maintain Write Performance)

1
2
sudo systemctl enable --now fstrim.timer
systemctl status fstrim.timer

Why: trims unused blocks weekly, preserving SSD speed.
Revert: sudo systemctl disable --now fstrim.timer

RiskBenefit
NoneSustains SSD performance automatically

3.3 Limit Journal Size — (Prevent Log Bloat)

1
2
3
sudo mkdir -p /etc/systemd/journald.conf.d
printf '%s\n' '[Journal]' 'SystemMaxUse=200M' | sudo tee /etc/systemd/journald.conf.d/size-limit.conf
sudo systemctl restart systemd-journald

Why: prevents logs from consuming disk space.
Revert: sudo rm /etc/systemd/journald.conf.d/size-limit.conf && sudo systemctl restart systemd-journald

RiskBenefit
LowPrevents storage bloat without losing important logs

3.4 Disable Non-Essential Services — (Reduce Boot Latency)

1
2
sudo systemctl disable --now man-db.timer smartmontools.service
sudo systemctl mask NetworkManager-wait-online.service

Why: disables slow or redundant background services.
Revert:

1
2
sudo systemctl unmask NetworkManager-wait-online.service
sudo systemctl enable --now man-db.timer smartmontools.service
RiskBenefit
Medium (loss of SMART alerts, slower manual man updates)Shorter boot and lower background I/O

🔋 4. Power & Device Management — (Efficiency & Longevity)

4.1 TLP Power Optimizer

1
2
3
sudo apt install -y tlp tlp-rdw
sudo systemctl enable --now tlp
sudo tlp-stat -s

Why: replaces multiple vendor daemons with one policy manager.
Revert: sudo systemctl disable --now tlp

RiskBenefit
LowLonger battery life, cooler operation

4.2 Lid Switch Policy

1
sudo nano /etc/systemd/logind.conf

Set desired behavior:
HandleLidSwitch=suspend or ignore for desktops.
Restart logind:

1
sudo systemctl restart systemd-logind
RiskBenefit
LowPredictable lid behavior; avoids accidental suspends

🖥️ 5. GNOME / Wayland UX — (Minimalist Comfort)

5.1 Refine Screen Lock Timing

1
2
gsettings set org.gnome.desktop.session idle-delay 900
gsettings set org.gnome.desktop.screensaver lock-delay 30
RiskBenefit
NonePrevents frequent lock interruptions

5.2 Hide Activities Button

1
gsettings set org.gnome.shell.extensions.dash-to-dock show-show-apps-button false
RiskBenefit
NoneCleaner dock layout and visual focus

5.3 Middle-Click Show Desktop

1
2
sudo apt install -y xdotool
xdotool key Super+d

Bind via Settings → Keyboard → Custom Shortcuts.

RiskBenefit
NoneFaster workspace visibility and navigation

5.4 — Set Up and Manage PIN Login on Zorin OS (GDM)

This section explains how to enable a secure PIN-based login and unlock on Zorin OS (GNOME / GDM) using libpam-pwdfile.
The PIN works alongside your regular password — you can use either at any time.


🧱 1. Install Required Packages

1
2
sudo apt update
sudo apt install -y libpam-pwdfile whois

🗂️ 2. Back Up the Original PAM Configuration

Always back up before editing PAM files.

1
2
3
sudo cp /etc/pam.d/gdm-password /etc/pam.d/gdm-password.bak
sudo chown root:root /etc/pam.d/gdm-password.bak
sudo chmod 600 /etc/pam.d/gdm-password.bak

🧾 3. Create the PIN Storage File

1
2
3
sudo touch /etc/custompinfile
sudo chown root:root /etc/custompinfile
sudo chmod 600 /etc/custompinfile

🔑 4. Add User + PIN Securely

Enter your PIN interactively so it isn’t exposed in shell history.

1
2
3
4
5
read -s -p "Enter PIN for current user: " PIN; echo
HASH=$(mkpasswd -m sha-512 "$PIN")
sudo bash -c "echo \"$(whoami):$HASH\" >> /etc/custompinfile"
sudo chown root:root /etc/custompinfile
sudo chmod 600 /etc/custompinfile

View it (optional) — only the hashed value is shown:

1
sudo cat /etc/custompinfile

Add this line to the top of /etc/pam.d/gdm-password:

1
sudo sed -i '1i auth sufficient pam_pwdfile.so pwdfile=/etc/custompinfile' /etc/pam.d/gdm-password

Confirm:

1
sudo sed -n '1,10p' /etc/pam.d/gdm-password

You should see:

1
2
3
4
auth sufficient pam_pwdfile.so pwdfile=/etc/custompinfile
#%PAM-1.0
auth    requisite       pam_nologin.so
...

🧪 6. Test the PIN

Lock the screen (Super + L) or log out.

Enter your PIN and press Enter.

If it fails once, log in with your normal password, then lock again and retry (PAM caches update after first login).

🩺 7. Verify Security

1
ls -l /etc/custompinfile

Expected:

1
-rw------- 1 root root ...

If needed:

1
2
sudo chown root:root /etc/custompinfile
sudo chmod 600 /etc/custompinfile

🧹 Disable or Remove PIN Login

You can temporarily disable or fully remove the feature.

A. Temporarily Disable PIN Login

Comment out the line in /etc/pam.d/gdm-password:

1
sudo sed -i 's|^auth sufficient pam_pwdfile.so pwdfile=/etc/custompinfile|#auth sufficient pam_pwdfile.so pwdfile=/etc/custompinfile|' /etc/pam.d/gdm-password

To re-enable:

1
sudo sed -i 's|^#auth sufficient pam_pwdfile.so pwdfile=/etc/custompinfile|auth sufficient pam_pwdfile.so pwdfile=/etc/custompinfile|' /etc/pam.d/gdm-password
B. Completely Remove PIN Login

Restore the original configuration and delete the PIN file:

1
2
3
sudo cp /etc/pam.d/gdm-password.bak /etc/pam.d/gdm-password
sudo chmod 644 /etc/pam.d/gdm-password
sudo rm -f /etc/custompinfile

🧯 Recovery if Locked Out

Boot into Advanced → Recovery Mode → root shell.

Remount root as writable:

1
mount -o remount,rw /

Restore backup:

1
cp /etc/pam.d/gdm-password.bak /etc/pam.d/gdm-password

Reboot:

1
reboot

🔄 Update PIN

1
2
3
4
read -s -p "Enter NEW PIN: " PIN; echo
HASH=$(mkpasswd -m sha-512 "$PIN")
sudo sed -i "/^$(whoami):/d" /etc/custompinfile
sudo bash -c "echo \"$(whoami):$HASH\" >> /etc/custompinfile"

✅ Summary

StepPurposeExample command / action
1Install dependenciessudo apt install libpam-pwdfile whois
2Backup PAM filesudo cp /etc/pam.d/gdm-password /etc/pam.d/gdm-password.bak
3Create PIN filesudo touch /etc/custompinfile && sudo chmod 600 /etc/custompinfile
4Add PIN interactivelysudo sh -c 'mkpasswd -m sha-512 >> /etc/custompinfile' (you’ll be asked for the PIN)
5Insert PAM rule at top of filesudo sed -i '1iauth sufficient pam_pwdfile.so pwdfile=/etc/custompinfile' /etc/pam.d/gdm-password
6TestLock the screen → enter PIN
Disable (temporarily)sudo sed -i 's/^auth sufficient pam_pwdfile.so pwdfile=\\/etc\\/custompinfile/# &/' /etc/pam.d/gdm-password
Remove (restore original state)sudo mv /etc/pam.d/gdm-password.bak /etc/pam.d/gdm-password && sudo rm /etc/custompinfile

Note: This method uses standard PAM modules and doesn’t modify GDM’s graphical interface. It’s secure when /etc/custompinfile is root-owned and chmod 600. Always keep your password login active as fallback. whois provides the mkpasswd command used to hash the PIN.


5.5 Listing All Installed Programs (APT, Flatpak, AppImage & GNOME Extensions)

This utility script provides a comprehensive overview of your installed applications and GNOME extensions on Zorin OS or any Ubuntu-based Linux distribution.

It detects:

  • APT packages you manually installed (excluding system defaults)
  • Flatpak apps, both user and system scope
  • AppImages in common directories (e.g. ~/Applications, ~/Downloads)
  • AppImageLauncher-integrated desktop entries
  • GNOME extensions, including name, description, and source URL

At the end of execution, the script prints results directly to terminal and asks if you want to save them to your home folder.
If confirmed, it writes a timestamped .txt file and opens it automatically in your default text editor.


🧩 How to Use

  1. Create the script file:
    1
    
    nano ~/bin/list-installed.sh
    
  2. Paste the full code below into the file.

  3. Save with Ctrl + O, exit with Ctrl + X, and make it executable:
    1
    
    chmod +x ~/bin/list-installed.sh
    
  4. Run anytime with:
    1
    
    list-installed.sh
    

📜 The Script

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
#!/bin/bash
# ===============================================================
#  List installed software on Zorin / Ubuntu-like systems
#  - APT (manual/user-installed)
#  - Flatpak (user + system)
#  - AppImages (common locations)
#  - AppImageLauncher desktop entries
#  - GNOME extensions (user + system) with metadata
#  Shows in terminal, then asks to save to $HOME
# ===============================================================

TMPFILE=$(mktemp)

teeout() {
  tee -a "$TMPFILE"
}

echo "===== USER-INSTALLED APT PACKAGES =====" | teeout
comm -23 \
  <(apt-mark showmanual | sort) \
  <(gzip -dc /var/log/installer/initial-status.gz 2>/dev/null | awk '/Package: / {print $2}' | sort) | teeout

echo | teeout
echo "===== FLATPAK (USER SCOPE) =====" | teeout
flatpak list --user --app --columns=application,version,origin 2>/dev/null | teeout || echo "No user-scope flatpaks or flatpak not installed." | teeout

echo | teeout
echo "===== FLATPAK (SYSTEM SCOPE) =====" | teeout
flatpak list --system --app --columns=application,version,origin 2>/dev/null | teeout || echo "No system-scope flatpaks." | teeout

echo | teeout
echo "===== APPIMAGE PROGRAMS (detected in common locations) =====" | teeout
SEARCH_DIRS=(
  "$HOME/Applications"
  "$HOME/.local/bin"
  "$HOME/Downloads"
  "$HOME/.local/share/applications"
)
found_any=false
for dir in "${SEARCH_DIRS[@]}"; do
  if [ -d "$dir" ]; then
    matches=$(find "$dir" -maxdepth 2 -type f -iname "*.AppImage" 2>/dev/null)
    if [ -n "$matches" ]; then
      echo "In: $dir" | teeout
      echo "$matches" | teeout
      echo | teeout
      found_any=true
    fi
  fi
done
if [ "$found_any" = false ]; then
  echo "No AppImages found in common locations." | teeout
fi

echo | teeout
echo "===== APPIMAGELAUNCHER-INTEGRATED ENTRIES (.desktop) =====" | teeout
launcher_entries=$(ls "$HOME/.local/share/applications/"*.desktop 2>/dev/null | grep -i appimage || true)
if [ -n "$launcher_entries" ]; then
  echo "$launcher_entries" | teeout
else
  echo "No AppImageLauncher desktop entries found." | teeout
fi

print_extensions_from_dir() {
  local DIR="$1"
  local LABEL="$2"

  echo | teeout
  echo "----- $LABEL -----" | teeout

  if [ ! -d "$DIR" ]; then
    echo "Directory not found: $DIR" | teeout
    return
  fi

  for extdir in "$DIR"/*; do
    [ -d "$extdir" ] || continue
    metadata="$extdir/metadata.json"
    if [ -f "$metadata" ]; then
      python3 - <<'PY' | teeout
import json, pathlib, os
md_path = pathlib.Path(os.environ.get('METADATA'))
try:
    data = json.loads(md_path.read_text(encoding="utf-8"))
except Exception:
    print(f"* {md_path.parent.name} (could not parse metadata.json)")
else:
    uuid = data.get("uuid", md_path.parent.name)
    name = data.get("name", "")
    desc = data.get("description", "")
    url = data.get("url", "")
    print(f"* {uuid}")
    if name:
        print(f"    name: {name}")
    if desc:
        print(f"    desc: {desc}")
    if url:
        print(f"    url:  {url}")
PY
    else
      echo "* $(basename "$extdir") (no metadata.json)" | teeout
    fi
  done
}

echo | teeout
echo "===== GNOME EXTENSIONS (USER, with metadata) =====" | teeout
print_extensions_from_dir "$HOME/.local/share/gnome-shell/extensions" "User extensions"

echo | teeout
echo "===== GNOME EXTENSIONS (SYSTEM, with metadata) =====" | teeout
print_extensions_from_dir "/usr/share/gnome-shell/extensions" "System extensions"

echo | teeout
echo "===== ENABLED GNOME EXTENSIONS =====" | teeout
if command -v gnome-extensions &>/dev/null; then
  gnome-extensions list --enabled 2>/dev/null | teeout || echo "No enabled extensions detected." | teeout
else
  echo "gnome-extensions command not found." | teeout
fi

echo | teeout
echo "===== SUMMARY =====" | teeout
echo "Run date: $(date)" | teeout

echo
read -rp "Save this report to your home folder? (y/n): " confirm
if [[ "$confirm" =~ ^[Yy]$ ]]; then
  OUTFILE="$HOME/installed_$(date +%Y-%m-%d_%H-%M).txt"
  mv "$TMPFILE" "$OUTFILE"
  echo "Saved to: $OUTFILE"
  if command -v xdg-open >/dev/null 2>&1; then
    xdg-open "$OUTFILE" >/dev/null 2>&1 &
  fi
else
  rm -f "$TMPFILE"
  echo "Report not saved."
fi

💡 Tips

  • Run this after setting up your Linux system — it gives a complete inventory useful for backup or reinstallation.
  • You can version-control your installed_*.txt reports in GitHub for reference.
  • Add an alias for quick access:
    1
    
    alias listapps='~/bin/list-installed.sh'
    

🧾 Output Example

1
2
3
4
5
6
7
8
9
===== USER-INSTALLED APT PACKAGES =====
gimp
curl
htop

===== FLATPAK (USER SCOPE) =====
com.github.tchx84.Flatseal   1.10.0   flathub
org.videolan.VLC             3.0.21   flathub
...

🏁 Result

Running this script gives you:

  • A live overview of all installed software
  • A clean, optional export file in your home directory
  • A reproducible record of your environment for migrations or audits

🌐 6. Connectivity & Peripherals — (Stable Wireless and Bluetooth)

6.1 Bluetooth Resume

1
sudo systemctl enable --now bluetooth.service
RiskBenefit
NoneEnsures Bluetooth auto-starts after suspend

6.2 Wi-Fi Powersave Fix

1
2
echo -e '[connection]\nwifi.powersave=2' | sudo tee /etc/NetworkManager/conf.d/wifi-powersave.conf
sudo systemctl restart NetworkManager
RiskBenefit
NonePrevents Wi-Fi dropouts and improves stability

🔒 7. Security & Access — (Harden Without Bloat)

7.1 Firewall

1
2
3
4
sudo ufw enable
sudo ufw allow ssh
sudo ufw allow 445/tcp
sudo ufw status
RiskBenefit
LowProtects network services with minimal overhead

7.2 ClamAV

1
2
sudo apt install -y clamav clamtk
sudo freshclam
RiskBenefit
Low (CPU use during scans)Detects common malware and infected USBs

7.3 Rootkit Hunter

1
2
3
sudo apt install -y rkhunter
sudo rkhunter --update
sudo rkhunter --check
RiskBenefit
LowDetects potential rootkits and hidden binaries

7.4 Password Managers

Bitwarden (cloud) or KeePassXC (offline).

1
flatpak install flathub org.keepassxc.KeePassXC
RiskBenefit
NoneSecure credential storage and autofill

⏱️ 8. Automation & Scheduling

1
2
3
systemctl list-timers --all
sudo apt autoremove --purge && sudo apt clean
tail -20 ~/.local/share/flatpak-auto.log
RiskBenefit
Very LowKeeps system clean and maintenance automated

🧬 9. Firmware & Boot Integrity

1
2
3
[ -d /sys/firmware/efi ] && echo "UEFI mode" || echo "Legacy BIOS mode"
mokutil --sb-state
sudo fwupdmgr get-updates
RiskBenefit
NoneConfirms Secure Boot and firmware health

🧹 10. Optional Reverts — (Return to Stock State)

1
2
3
dconf reset -f /
sudo systemctl unmask NetworkManager-wait-online.service
sudo systemctl enable --now man-db.timer smartmontools.service
RiskBenefit
Medium (resets all GNOME tweaks)Full restoration of Zorin default behavior

🧭 11. Summary & Philosophy

Tune your system because you understand it — not because “a list said so.”
The best optimization is one you can confidently undo.

PrincipleGoal
Keep APT updates and UFW activeMaintain secure base
Automate Flatpak updatesEnsure app freshness
Lower swappinessBoost responsiveness
Enable SSD TRIMPreserve SSD longevity
Verify Secure BootMaintain integrity

Author: Sinisa Bozic · 2025
Linux Optimization & Security Manual — Zorin & Ubuntu-based Systems

This post is licensed under CC BY 4.0 by the author.