Automatically executing a shell script at system startup allows you to run custom commands, initialize services, or configure environments without manual intervention each time your Linux machine boots. Using systemd, the modern init system on most Linux distributions, you can create a service that launches your shell script reliably and with fine-grained control. This guide walks you through the process step-by-step, ensuring your script runs smoothly every time your system powers on.
What You’ll Need
- A Linux system using
systemd(most modern distributions such as Ubuntu, Fedora, Debian, CentOS, Arch Linux). - A shell script you want to run automatically. Make sure it is executable and tested manually.
- Root or sudo privileges to create and enable systemd service files.
- Basic familiarity with the command line and text editors like
nanoorvim.
Step-by-Step
- Create or prepare your shell script. Write your script and save it in a suitable location, for example
/usr/local/bin/myscript.sh. Make sure the script is executable by running:sudo chmod +x /usr/local/bin/myscript.sh
Test it manually first by executing
/usr/local/bin/myscript.shto ensure it behaves as expected. - Create a new systemd service file. Using a text editor with root privileges, create a file in
/etc/systemd/system/calledmyscript.service:sudo nano /etc/systemd/system/myscript.service
Inside, add the following content, adjusting paths and descriptions as needed:
[Unit] Description=Run my custom shell script at startup After=network.target [Service] Type=simple ExecStart=/usr/local/bin/myscript.sh Restart=on-failure [Install] WantedBy=multi-user.target
Explanation:
[Unit]: Describes the service and sets dependencies.After=network.targetensures the network is up before running the script if it needs networking.[Service]: Defines how the service runs.Type=simpleis suitable for most scripts.ExecStartpoints to your script.[Install]: Specifies when this service should start.multi-user.targetmeans it starts during normal multi-user boot.
- Reload systemd to recognize the new service.
sudo systemctl daemon-reload
This refreshes systemd’s configuration and makes it aware of your new
myscript.service. - Enable the service to run at boot.
sudo systemctl enable myscript.service
This creates the necessary symlinks so your script runs automatically on startup.
- Start the service immediately (optional). To test your service without rebooting, run:
sudo systemctl start myscript.service
Check its status with:
sudo systemctl status myscript.service
This will show if the script executed successfully or if there were errors.
- Reboot your system to confirm automatic execution.
sudo reboot
After reboot, verify your script ran by checking its expected effects or reviewing logs (see troubleshooting below).
Common Mistakes
- Script not executable: Forgetting
chmod +xon your script will prevent systemd from running it. - Incorrect script path: The path in
ExecStartmust be absolute and correct; relative paths won’t work. - Not reloading systemd: After creating or modifying a service file, you must run
systemctl daemon-reloador changes won’t apply. - Missing dependencies: If your script requires networking or other services, specify them in the
[Unit]section to ensure proper startup order. - Using
Type=forkingincorrectly: If your script doesn’t fork into the background, useType=simple. Mismatched types cause systemd to misinterpret the service state. - Ignoring user permissions: By default, systemd runs services as root. If you want to run the script as a non-root user, add
User=usernamein the[Service]section.
Tips
- Log output for debugging: Redirect your script’s output to a log file within the script or use
StandardOutput=file:/var/log/myscript.login the service file. - Use
AfterandRequireswisely: These directives control startup order and dependencies, preventing race conditions. - Test your script manually first: This avoids confusion when troubleshooting systemd failures.
- Run as non-root if possible: For security, specify a less privileged user in the service file.
- Use
systemctl edit myscript.servicefor overrides: Instead of editing the main service file, create drop-in configs for easier updates. - Consider
WantedBy=default.targetif you want the script to run in graphical environments. - Use
Restart=on-failureorRestart=alwaysto ensure your script restarts if it crashes.
Troubleshooting
If your script does not run at startup as expected, try the following steps:
- Check the service status:
sudo systemctl status myscript.service
Look for errors or clues why the script failed.
- Examine logs with journalctl:
sudo journalctl -u myscript.service
This shows all output and errors related to your service.
- Verify script permissions and shebang: Ensure the first line of your script is a proper shebang like
#!/bin/bashor#!/usr/bin/env bash, and confirm it’s executable. - Confirm environment differences: systemd services run in a minimal environment. If your script depends on environment variables or paths, set them explicitly in the script or service file.
- Run the script manually as root: Use
sudo /usr/local/bin/myscript.shto check for runtime errors. - Check for user permissions: If running as a non-root user, ensure that user has permissions to execute the script and access needed resources.
- Use
ExecStartPreorExecStartPostfor debugging: Add commands that log status before and after your script runs. - Make sure the service is enabled:
sudo systemctl is-enabled myscript.service
If it’s disabled, enable it again with
systemctl enable.
Frequently Asked Questions
Can I run multiple scripts with one systemd service?
Yes, you can create a wrapper script that calls multiple scripts in sequence, then point ExecStart to that wrapper. Alternatively, create multiple service files and manage dependencies between them.
How do I run the script as a specific user instead of root?
Add the line User=username under the [Service] section in your service file. Ensure the user has execute permissions for the script and any resources it needs.
My script requires network access. How can I ensure networking is ready?
Include After=network.target and optionally Wants=network.target in the [Unit] section to delay script execution until networking is up.
What if my script needs to run only once after the first boot?
You can create a oneshot service by setting Type=oneshot and disabling the service after it runs, or use a condition in your script to check if it has already run.
Why does my script not have access to environment variables I use in the terminal?
Systemd services run with a minimal environment. You can set environment variables explicitly in the service file using Environment=VAR=value or source a profile script inside your shell script.
Can I delay the script execution after boot?
Yes, you can add ExecStartPre=/bin/sleep 10 in the service file to delay starting by 10 seconds, or use Timer units for more complex scheduling.
By following this guide, you can reliably automate the execution of any shell script on system startup using systemd. This setup is robust, integrates cleanly with the system’s init process, and is highly configurable to suit a variety of use cases. Whether you want to launch custom services, initialize hardware, or run maintenance tasks, systemd provides a powerful and standardized way to do it.
Remember, always test your scripts manually first and use the diagnostic tools systemd provides to ensure smooth startup execution.
Using systemd Timers for Scheduled Script Execution
While running a shell script at system startup is common, you might also want to execute scripts at regular intervals or after a specific delay post-boot. Systemd timers provide a native and flexible way to schedule such tasks, replacing traditional cron jobs with more integration into the systemd ecosystem.
To use systemd timers, you need two unit files: one service unit that defines the script execution, and one timer unit that defines when the service runs.
- Create the service file: For example,
myscript.serviceshould contain the[Service]section with your script inExecStart. - Create the timer file: For example,
myscript.timerwill have a[Timer]section specifying when to run the service.
Here’s a basic example of a timer unit:
[Unit] Description=Run myscript.service every 15 minutes [Timer] OnBootSec=5min OnUnitActiveSec=15min Persistent=true [Install] WantedBy=timers.target
This configuration will wait 5 minutes after boot to run the script for the first time, then repeat every 15 minutes. The Persistent=true flag ensures that missed runs during downtime are executed at the next opportunity.
After creating both unit files in /etc/systemd/system/, enable and start the timer with:
sudo systemctl enable myscript.timer sudo systemctl start myscript.timer
Use systemctl list-timers to view active timers and their next activation.
Logging Output from Your Script for Troubleshooting
One of the challenges with running scripts automatically at startup is capturing output and errors for debugging. Since systemd services run in the background without a terminal, it’s important to direct logs appropriately.
- Standard output and error: By default, systemd captures
stdoutandstderrof services and sends them to the journal, accessible viajournalctl. - View logs: Run
journalctl -u myscript.serviceto see logs related to your service. - Redirect logs explicitly: If you want logs saved to a file, modify your script to redirect output, or set
StandardOutput=file:/var/log/myscript.logandStandardError=file:/var/log/myscript-error.login the[Service]section. - Use logger command: Your script can use the
loggercommand to send messages directly to syslog and journal.
Proper logging helps you identify issues such as missing dependencies, permission errors, or script failures during startup.
Handling Dependencies and Ordering in systemd Service Files
Systemd allows precise control over the order and timing of service startup through dependencies and ordering directives. This is critical when your script depends on other services or system states.
After=andBefore=: These directives control the order in which units start. For example,After=network.targetensures your script runs only after the network is available.Requires=andWants=: These specify dependencies.Requires=means your service will fail if the required unit fails, whileWants=is a softer dependency.- Example:
[Unit] Description=My script service After=network.target Wants=network.target
This setup ensures network services are started before your script runs but does not fail the script if the network is unavailable.
Additionally, you can use Conflicts= to prevent your service from running simultaneously with conflicting units.
Running Scripts on User Login or Graphical Target
Systemd’s flexibility extends beyond system startup. You can also run shell scripts automatically when a user logs in or when the graphical session starts.
- Per-user systemd services: Systemd supports user-level units executed in the context of a logged-in user. To create one, place the service file in
~/.config/systemd/user/and manage it withsystemctl --user. - Example: A service that runs your script upon user login:
[Unit] Description=Run my script on user login After=default.target [Service] Type=simple ExecStart=/home/username/bin/myscript.sh [Install] WantedBy=default.target
Enable it with:
systemctl --user enable myscript.service systemctl --user start myscript.service
This method is ideal for user-specific initialization tasks.
- Graphical target: To run scripts once the graphical environment is ready, use
graphical.targetin the[Unit]section. - Example:
After=graphical.targetensures your script runs after the display manager and desktop environment have started.
Security Considerations When Running Startup Scripts
Automatically running scripts at boot or login can introduce security risks if not carefully managed. Here are some best practices:
- Use least privilege: Run scripts as non-root users whenever possible by specifying the
User=directive. - Verify script sources: Ensure your scripts are secure, free from vulnerabilities, and not writable by unauthorized users.
- Set proper permissions: Scripts should have restrictive permissions (e.g.,
chmod 700) to prevent tampering. - Avoid storing sensitive data in service files: Instead, store secrets in secured files or use environment variables with restricted access.
- Sanitize inputs: If your script processes external input, validate and sanitize it to prevent injection attacks.
- Use
ProtectSystem=andProtectHome=: systemd provides options to restrict file system access for services, which can be added under the[Service]section.
Testing and Debugging systemd Services
Even with careful configuration, services might not behave as expected. Here are some tips for testing and debugging your systemd-based startup scripts:
- Run the script manually: Test your script outside systemd to ensure it works as intended.
- Start the service manually: Use
sudo systemctl start myscript.serviceand observe the result. - Check status and logs: Use
sudo systemctl status myscript.serviceandjournalctl -u myscript.serviceto view real-time logs and error messages. - Enable verbose logging: Add
Environment=SYSTEMD_LOG_LEVEL=debugin the[Service]or runsystemd-analyze verify myscript.serviceto validate your service file syntax and dependencies. - Use
systemd-runfor testing: This command runs a service transiently, useful for one-off tests:
sudo systemd-run --unit=myscript-test.service /usr/local/bin/myscript.sh
This approach can help isolate issues without modifying your permanent service files.
Related
- Kasabian Original Band Members: A Complete Guide
- Sepultura Original Band Members: Who Shaped the Metal Legend
- Complete List of Megadeth Band Members Through the Years