Keeping your Linux system up-to-date is crucial for stability, performance, and especially security.
But running sudo apt update && sudo apt upgrade
manually every morning gets repetitive.
Let’s automate it so your Pop!_OS machine updates itself once per day, only on the first boot of the day — not on every restart.
🧠 Goal
We’ll build a lightweight automation that:
- Runs system and Flatpak updates automatically once per day
- Ensures updates happen after the network is online
- Logs everything to
journalctl
for easy review - Works quietly in the background using systemd timers
🧰 Overview
You’ll create three small components:
- A Bash script that performs the updates
- A
systemd
service to define how the script runs - A
systemd
timer to define when it runs (once per day)
🪄 Step 1 — Create the Update Script
File: /usr/local/sbin/daily-update.sh
sudo tee /usr/local/sbin/daily-update.sh >/dev/null <<'EOF'
#!/usr/bin/env bash
set -euo pipefail
# Log everything to the system journal (view with: journalctl -t daily-update)
exec 1> >(logger -t daily-update) 2>&1
export DEBIAN_FRONTEND=noninteractive
# Skip if APT is already running
if pidof apt apt-get >/dev/null; then
echo "APT already running, skipping update."
exit 0
fi
# Refresh package lists
apt-get update
# Upgrade packages safely, keeping existing config files
apt-get -o Dpkg::Options::=--force-confdef \
-o Dpkg::Options::=--force-confold \
-y dist-upgrade
# Clean out old dependencies
apt-get -y autoremove
# Update Flatpak apps if installed
flatpak update -y || true
EOF
sudo chmod +x /usr/local/sbin/daily-update.sh
🔍 Explanation
Line | Purpose |
---|---|
set -euo pipefail | Exits on any error, undefined variable, or broken pipe |
logger -t daily-update | Sends logs to journalctl under the tag daily-update |
DEBIAN_FRONTEND=noninteractive | Prevents interactive prompts during unattended upgrades |
apt-get update | Fetches the latest package lists |
dist-upgrade | Installs all available upgrades, resolving dependencies |
autoremove | Cleans unused packages automatically |
flatpak update | Updates any Flatpak apps, if present |
Check logs anytime with:
journalctl -t daily-update -n 200 --no-pager
⚙️ Step 2 — Create the systemd Service
File: /etc/systemd/system/daily-update.service
[Unit]
Description=Daily system + Flatpak update
Wants=network-online.target
After=network-online.target
[Service]
Type=oneshot
ExecStart=/usr/local/sbin/daily-update.sh
Nice=10
IOSchedulingClass=best-effort
IOSchedulingPriority=7
🧩 What Each Setting Does
- Wants=network-online.target
Ensures network is initialized before running (important for APT). - After=network-online.target
Prevents the update from starting too early at boot. - Type=oneshot
Runs once and exits; not a background service. - Nice / IOSchedulingClass / IOSchedulingPriority
Lowers CPU and disk priority so updates don’t slow your desktop.
⏰ Step 3 — Create the Timer
File: /etc/systemd/system/daily-update.timer
[Unit]
Description=Run daily-update once per day
[Timer]
OnCalendar=daily
RandomizedDelaySec=1h
Persistent=true
Unit=daily-update.service
[Install]
WantedBy=timers.target
🔍 Explanation
Setting | Meaning |
---|---|
OnCalendar=daily | Runs once every 24 hours |
RandomizedDelaySec=1h | Adds a random 0–1 hour delay to avoid network congestion |
Persistent=true | Runs automatically after next boot if missed |
Unit= | Tells the timer which service to trigger |
🚀 Step 4 — Enable and Test
Reload and enable the timer:
sudo systemctl daemon-reload
sudo systemctl enable --now daily-update.timer
Check scheduled timers:
systemctl list-timers | grep daily-update
Run manually (for testing):
sudo systemctl start daily-update.service
View logs:
journalctl -t daily-update -n 50 --no-pager
🧯 Common Error: Permission Denied on Lock File
If you see:
could not open lock file /var/lib/apt/lists/lock - open (13: Permission denied)
it means the script wasn’t run as root.
Use:
sudo /usr/local/sbin/daily-update.sh
When triggered by systemd
, it runs as root automatically.
🧾 Verify It’s Working
- Show next scheduled run
systemctl list-timers --all | grep daily-update
- View last run
systemctl status daily-update.service
- Check logs for today
journalctl -t daily-update --since today
💬 Final Thoughts
This lightweight automation makes your Pop!_OS machine self-maintaining.
It runs quietly once a day, applies updates, and cleans old packages — all while you focus on your work.
For an optional desktop notification, add this to the end of your script:
notify-send "Daily Update" "System packages and Flatpaks are up to date!"
That’s it — your Pop!_OS now keeps itself secure and current every day ✨
✅ Written and tested on Pop!_OS 22.04 LTS (Ubuntu base). Works on any systemd-based distro: Ubuntu, Debian, Fedora, etc.