How to Schedule a One-Time Command Execution After Reboot in Linux (RunOnce Equivalent)


1 views

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