Introduction
If you've ever needed to automate an application or script to run on boot or wanted to manage a custom process, chances are you've come across systemd
. As the default init system on Ubuntu, systemd
controls how services are started, stopped, and managed. But did you know you can create your own custom services with systemd
?
In this post, we will explore the process of creating and managing custom systemd
services on Ubuntu and provide with a clear and practical approach to writing, configuring, and maintaining your own systemd
services.
What is a systemd Service Unit?
A systemd
service unit is a configuration file used by the systemd
init system to define and manage a service on your Linux system. These services are typically long-running background processes, also known as daemons, that perform specific tasks or provide functionality. Examples include web servers, database servers, or even custom applications and scripts.
Service unit files have a .service
extension and are usually located in directories like /etc/systemd/system/
for custom services or /lib/systemd/system/
for system-installed services. Each service unit file specifies how the service should behave, including how to start, stop, or restart it.
A typical service unit file consists of three main sections:
- [Unit]: Contains metadata and dependencies for the service, such as its description and what other services it depends on.
- [Service]: Defines how the service runs, including the command to execute, restart policies, and runtime options.
- [Install]: Specifies installation details, such as when the service should be started (e.g., at boot time).
Step 1: Creating a Basic Service File
Creating a custom service in systemd
begins with writing a .service
file. This file defines the behavior of your service, such as the program to run, its restart policies, and when it should start.
Start by opening a new file in the /etc/systemd/system/
directory. For this example, we’ll create a service called my-custom.service
:
sudo nano /etc/systemd/system/my-custom.service
Inside the file, define the following basic structure:
[Unit]
Description=My Custom Service
After=network.target
[Service]
ExecStart=/usr/bin/python3 /path/to/my-script.py
Restart=always
User=myuser
Group=mygroup
[Install]
WantedBy=multi-user.target
Explanation of Each Section:
-
[Unit]:
- Description: A brief description of your service.
- After: Ensures the service starts only after the network.target is reached, meaning the network is up.
-
[Service]:
- ExecStart: The command to start your service (in this case, a Python script).
- Restart: Ensures the service restarts automatically if it fails.
- User and Group: Specifies the user and group under which the service runs for security.
-
[Install]:
- WantedBy: Specifies that the service should be started during the multi-user.target boot phase, which is common for most services.
Once you’ve saved the file, reload the systemd daemon to recognize the new service:
sudo systemctl daemon-reload
Step 2: Installing and Enabling the Service
Once the service file is created, the next step is to install and enable it so that it runs as expected. In systemd
, "installing" essentially means notifying the system about your new service, and "enabling" ensures it starts automatically on boot.
Start the Service
To start the service immediately, use the following command:
sudo systemctl start my-custom.service
You can verify that the service is running by checking its status:
sudo systemctl status my-custom.service
This command will display information about the service, including whether it started successfully, its runtime status, and any logged output.
Enable the Service at Boot
To ensure the service starts automatically at boot, enable it with:
sudo systemctl enable my-custom.service
This creates a symbolic link in the appropriate systemd
directory to include your service in the boot sequence.
Reloading Changes
If you ever make changes to the .service
file, you need to reload the systemd
daemon to apply the updates:
sudo systemctl daemon-reload
After reloading, restart the service to apply the changes:
sudo systemctl restart my-custom.service
Step 3: Debugging
To ensure your custom service works as intended, follow these steps for debugging:
Start and Verify the Service
Start your service:
sudo systemctl start my-custom.service
Check its status for runtime details:
sudo systemctl status my-custom.service
Look for "Active: active (running)" to confirm it’s operational.
Examine Logs
Inspect logs for errors or output using:
sudo journalctl -u my-custom.service
Advanced Features
While the basic setup of systemd services covers most use cases, advanced features provide additional flexibility and control. Some noteworthy advanced options include:
Timer Units
Instead of running services immediately, you can create timer units to schedule services at specific times or intervals. Example:
[Unit]
Description=Run custom script daily
[Timer]
OnCalendar=daily
Unit=my-custom.service
[Install]
WantedBy=timers.target
Activate the timer with:
sudo systemctl enable my-custom.timer
sudo systemctl start my-custom.timer
Environment Variables
You can define environment variables within the service file to customize behavior dynamically.
[Service]
Environment="VAR_NAME=value"
Sandboxing and Security Options
Use directives like PrivateTmp=true
, ProtectSystem=full
, or NoNewPrivileges=true
to isolate your service and limit its capabilities for enhanced security.
Dependencies
Define service dependencies using Before=
, After=
, and Requires=
to control startup order and relationships with other services.
Common Pitfalls
Even with a straightforward setup, some common issues can arise when working with systemd services:
- Forgetting to Reload Daemon: After creating or modifying a service file, always reload the systemd daemon with:
sudo systemctl daemon-reload
- Incorrect Paths or Permissions: Ensure all paths in ExecStart are correct and accessible by the user running the service. Verify permissions on scripts and executables.
- Failure to Enable the Service: Not enabling the service results in it not starting at boot. Always use:
sudo systemctl enable my-custom.service
-
Insufficient Logging: If a service fails silently, use
journalctl -u <service-name>
to diagnose issues and ensure adequate logging is set up in your scripts or applications. -
Overlooking Dependencies: Misconfigured dependencies can cause startup delays or failures. Use
After=
andRequires=
judiciously.
Real-World Examples
Example 1: Deploying a Web Server
Suppose you need a lightweight Python web server to start automatically on boot. Create a service file:
[Unit]
Description=Simple Python Web Server
After=network.target
[Service]
ExecStart=/usr/bin/python3 -m http.server 8080
WorkingDirectory=/home/myuser/webroot
Restart=on-failure
User=myuser
Group=mygroup
[Install]
WantedBy=multi-user.target
Reload, start, and enable the service:
sudo systemctl daemon-reload
sudo systemctl start web-server.service
sudo systemctl enable web-server.service
Example 2: Automating a Backup Script
For a script that backs up files nightly, create a service and timer unit:
Service file:
[Unit]
Description=Nightly Backup Service
[Service]
ExecStart=/usr/bin/bash /home/myuser/backup.sh
User=myuser
Group=mygroup
Timer file:
[Unit]
Description=Run backup nightly
[Timer]
OnCalendar=02:00
Persistent=true
[Install]
WantedBy=timers.target
Activate the timer:
sudo systemctl enable backup.timer
sudo systemctl start backup.timer
Conclusion
Custom systemd
services provide a powerful and flexible way to manage processes and scripts on Ubuntu. With the ability to define dependencies, schedule executions, and secure your services, systemd
becomes an essential tool for developers and sysadmins alike. Avoid common pitfalls by thoroughly testing your configurations, and leverage advanced features like timers and security options to enhance functionality. Whether automating backups or deploying servers, systemd
services ensure reliable and repeatable management of tasks.
Top comments (0)