Automating Daily Updates on Pop!_OS Using systemd Timer

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:

  1. A Bash script that performs the updates
  2. A systemd service to define how the script runs
  3. 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

LinePurpose
set -euo pipefailExits on any error, undefined variable, or broken pipe
logger -t daily-updateSends logs to journalctl under the tag daily-update
DEBIAN_FRONTEND=noninteractivePrevents interactive prompts during unattended upgrades
apt-get updateFetches the latest package lists
dist-upgradeInstalls all available upgrades, resolving dependencies
autoremoveCleans unused packages automatically
flatpak updateUpdates 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

SettingMeaning
OnCalendar=dailyRuns once every 24 hours
RandomizedDelaySec=1hAdds a random 0–1 hour delay to avoid network congestion
Persistent=trueRuns 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.

Leave a Reply

Your email address will not be published. Required fields are marked *