Slices and scopes in systemd aren’t just organizational tools; they’re fundamental mechanisms for controlling resource allocation and process isolation, allowing you to treat groups of processes as first-class citizens with their own resource limits.
Let’s see this in action. Imagine you want to run a bunch of background worker processes for a web application, and you want to ensure they don’t hog all the CPU or memory, especially during peak load. You can group them into a slice.
First, you’d define a slice. This is done by creating a .slice unit file. For example, my-workers.slice:
[Unit]
Description=My Worker Processes Slice
This file itself doesn’t do much, but it serves as a placeholder to which you can assign processes. Now, when you start a service that you want to be part of this slice, you specify it in the service unit file:
# /etc/systemd/system/my-worker-app.service
[Unit]
Description=My Worker Application Service
[Service]
ExecStart=/usr/local/bin/my-worker-app
Slice=my-workers.slice
# Other service configurations...
When my-worker-app.service starts, systemd automatically creates the my-workers.slice if it doesn’t exist and places the main process of the service, and any children it spawns by default, into that slice. You can then inspect the slice:
systemctl status my-workers.slice
This will show you the processes belonging to this slice and their resource usage.
The real power comes when you start applying resource controls to the slice. You can edit the slice unit file directly (or better, create an override file) to set limits. For instance, to limit the CPU time and memory for all processes within my-workers.slice:
sudo systemctl edit my-workers.slice
In the editor, add:
[Slice]
CPUQuota=50%
MemoryMax=512M
Now, any process within my-workers.slice will collectively be limited to 50% of the CPU and 512MB of RAM. If the workers try to exceed these limits, systemd will throttle their CPU usage or, in the case of memory, start to kill processes within the slice to stay within the MemoryMax boundary.
Scopes are similar to slices but are typically used for transient processes or groups of processes that are managed by an existing unit. For example, a user session often gets its own scope, and applications launched by that user will inherit from it. You can also create scopes manually for ad-hoc process grouping.
# Start a command in a new scope
systemd-run --scope --unit=my-adhoc-task /bin/sleep 60
This command runs /bin/sleep 60 in a new scope named my-adhoc-task.scope. You can then check its status:
systemctl status my-adhoc-task.scope
And apply temporary resource controls:
sudo systemctl set-property my-adhoc-task.scope CPUWeight=100 MemoryLimit=100M
The key difference is that slices are designed for longer-running, managed groups of processes (like services), while scopes are more for temporary or user-session-related process grouping. When a scope’s main process exits, the scope unit itself is typically removed. Slices, on the other hand, persist as long as they contain processes or are explicitly managed.
The hierarchy of slices is crucial. Systemd has a default hierarchy: .slice is the root, and units can be nested. For instance, a user’s services might be in user-1000.slice, and a specific application within that user’s session could be in user-1000-myapp.slice. This allows for fine-grained resource delegation. A parent slice can have resource controls, and child slices inherit these and can further subdivide them. For example, if user-1000.slice has CPUQuota=80%, a child slice user-1000-myapp.slice could be given CPUQuota=50% of the parent’s allocation, effectively getting 40% of the total system CPU.
The actual resource controls are implemented using Linux Control Groups (cgroups). Systemd provides a high-level, user-friendly abstraction over the complex cgroup filesystem. When you set CPUQuota=50% on a slice, systemd translates this into the appropriate settings within the cgroup v2 hierarchy for that slice’s processes. This is why you can see cgroup paths when inspecting processes via systemctl status.
What most people miss is that the systemd-run command, when used with --slice, doesn’t just create a scope; it can create a slice if the specified slice name doesn’t already exist, and then place the process within it. This makes it a versatile tool for creating both transient scopes and ad-hoc slices for temporary resource management.
You’ll next encounter how to manage these units through systemd’s bus API, allowing programmatic control and monitoring of process groups.