Table of Contents
- What Are Scoped Units?
- Key Characteristics of Scoped Units
- When to Use Scoped Units
- How Scoped Units Work: Under the Hood
- Practical Examples: Creating and Managing Scoped Units
- Scoped Units vs. Other Unit Types
- Best Practices for Using Scoped Units
- Troubleshooting Common Issues
- Conclusion
- References
What Are Scoped Units?
Scoped units (.scope) are a type of systemd unit designed to manage transient, externally started processes. Unlike service units (.service), which define how systemd should start, stop, and monitor processes it controls, scoped units attach to and manage processes that were started outside of systemd (e.g., by a user, script, or external process manager).
Think of scoped units as “adapters” that bridge external processes into systemd’s management framework. They allow you to:
- Monitor external processes using systemd tools (e.g.,
systemctl,journalctl). - Apply resource limits (CPU, memory) to groups of processes.
- Organize processes into logical groups (scopes) for easier management.
- Automatically clean up processes when they exit.
Scoped units are not persistent—they exist only as long as the processes they manage are running. Once all processes in a scope terminate, the unit is automatically removed from the systemd manager.
Key Characteristics of Scoped Units
To understand scoped units, let’s highlight their defining traits:
1. Transient and Ephemeral
Scoped units are not stored on disk (unlike service units, which are defined in /etc/systemd/system/ or /usr/lib/systemd/system/). They are created dynamically in memory and destroyed when their processes exit.
2. Manages External Processes
Scoped units do not start processes themselves (no ExecStart directive like service units). Instead, they “adopt” existing processes or processes started externally (e.g., via systemd-run).
3. Uses the [Scope] Section
Scoped units are defined with a [Scope] INI section (instead of [Service] for service units). This section configures metadata and behavior (e.g., Description, Documentation).
4. Integrates with Cgroups
Like all systemd units, scoped units leverage Linux cgroups (control groups) to group processes and enforce resource limits. Each scope gets its own cgroup, making it easy to track and manage resources.
5. Auto-Cleanup
When the last process in a scoped unit terminates, systemd automatically stops and removes the unit. No manual cleanup is needed!
When to Use Scoped Units
Scoped units shine in scenarios where you need dynamic, temporary process management. Here are common use cases:
- User-Started Background Processes: Wrapping a script or command you run in the background (e.g.,
./long-running-script &) to monitor it withsystemctl. - Ad-Hoc Workloads: Managing one-time tasks like batch jobs, backups, or data processing scripts.
- External Process Managers: Integrating processes started by tools like
nohup,screen, or custom scripts into systemd’s monitoring ecosystem. - Resource Limiting: Restricting CPU/memory usage for untrusted or resource-heavy temporary processes (e.g., “limit this video encoding script to 2 CPU cores”).
- Debugging: Grouping related processes (e.g., a web server and its dependencies) to monitor their combined behavior.
How Scoped Units Work: Under the Hood
To understand scoped units, we need to briefly explore cgroups, the Linux kernel feature that enables process grouping and resource management.
Cgroups and Systemd
Systemd uses cgroups to organize processes into a hierarchy. Every unit (service, scope, slice) is associated with a cgroup, allowing systemd to track resource usage and enforce limits. Scoped units fit into this hierarchy as leaf nodes, typically under a slice unit (e.g., user.slice for user processes, system.slice for system processes).
Lifecycle of a Scoped Unit
- Creation: A scoped unit is created dynamically (via
systemd-runor the systemd API). - Cgroup Allocation: Systemd creates a new cgroup for the scope, usually under a parent slice (e.g.,
user-1000.slice/[email protected]/my-scope.scope). - Process Attachment: Processes are attached to the scope’s cgroup (either via
systemd-runstarting a process or manually moving existing processes). - Monitoring: Systemd monitors the cgroup. If all processes in the cgroup exit, the scope is marked as “dead.”
- Cleanup: Systemd removes the scope unit and its cgroup, freeing resources.
Practical Examples: Creating and Managing Scoped Units
Let’s walk through hands-on examples to master scoped units. We’ll use systemd-run, a command-line tool to create transient units (including scopes).
Creating a Basic Scoped Unit
To create a scoped unit, use systemd-run --scope followed by the command to run. This starts the command and wraps it in a scoped unit.
Example: Start a 60-second sleep process in a scoped unit:
systemd-run --scope --description="My First Scoped Unit" sleep 60
Output:
Running as unit: run-u1234.scope
--scope: Explicitly creates a scoped unit (instead of a transient service).--description: Adds a human-readable description (optional but helpful).- The unit name (e.g.,
run-u1234.scope) is auto-generated.
Setting Resource Limits
Scoped units inherit resource limits from their parent slice, but you can override them with --property to set cgroup parameters.
Example: Limit a process to 512MB of memory and 1 CPU core:
systemd-run --scope \
--description="Memory-Limited Process" \
--property=MemoryMax=512M \
--property=CPUQuota=100% \
stress --vm 1 --vm-bytes 1G # A tool to stress test memory/CPU
Here:
MemoryMax=512M: Caps memory usage at 512MB.CPUQuota=100%: Limits CPU usage to 1 core (100% of one core).
Verify the limits with systemctl show:
systemctl show run-u1234.scope | grep -E "MemoryMax|CPUQuota"
Output:
MemoryMax=536870912
CPUQuota=100%
Grouping Processes in a Scope
You can manually attach existing processes to a scoped unit (useful for grouping already running processes).
Step 1: Start a process (e.g., a background ping):
ping google.com > /dev/null &
# Note the PID (e.g., 12345)
Step 2: Create an empty scoped unit:
systemd-run --scope --description="Grouped Ping Processes" --no-ask-password true
# Output: Running as unit: run-u5678.scope
--no-ask-password: Bypasses password prompts (for user sessions).true: Immediately exits, leaving an empty scope.
Step 3: Attach the ping process to the scope using systemctl set-property:
sudo systemctl set-property run-u5678.scope TasksMax=infinity # Allow unlimited tasks (optional)
sudo cgclassify -g name=systemd:/run-u5678.scope 12345 # Attach PID 12345 to the scope
Now, the ping process is managed by run-u5678.scope!
Monitoring and Controlling Scoped Units
Use standard systemd tools to monitor and control scoped units:
Check Status
systemctl status run-u1234.scope
Output:
● run-u1234.scope - My First Scoped Unit
Loaded: loaded (/run/systemd/transient/run-u1234.scope; transient)
Transient: yes
Active: active (running) since Wed 2024-05-20 10:00:00 UTC; 10s ago
Tasks: 1 (limit: 18762)
Memory: 1.2M (max: 512.0M)
CPU: 10ms
CGroup: /user.slice/user-1000.slice/[email protected]/app.slice/run-u1234.scope
└─12345 /usr/bin/sleep 60
View Logs
journalctl -u run-u1234.scope
Stop the Scope (and Its Processes)
systemctl stop run-u1234.scope
This terminates all processes in the scope’s cgroup.
Scoped Units vs. Other Unit Types
It’s critical to distinguish scoped units from other common systemd unit types:
| Feature | Scoped Unit (.scope) | Service Unit (.service) | Slice Unit (.slice) |
|---|---|---|---|
| Purpose | Manage external/transient processes | Manage systemd-started daemons | Organize units into resource groups |
| Persistence | Transient (in-memory, auto-deleted) | Persistent (stored on disk) | Persistent (stored on disk) |
| Starts Processes? | No (adopts existing processes) | Yes (via ExecStart) | No (organizes other units) |
| Key Section | [Scope] | [Service] | [Slice] |
| Lifecycle | Tied to process exit | Controlled by systemctl start/stop | Exists until explicitly stopped |
Best Practices for Using Scoped Units
- Use
systemd-run --scopefor Creation: Avoid manual unit files—systemd-runhandles cgroup setup and naming automatically. - Name Units Descriptively: Use
--descriptionto add context (e.g.,--description="Backup Script Scope"). - Set Resource Limits: Always limit resources for untrusted or resource-heavy processes (e.g.,
CPUQuota,MemoryMax). - Avoid Persistence: Scoped units are transient—never try to enable them with
systemctl enable(they won’t survive a reboot). - Monitor with
systemctlandjournalctl: Usesystemctl statusandjournalctl -u <unit>to track scope health.
Troubleshooting Common Issues
Issue: Scoped Unit Disappears Immediately
Cause: The process in the scope exits immediately (e.g., a typo in the command).
Fix: Verify the command runs correctly outside the scope. Use journalctl -u <unit> to check for errors.
Issue: Can’t Attach Processes to a Scope
Cause: Insufficient permissions (user scopes can’t attach system processes) or cgroup limits.
Fix: Use sudo for system processes, or ensure the process is in the same user session as the scope.
Issue: Resource Limits Not Applied
Cause: Typos in property names (e.g., MemoryMax vs. MemoryLimit) or parent slice limits overriding your settings.
Fix: Use systemctl show <unit> to verify properties. Check the parent slice with systemctl status <slice>.
Conclusion
Scoped units are a powerful addition to systemd’s toolbox, enabling dynamic, on-the-fly management of external processes. By wrapping ad-hoc tasks, user scripts, or temporary workloads in scoped units, you gain systemd’s monitoring, resource control, and lifecycle management capabilities—without the rigidity of traditional service units.
Whether you’re a system administrator managing temporary workloads or a developer debugging background processes, scoped units offer the flexibility to integrate unmanaged processes into systemd’s robust ecosystem. With the examples and best practices in this guide, you’re ready to start using scoped units to streamline your Linux process management.