How to Run Bash Scripts at Startup

How to Run Bash Scripts at Startup

I still remember the first time I needed a script to run automatically every time a server rebooted — a small monitoring tool that had to start without anyone logging in manually. It took some trial and error to understand the right approach, because Linux actually offers several different ways to run scripts at startup, and picking the wrong one leads to scripts that silently fail or don’t run in the environment you expect.

This guide walks through every major method for running Bash scripts at startup, from the simplest desktop-friendly options to production-grade systemd services, along with the reasoning behind when to use each one.

Why Startup Scripts Matter

Startup scripts are essential for automating tasks that need to happen every time a machine boots: starting application servers, mounting network drives, setting up environment variables, launching monitoring agents, or running cleanup tasks. Getting this right means your systems recover automatically after a reboot instead of requiring manual intervention.

Method 1: Using systemd (The Modern Standard)

Most modern Linux distributions (Ubuntu 16.04+, CentOS 7+, Debian 8+) use systemd as their init system, and it’s by far the most robust and recommended way to run scripts at startup.

Here’s how to set it up. First, write your script and make it executable:

#!/bin/bash
echo "Starting my custom service..." >> /var/log/mystartup.log
/usr/local/bin/myapp

Save it as /usr/local/bin/mystartup.sh and make it executable:

chmod +x /usr/local/bin/mystartup.sh

Next, create a systemd service unit file at /etc/systemd/system/mystartup.service:

[Unit]
Description=My Custom Startup Script
After=network.target

[Service]
Type=simple
ExecStart=/usr/local/bin/mystartup.sh
Restart=on-failure
User=john

[Install]
WantedBy=multi-user.target

Let’s break down what each section does:

  • [Unit] describes the service and its dependencies. After=network.target ensures your script only runs after networking is available, which matters if your script needs internet or LAN access.
  • [Service] defines how the process runs. Type=simple means systemd treats the process as running as soon as it’s started. ExecStart points to your script. Restart=on-failure automatically restarts the script if it crashes. User=john runs the script as a specific non-root user for safety.
  • [Install] defines when the service should be enabled. WantedBy=multi-user.target means it starts during normal multi-user boot, which is what you want for most background services.

Now reload systemd’s configuration and enable your service:

sudo systemctl daemon-reload
sudo systemctl enable mystartup.service
sudo systemctl start mystartup.service
  • daemon-reload tells systemd to re-read unit files.
  • enable sets the service to start automatically on every future boot.
  • start runs it immediately, without waiting for a reboot, so you can verify it works.

Check its status any time with:

systemctl status mystartup.service

Method 2: Using crontab with @reboot

If you don’t need the full power of systemd (no restart policies, no dependency management), cron’s @reboot directive is a lightweight alternative.

Open your crontab for editing:

crontab -e

Add a line like this:

@reboot /usr/local/bin/mystartup.sh >> /var/log/mystartup.log 2>&1

This tells cron to run the script exactly once, every time the system boots, and redirect both standard output and errors into a log file so you have a record of what happened. This method is quick, doesn’t require root (each user has their own crontab), and works well for simple personal automation.

Method 3: Using rc.local (Legacy Systems)

Older systems or minimal distributions sometimes still support /etc/rc.local, a script that runs at the end of the boot process.

#!/bin/bash
/usr/local/bin/mystartup.sh &
exit 0

Make sure the file is executable:

sudo chmod +x /etc/rc.local

Note that many modern distributions have deprecated rc.local in favor of systemd, so if it doesn’t exist on your system, you may need to enable it manually or fall back to one of the other methods.

Method 4: Desktop Autostart (.desktop files)

If you’re working on a Linux desktop environment (GNOME, KDE, XFCE) rather than a server, and you want a script to run when you log into your graphical session rather than at full system boot, autostart .desktop files are the right tool.

Create a file at ~/.config/autostart/myscript.desktop:

[Desktop Entry]
Type=Application
Exec=/usr/local/bin/mystartup.sh
Hidden=false
NoDisplay=false
X-GNOME-Autostart-enabled=true
Name=My Startup Script
Comment=Runs my custom script at login

This approach ties execution to a desktop login session rather than system boot, which is a meaningful difference — it’s ideal for user-specific tools like a clipboard manager or notification helper, but not appropriate for background services that should run regardless of whether anyone is logged in.

Method 5: Adding to Shell Profile Files

For lightweight tasks tied to opening a terminal session (not full boot), you can add commands directly to ~/.bashrc or ~/.bash_profile:

echo "Welcome back, $(whoami)! Today is $(date)."

This runs every time a new interactive Bash shell starts, which is useful for setting environment variables or printing reminders, but it is not a substitute for a proper startup service, since it only executes when a shell is actually opened.

How systemd, cron, and rc.local Compare

MethodRuns at bootSurvives crash (auto-restart)Runs without loginBest for
systemdYesYes (configurable)YesProduction services
cron @rebootYesNoYesSimple one-off tasks
rc.localYesNoYesLegacy systems
Desktop autostartNo (login only)NoNoDesktop apps
Shell profileNo (shell only)NoNoInteractive shell setup

Real-World Use Cases

1. Application servers. A Node.js, Python, or Java backend that needs to start automatically after every reboot, with automatic restart if it crashes — this is exactly what systemd’s Restart=on-failure is built for.

2. Network mounts. Scripts that mount NFS or SMB shares at boot, ensuring they’re available before dependent applications try to read from them (using After=network-online.target in your systemd unit).

3. IoT and embedded devices. Raspberry Pi projects often use @reboot cron jobs or systemd services to launch sensor-reading scripts automatically whenever the device powers on.

4. Monitoring agents. Lightweight health-check scripts that ping a service and log or alert on failures, running continuously from boot onward.

Best Practices

  • Prefer systemd over cron or rc.local whenever you’re setting up anything resembling a production service — the restart policies and dependency management are worth the extra setup time.
  • Always log output from startup scripts (>> /var/log/yourscript.log 2>&1) so you can debug failures without needing to reproduce them manually.
  • Use absolute paths everywhere in startup scripts. The environment at boot time is minimal and won’t have your usual $PATH set up.
  • Test scripts manually before relying on them at boot — run them exactly as systemd or cron would, as the same user, with the same environment.
  • Avoid running startup scripts as root unless absolutely necessary; specify a dedicated User= in your systemd unit file instead.

Security Considerations

  • Startup scripts often run with elevated privileges, especially with rc.local or system-wide cron. Never place scripts with loose permissions (world-writable) in locations that run automatically — a malicious actor could replace your script with something harmful.
  • Always set explicit, restrictive file permissions: chmod 750 or tighter for scripts that run with root or service-account privileges.
  • Avoid hardcoding secrets (API keys, passwords) directly in startup scripts. Use environment files (EnvironmentFile= in systemd) with locked-down permissions instead.
  • Validate any external input your startup script consumes, especially if it reads from user-writable directories.

Troubleshooting Common Issues

Script works manually but not at boot: This is almost always an environment issue — missing $PATH entries, unset environment variables, or the network not being available yet. Add After=network-online.target and Wants=network-online.target to your systemd unit if your script needs network access.

systemd service fails immediately: Check logs with:

journalctl -u mystartup.service -e

This shows the most recent log entries for that specific service, which usually reveals the exact error.

Cron @reboot job never runs: Confirm the cron daemon itself is enabled and running (systemctl status cron or crond depending on your distro), and check /var/log/syslog or /var/log/cron for related entries.

Common Mistakes to Avoid

  • Forgetting chmod +x on the script file, which silently prevents execution.
  • Using relative paths inside scripts that only work when run from a specific directory.
  • Not redirecting output, making it impossible to debug why a startup script failed.
  • Assuming environment variables from your interactive shell (like a custom $PATH) will be present at boot — they usually aren’t.

FAQs

Q: What’s the best method for a production server? systemd, almost always. It gives you logging, restart policies, dependency ordering, and easy status checks.

Q: Can I run a script at both boot and login? Yes — combine a systemd service (for boot-time execution) with a .desktop autostart entry (for session-specific behavior) if you genuinely need both.

Q: Why does my script fail at boot but work when I run it manually? Almost always an environment difference — missing PATH entries, unavailable network, or a dependency (like a database) that hasn’t started yet.

Q: Is rc.local still supported? On many modern distributions it’s deprecated but can be re-enabled. It’s better to migrate to a systemd unit file for compatibility going forward.

Q: How do I stop a startup script from running automatically? For systemd: sudo systemctl disable mystartup.service. For cron: remove or comment out the @reboot line in crontab -e.

Summary

Running Bash scripts at startup is a foundational skill for anyone managing Linux systems, whether that’s a home server, a cloud VM, or a Raspberry Pi project. systemd is the modern, robust choice for anything resembling a real service, cron’s @reboot is great for quick one-off tasks, and desktop autostart files handle session-specific tools. Pick the method that matches how critical and long-running your task actually is, and always test your script’s behavior in the same environment it will actually run in.

References

Total
2
Shares

Leave a Reply

Previous Post
How to Set Environment Variables in Bash

How to Set Environment Variables in Bash

Next Post
How to Check Disk Space in Bash

How to Check Disk Space in Bash

Related Posts