Backups are the lifeblood of any homelab or small server. I usually run a nightly rsync job that copies hundreds of gigabytes from a production NFS share to a local Btrfs snapshot. On a 4‑core workstation the job is usually a quiet background task, but sometimes the process spikes to 100 % CPU and the terminal I use to monitor the job freezes. The culprit is often the default scheduling class and priority that rsync inherits from the shell.
Below is a step‑by‑step guide to diagnosing the problem with ps, lowering the priority with nice, and throttling I/O with ionice. The techniques are generic enough for any backup tool—tar, borg, duplicity, or even a custom Python script.
1. Spot the culprit with ps
The first thing I do is check the process list. A single line of ps is usually enough:
ps -eo pid,ppid,ni,pri,pcpu,pmem,stat,comm | grep rsync
| Field | Meaning |
|---|---|
pid |
Process ID |
ppid |
Parent PID |
ni |
Nice value |
pri |
Priority (nice + static priority) |
pcpu |
% CPU usage |
pmem |
% memory usage |
stat |
State (e.g., R for running) |
comm |
Command name |
Typical output:
1234 1200 0 20 99.5 1.2 R rsync
Notice the pri of 20 (default) and the pcpu of 99.5 %. The nice value is 0, meaning the process is unmodified. If the CPU usage stays high for more than a few seconds, the job is likely the main source of contention.
2. Lower the CPU priority with nice
Linux’s scheduler lets you influence how much CPU time a process gets by adjusting its nice value. The range is –20 (most favorable) to +19 (least favorable). A negative nice value requires root privileges; a positive value is safe for ordinary users.
nice -n 10 rsync -a /mnt/nfs/ /mnt/backup/
Trade‑offs
| Nice value | Effect | When to use |
|---|---|---|
-10 |
Gives the job a higher share of CPU, useful for time‑critical backups | When you need the backup to finish before a scheduled maintenance window |
+10 |
Reduces CPU share, keeps the system responsive | When the backup runs during peak user activity |
0 |
Default | Normal background job |
If you already have a running job, you can change its nice value with renice:
renice +10 -p 1234
This command is non‑intrusive and works even if the process is in the middle of a large file copy.
3. Throttle disk I/O with ionice
CPU is only part of the problem. A backup that reads and writes a lot of data can saturate the block device, causing other I/O‑heavy tasks (e.g., a database, a media server) to stall. ionice lets you set the I/O scheduling class and priority.
ionice -c2 -n7 rsync -a /mnt/nfs/ /mnt/backup/
-c2– Best‑Effort class (default for normal users)-n7– Lowest priority within that class (0 is highest)
The Best‑Effort class is appropriate for most backups. If you need to guarantee a minimum I/O bandwidth (e.g., for a real‑time video stream), you can use the Idle class (-c3) which only runs when no other process needs the disk.
Why ionice matters
- Disk contention: A single
rsynccan consume up to 90 % of a SATA drive’s bandwidth. Other services may experience latency spikes. - Power‑saving: On laptops, throttling I/O can reduce heat and power draw, extending battery life during a backup.
- Security: By limiting I/O, you reduce the risk of a backup script being used as a side‑channel to exfiltrate data through disk activity patterns.
4. Combine nice and ionice in a wrapper script
For repeatability, I wrap the two commands in a small shell script:
#!/usr/bin/env bash
# backup.sh – run rsync with low CPU and I/O priority
set -euo pipefail
TARGET="/mnt/backup"
SRC="/mnt/nfs"
nice -n 10 ionice -c2 -n7 rsync -a "$SRC/" "$TARGET/" 2>&1 | tee /var/log/backup.log
Make it executable:
chmod +x backup.sh
Now I can schedule it with systemd or cron and be confident that it will stay in the background.
5. Verify the effect with top and iotop
After launching the wrapper, open two terminals:
top -p $(pgrep -f backup.sh)
You should see the rsync process with a lower CPU share (e.g., 5–10 %) and a nice value of 10.
sudo iotop -o
The iotop output will show the I/O rate for the process. With ionice -n7, the I/O usage should be modest compared to an unthrottled run.
6. Common pitfalls and troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
| Backup still hogs CPU | nice value not applied (e.g., script runs as a different user) |
Ensure the script runs under the same UID that set the nice value |
| Disk I/O remains high | Using ionice -c3 (Idle) on a device that is heavily used by other processes |
Switch to -c2 -n7 or adjust the priority |
rsync fails with “Permission denied” |
The backup destination is owned by root | Run the script with sudo or change ownership of the target directory |
iotop shows “no device” |
The device is a virtual block device (e.g., LVM thin pool) | Use -d to specify the device or rely on the default Best‑Effort class |
7. Security side‑note
When you run backups as root (common on servers), remember that nice and ionice are not security controls. They only affect scheduling. If your backup script pulls data from a network share, ensure that the share is mounted with the least privilege possible (nosuid,nodev,noexec) and that the credentials are stored in a secure keyring or systemd secret.
Also, keep the backup destination encrypted (e.g., LUKS) if it contains sensitive data. The combination of low‑priority scheduling and encryption protects both system performance and data confidentiality.
8. A real‑world example
On my home NAS, I run:
#!/usr/bin/env bash
# nightly-backup.sh
nice -n 10 ionice -c2 -n7 rsync -a --delete /srv/data/ /mnt/backup/
Scheduled via systemd:
[Unit]
Description=Nightly backup of /srv/data
After=network-online.target
[Service]
Type=oneshot
ExecStart=/usr/local/bin/nightly-backup.sh
User=backup
Group=backup
Nice=10
IOAccounting=yes
[Install]
WantedBy=timers.target
The Nice=10 line in the unit file is redundant but serves as a safety net if the script is launched directly by systemd. The backup completes in ~45 minutes, never exceeding 15 % CPU, and the rest of the system remains responsive.
9. When to go beyond nice/ionice
If you need finer control, consider:
- cgroups v2: Allocate a specific CPU quota (
cpu.max) and I/O bandwidth (io.max) to the backup process. See the kernel documentation for details: https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v2.html. - systemd‑slice: Create a dedicated slice for backup jobs and set default CPU and I/O limits there.
- Btrfs snapshots: Use
btrfs subvolume snapshotto create a point‑in‑time copy before the backup starts. This reduces the amount of data thatrsyncmust transfer and can improve performance.
10. Bottom line
A backup script that spikes CPU or saturates disk I/O can make a quiet workstation feel like a server under load. By inspecting the process with ps, lowering its priority with nice, and throttling I/O with ionice, you can keep the terminal responsive and the rest of the system healthy. Wrap the commands in a small script, schedule it with systemd, and monitor the impact with top and iotop. The result is a reliable, low‑impact backup routine that fits neatly into a homelab or small‑server workflow.
See also
- Why my headphone jack stopped working after the latest update—and how a single udev rule brought it back
- How I Configured Caddy as a Reverse Proxy for My Home Lab and Solved the HTTPS Redirect Bug
- How I Prevented a Rootless Podman Container From Filling My Disk With Logs
- Recovering a single config file from a corrupted Borg backup without restoring the whole archive
- Running My Home NAS Backups with `nice` and `ionice` So My Desktop Never Freezes