Mets Minor League Blog

Music theory, instruments and musicians

Mets Minor League Blog

Music theory, instruments and musicians

How to Automatically Run a Shell Script at Startup with systemd

How to Automatically Run a Shell Script at Startup with systemd

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 nano or vim.

Step-by-Step

  1. 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.sh to ensure it behaves as expected.

  2. Create a new systemd service file. Using a text editor with root privileges, create a file in /etc/systemd/system/ called myscript.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.target ensures the network is up before running the script if it needs networking.
    • [Service]: Defines how the service runs. Type=simple is suitable for most scripts. ExecStart points to your script.
    • [Install]: Specifies when this service should start. multi-user.target means it starts during normal multi-user boot.
  3. 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.

  4. Enable the service to run at boot.
    sudo systemctl enable myscript.service

    This creates the necessary symlinks so your script runs automatically on startup.

  5. 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.

  6. 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 +x on your script will prevent systemd from running it.
  • Incorrect script path: The path in ExecStart must be absolute and correct; relative paths won’t work.
  • Not reloading systemd: After creating or modifying a service file, you must run systemctl daemon-reload or 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=forking incorrectly: If your script doesn’t fork into the background, use Type=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=username in 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.log in the service file.
  • Use After and Requires wisely: 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.service for overrides: Instead of editing the main service file, create drop-in configs for easier updates.
  • Consider WantedBy=default.target if you want the script to run in graphical environments.
  • Use Restart=on-failure or Restart=always to 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/bash or #!/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.sh to 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 ExecStartPre or ExecStartPost for 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.service should contain the [Service] section with your script in ExecStart.
  • Create the timer file: For example, myscript.timer will 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 stdout and stderr of services and sends them to the journal, accessible via journalctl.
  • View logs: Run journalctl -u myscript.service to 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.log and StandardError=file:/var/log/myscript-error.log in the [Service] section.
  • Use logger command: Your script can use the logger command 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= and Before=: These directives control the order in which units start. For example, After=network.target ensures your script runs only after the network is available.
  • Requires= and Wants=: These specify dependencies. Requires= means your service will fail if the required unit fails, while Wants= 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 with systemctl --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.target in the [Unit] section.
  • Example: After=graphical.target ensures 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= and ProtectHome=: 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.service and observe the result.
  • Check status and logs: Use sudo systemctl status myscript.service and journalctl -u myscript.service to view real-time logs and error messages.
  • Enable verbose logging: Add Environment=SYSTEMD_LOG_LEVEL=debug in the [Service] or run systemd-analyze verify myscript.service to validate your service file syntax and dependencies.
  • Use systemd-run for 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

How to Automatically Run a Shell Script at Startup with systemd
Scroll to top