Services
A service is a background process managed by the operating system. It might be a database, a sync daemon, a VPN client, Docker, or another program that should keep running without being started by hand.
On Linux, dots supports systemd and OpenRC services. On macOS, it supports services managed by Homebrew.
if dots.platform.family == "arch" then
dots.systemd.enable({ "docker.service" })
dots.systemd.start({ "docker.service" })
end
if dots.platform.family == "darwin" then
dots.brew.enable()
dots.brew.install({ "postgresql@16" })
dots.brew.service.start({ "postgresql@16" })
endThe four service actions are:
enable: start the service automatically on bootdisable: do not start the service automatically on bootstart: start the service nowstop: stop the service now
Use both enable and start when the service should run now and after reboot. Use stop or disable to keep a service off even when it was activated outside dots.
dots.systemd.stop({ "cups.service" })
dots.systemd.disable({ "cups.service" })Removing a stop or disable declaration only removes the constraint. Dots does not start or enable the service.
systemd
Use dots.systemd for Linux system services:
dots.systemd.enable({ "docker.service" })
dots.systemd.start({ "docker.service" })If start or enable is removed later, dots check shows the inverse action:
enablebecomesdisablestartbecomesstop
Removing stop or disable only makes dots forget the constraint.
Restart after changes
A running service may need a restart after its configuration changes. Subscribe it to file, directory, command, or systemd unit references with restart_on_change:
local config = dots.file("/etc/headscale/config.yaml", {
source = "headscale/config.yaml",
})
dots.systemd.start({ "headscale.service" }, {
restart_on_change = { config },
})When the file changes, dots applies it first and restarts Headscale once. Tracking the file or applying with no changes does not restart the service. If Headscale is stopped, dots starts it after applying the file instead of starting and then restarting it.
Unit files
You can also install your own systemd units:
dots.systemd.install({
"systemd/automatic-timezone.service",
"systemd/automatic-timezone.timer",
})
dots.systemd.enable({ "automatic-timezone.timer" })
dots.systemd.start({ "automatic-timezone.timer" })Source paths are relative to the dots project. Pass a string to install one unit or a table to install several. Each file is installed in /etc/systemd/system using its basename. This works with services, timers, sockets, paths, and other unit types.
dots.systemd.install() returns one reference for a string and a table of references for a table. References can be used in command needs.
When applying, dots installs the files and reloads systemd before enabling or starting them. If you remove all declarations, dots stops and disables the timer before removing both files. Removing only start or enable keeps the unit files installed.
dots check compares each source with its installed unit file. A changed source is shown as an update. Dots refuses to remove a unit file that changed outside dots.
OpenRC
Use dots.openrc on distros that boot with OpenRC, such as Alpine:
dots.openrc.enable({ "docker" })
dots.openrc.start({ "docker" })
dots.openrc.stop({ "cupsd" })
dots.openrc.disable({ "cupsd" })enable adds the service to the default runlevel with rc-update add. Service constraints follow the same removal behavior as systemd.
Homebrew services
Use dots.brew.service for services managed by Homebrew:
dots.brew.enable()
dots.brew.install({ "postgresql@16" })
dots.brew.service.start({ "postgresql@16" })If the declaration is removed later, dots runs brew services stop for that service. Use dots.brew.service.stop({ "postgresql@16" }) to keep an existing service stopped. Removing that constraint does not start it.
Profiles
Profiles can decide which services are active on the same machine. For example, a work profile might start a VPN service while a personal profile leaves it stopped:
if dots.profile == "work" then
dots.systemd.start({ "example-vpn.service" })
endSwitch profiles with:
dots --profile work apply
dots --profile personal applyRemoving a start declaration only stops a service that dots already tracks. A stop declaration enforces the stopped state even when the service was started outside dots.