Oneshot Startup Script
Build a systemd oneshot service for running initialization scripts at boot. Learn about RemainAfterExit, ordering with Before/After, and how oneshot differs from simple services.
Detailed Explanation
Running Scripts Once with systemd
The Type=oneshot service type is designed for processes that perform a task and then exit. Unlike simple services, systemd waits for the process to complete before considering the service "started."
[Unit]
Description=System Initialization Script
After=network-online.target
Wants=network-online.target
[Service]
Type=oneshot
ExecStart=/opt/scripts/initialize.sh
RemainAfterExit=yes
StandardOutput=journal
StandardError=journal
User=root
[Install]
WantedBy=multi-user.target
RemainAfterExit
The RemainAfterExit=yes directive is key for oneshot services. Without it, the service status would show "inactive (dead)" after the script completes, making it confusing to determine if it ran successfully. With this setting:
- After the script exits with code 0, the service shows as active (exited)
- Other services that depend on this one can verify it completed
systemctl is-active myinit.servicereturns "active"
Network Dependency
Note the use of network-online.target instead of network.target:
network.target: Network interfaces are configured, but may not have an IP yetnetwork-online.target: Network is fully operational with connectivity
If your script needs to download files, make API calls, or connect to remote services, use network-online.target with Wants=.
Multiple ExecStart Lines
Oneshot services uniquely allow multiple ExecStart= directives. They run sequentially:
[Service]
Type=oneshot
ExecStart=/opt/scripts/step1-setup-dirs.sh
ExecStart=/opt/scripts/step2-download-config.sh
ExecStart=/opt/scripts/step3-apply-migrations.sh
If any step fails (non-zero exit), subsequent steps are skipped and the service enters a failed state.
Ordering with Other Services
Oneshot services are commonly used as initialization gates:
[Unit]
Description=Database Migration
After=postgresql.service
Before=myapp.service
Requires=postgresql.service
This ensures migrations run after PostgreSQL starts but before the application starts.
Use Case
Running database migrations, creating required directories, downloading configuration from a remote source, or any initialization task that must complete before other services start.