Some tasks aren’t long-running daemons — they run once, do something (apply sysctl tweaks, warm a cache, run a migration), and exit. Type=oneshot is built for that.
The unit
Save as /etc/systemd/system/firstboot-setup.service:
[Unit]
Description=One-time boot setup
After=network-online.target
Wants=network-online.target
[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/usr/local/bin/step-one.sh
ExecStart=/usr/local/bin/step-two.sh
[Install]
WantedBy=multi-user.target
Type=oneshottells systemd the process is expected to exit; the unit is “starting” until the command(s) finish, so units that depend on it (After=) wait for it to complete.RemainAfterExit=yeskeeps the unit shown as active (exited) after it finishes, soenableworks cleanly andsystemctl statusdoesn’t look “dead”.- Multiple
ExecStart=lines run in order; the unit fails if any one returns non-zero (prefix a line with-to ignore its failure).
Enable / run
sudo systemctl daemon-reload
sudo systemctl enable --now firstboot-setup.service
systemctl status firstboot-setup.service # shows "active (exited)"
To re-run it later, just sudo systemctl restart firstboot-setup.service.
Use it with a timer: a oneshot service is exactly what a .timer activates on a schedule (see the timer recipes). Gotchas: don’t set Restart= on a oneshot — it’s meant to exit. If you need ordering before the network or a mount, use Before=/After= and the right target.