When working with Linux systems, particularly in automated deployment scenarios, I often need to execute a command exactly once after system reboot. Unlike Windows which has a dedicated RunOnce registry mechanism, Linux requires a more creative approach.
Many administrators reach for cron's @reboot
directive first, but this executes the command on every reboot. For our use case, we need a self-destructing mechanism that removes the job after first execution.
Here's a robust method I've used across various Linux distributions including SLES (as in the question's environment):
#!/bin/bash
# Add this to /etc/rc.local before 'exit 0'
if [ -f /tmp/runonce_flag ]; then
/path/to/your/command
# Self-clean from rc.local
sed -i '/\/tmp\/runonce_flag/d' /etc/rc.local
rm -f /tmp/runonce_flag
fi
# Create the trigger file
touch /tmp/runonce_flag
For systems where modifying rc.local isn't ideal, we can implement a cron-based solution with atomic file operations:
# In crontab -e
@reboot /usr/bin/flock -n /tmp/runonce.lock -c "/path/to/command && crontab -l | grep -v '@reboot /usr/bin/flock' | crontab -"
For modern systems with systemd, we can create a one-shot service:
# /etc/systemd/system/runonce.service
[Unit]
Description=RunOnce Service
After=network.target
[Service]
Type=oneshot
ExecStart=/path/to/your/command
ExecStartPost=/bin/rm -f /etc/systemd/system/runonce.service
[Install]
WantedBy=multi-user.target
Enable it with:
systemctl enable runonce.service
Always test your RunOnce implementation by:
1. Checking system logs (journalctl -xe
for systemd)
2. Verifying the cleanup occurred
3. Testing with a harmless command first like touch /tmp/testfile
The rc.local method works on:
- Debian/Ubuntu (enable rc-local service if needed)
- RHEL/CentOS/SLES
- Most traditional init systems
For systems without rc.local, the cron or systemd approaches are preferable. Always consider your specific environment's constraints.
While Linux's crontab @reboot
is perfect for persistent startup tasks, it falls short when you need one-time execution. The Windows RunOnce registry key has no direct Linux equivalent, but we can build a robust solution.
For most modern systems using systemd, we can leverage /etc/rc.local
with automatic cleanup:
#!/bin/bash
# Place this in /etc/rc.local (before 'exit 0' if present)
COMMAND="/path/to/your_script.sh"
LOG="/var/log/runonce.log"
# Execute command
$COMMAND >> $LOG 2>&1
# Remove itself from rc.local
sed -i '/\/path\/to\/your_script.sh/d' /etc/rc.local
exit 0
A more elegant approach using systemd:
# /etc/systemd/system/runonce.service
[Unit]
Description=RunOnce Service
After=network.target
[Service]
Type=oneshot
ExecStart=/path/to/your_script.sh
RemainAfterExit=no
[Install]
WantedBy=multi-user.target
Then enable it with:
sudo systemctl enable --now runonce.service
For systems without systemd, modify your crontab approach:
@reboot /path/to/wrapper_script.sh
Wrapper script:
#!/bin/bash
/path/to/actual_command.sh
(crontab -l | grep -v "@reboot /path/to/wrapper_script.sh") | crontab -
For mission-critical operations, use a flag file system:
#!/bin/bash
FLAGFILE="/var/run/.firstboot"
if [ ! -f "$FLAGFILE" ]; then
# Your one-time commands here
touch "$FLAGFILE"
fi
Remember to:
- Set proper permissions on scripts (chmod 700)
- Use absolute paths
- Validate script integrity
- Implement proper logging
For cloud deployments where cloud-init isn't available:
#!/bin/bash
# /etc/rc.local
if [ -f /root/.firstboot ]; then
exit 0
fi
# Initial configuration commands
hostnamectl set-hostname my-new-server
apt-get update && apt-get upgrade -y
# Mark as completed
touch /root/.firstboot