service¶
service manages a service: whether it starts at boot (enable) and
whether it is running now (state). It works with systemd, and with OpenRC
on Alpine. It reads the current state with systemctl is-enabled and
systemctl is-active (or rc-update show and rc-service NAME status)
and only changes what differs. Other resources can ask a service to reload
or restart when they change, through notifies:.
Minimal example¶
# On Arch, installing nginx does not enable or start it; the service
# resource does. Debian and Ubuntu start services on install, so there the
# same resource usually reports no change.
package "nginx"
service "nginx", enable: true, state: :running
Options¶
| Option | Type | Default | Description |
|---|---|---|---|
enable |
true or false |
unmanaged | Start at boot (systemctl enable) or not (systemctl disable). Enabled means systemctl is-enabled prints enabled. |
state |
:running or :stopped |
unmanaged | Running now (systemctl start) or not (systemctl stop). Running means systemctl is-active prints active. |
user |
String | none | Manage the unit in this user's systemd instance (systemctl --user -M USER@), such as a unit in ~/.config/systemd/user/. The user must be logged in or have lingering enabled (loginctl enable-linger USER). systemd only. |
notifies |
[:action, "type[name]"], optionally with :immediately, or a list of them |
none | Actions to run on other resources when this one changes. See Notifications. |
subscribes |
[:action, "type[name]"], optionally with :immediately, or a list of them |
none | Actions to run on this resource when another one changes. |
tags |
Symbol, String, or an Array of them | none | Tags for running part of a recipe with --tags and --skip-tags. See Tags. |
A service with neither option manages nothing by itself; it is still a valid target for notifications.
OpenRC¶
On a host that runs OpenRC instead of systemd, as Alpine does, the same
recipe works with OpenRC's commands. sadame checks for
/run/systemd/system first, then for /run/openrc/softlevel.
| systemd | OpenRC | |
|---|---|---|
| Enabled means | systemctl is-enabled prints enabled |
the service is in a runlevel (rc-update show) |
enable: true |
systemctl enable NAME |
rc-update add NAME default |
enable: false |
systemctl disable NAME |
rc-update del NAME RUNLEVEL for each runlevel it is in |
| Running means | systemctl is-active prints active |
rc-service NAME status exits 0 |
state: :running |
systemctl start NAME |
rc-service NAME start |
New in 0.2.0
sadame 0.1.0 supports systemd only.
$ sudo sadame apply examples/resources/service
sadame 0.5.1 apply on local (Alpine Linux v3.20)
changed package[nginx]
state: absent -> installed
changed service[nginx]
enabled: false -> true
state: stopped -> running
Summary
changed 2 package[nginx]
service[nginx]
unchanged 0
failed 0
skipped 0
notified 0
Finished in 1.26s.
$ rc-update show default
nginx | default
$ rc-service nginx status
* status: started
Actions for notifications¶
Other resources can trigger these with notifies: [:action, "service[name]"]:
| Action | Runs with systemd | Runs with OpenRC |
|---|---|---|
:start |
systemctl start NAME |
rc-service NAME start |
:stop |
systemctl stop NAME |
rc-service NAME stop |
:restart |
systemctl restart NAME |
rc-service NAME restart |
:reload |
systemctl reload NAME |
rc-service NAME reload |
:enable |
systemctl enable NAME |
rc-update add NAME default |
:disable |
systemctl disable NAME |
rc-update del NAME RUNLEVEL |
Actions run only when the notifying resource changed. By default they run
once each, after all resources; add :immediately to run one right away.
See the scenario below and Notifications.
Scenarios¶
# On Arch, installing nginx does not enable or start it; the service
# resource does. Debian and Ubuntu start services on install, so there the
# same resource usually reports no change.
package "nginx"
service "nginx", enable: true, state: :running
Installing and starting nginx¶
On this Debian 12 machine the nginx package was enabled on install but not
started. Each resource reads its current state right before it runs, so by
the time service[nginx] is applied, only the running state is left to
change:
$ sudo sadame plan examples/resources/service
sadame 0.5.1 plan on local (Debian GNU/Linux 12 (bookworm))
+ package[nginx]
state: absent -> installed
~ service[nginx]
enabled: false -> true
state: stopped -> running
Plan: 1 to create, 1 to update, 0 to remove, 0 to run, 0 unchanged.
$ sudo sadame apply examples/resources/service
sadame 0.5.1 apply on local (Debian GNU/Linux 12 (bookworm))
changed package[nginx]
state: absent -> installed
changed service[nginx]
state: stopped -> running
Summary
changed 2 package[nginx]
service[nginx]
unchanged 0
failed 0
skipped 0
notified 0
Finished in 4.88s.
Someone stopped and disabled it¶
$ sudo systemctl stop nginx
$ sudo systemctl disable --quiet nginx
$ sudo sadame plan examples/resources/service
sadame 0.5.1 plan on local (Debian GNU/Linux 12 (bookworm))
~ service[nginx]
enabled: false -> true
state: stopped -> running
Plan: 0 to create, 1 to update, 0 to remove, 0 to run, 1 unchanged.
$ sudo sadame apply examples/resources/service
sadame 0.5.1 apply on local (Debian GNU/Linux 12 (bookworm))
unchanged package[nginx]
changed service[nginx]
enabled: false -> true
state: stopped -> running
Summary
changed 1 service[nginx]
unchanged 1
failed 0
skipped 0
notified 0
Finished in 0.08s.
Reloading after a configuration change¶
# Reload nginx when its configuration changes. The reload runs once, at the
# end of apply, and only if the file changed.
package "nginx"
file "/etc/nginx/conf.d/status.conf",
content: "server {\n listen 127.0.0.1:8080;\n location = /status { stub_status; }\n}\n",
mode: "0644",
notifies: [:reload, "service[nginx]"]
service "nginx", enable: true, state: :running
$ sudo sadame apply examples/resources/service/reload_on_change.rb
sadame 0.5.1 apply on local (Debian GNU/Linux 12 (bookworm))
changed package[nginx]
state: absent -> installed
changed file[/etc/nginx/conf.d/status.conf]
state: absent -> present
mode: 0644
--- /dev/null
+++ /etc/nginx/conf.d/status.conf (desired)
@@ -0,0 +1,4 @@
+server {
+ listen 127.0.0.1:8080;
+ location = /status { stub_status; }
+}
changed service[nginx]
state: stopped -> running
notified service[nginx]: reload (delayed, from file[/etc/nginx/conf.d/status.conf])
Summary
changed 3 package[nginx]
file[/etc/nginx/conf.d/status.conf]
service[nginx]
unchanged 0
failed 0
skipped 0
notified 1 service[nginx]: reload
Finished in 4.11s.
On the next run nothing changes, so nothing is reloaded.
Common mistakes¶
- No service manager. The resource needs systemd or OpenRC running. In most containers neither runs, and it fails with "no service manager is running on this host".
- Declaring the service before its package. On a fresh machine the unit
does not exist yet, so plan shows it as disabled and stopped, and
applyfails if the package comes later in the recipe. Putpackagefirst. - Units that cannot be enabled.
systemctl is-enabledprintsstaticfor units without an[Install]section. Those never count as enabled, soenable: trueon them shows a change on every plan. Leaveenableout. - Expecting a reload when only the service changed.
reloadandrestarthappen only through notifications from other resources. - Reloading a service that cannot reload.
systemctl reloadfails for units withoutExecReload=, andrc-service NAME reloadfor init scripts without a reload command. Notify:restartfor those. - Different names on different systems. The SSH server is
sshon Debian and Ubuntu andsshdelsewhere; cron iscron,crondorcronie. Pick the name fromplatform.family.