Yes, you can create new service units in systemd by writing a unit file with a .service extension and placing it in one of the systemd unit directories. This article explains the exact steps, the syntax you need, and the common pitfalls to avoid.
What Is a systemd Service Unit?
A systemd service unit is a plain-text configuration file that tells systemd how to manage a process: what command to run, when to start it, whether to restart it, and under what user or group it should run. Unlike older init scripts, which were shell scripts with start/stop/restart cases, a unit file is declarative—you describe the desired state, and systemd handles the rest.
Each service unit ends with .service and lives in one of several directories. The two you will use most often are:
/etc/systemd/system/– for administrator-created units and overrides (takes precedence over vendor units)./usr/lib/systemd/system/– for units installed by packages. You should not edit these directly; use a drop-in override instead.
You can also place units in /run/systemd/system/ for transient, runtime-only services that disappear on reboot, but that is rarely needed for a permanent service.
Step-by-Step: Creating a Basic Service
Here is the minimal workflow to create and enable a service that runs a simple script or command.
- Create the unit file in
/etc/systemd/system/. For example,sudo nano /etc/systemd/system/myapp.service. - Write the unit content. A minimal file looks like this:
[Unit] Description=My custom application [Service] ExecStart=/usr/local/bin/myapp [Install] WantedBy=multi-user.target
- Reload systemd so it picks up the new file:
sudo systemctl daemon-reload. - Start the service:
sudo systemctl start myapp.service. - Enable it to start at boot:
sudo systemctl enable myapp.service.
That is the whole process. The WantedBy=multi-user.target line creates a symlink in multi-user.target.wants/, which is the standard way to make a service start at boot without blocking the boot process.
Understanding the [Unit], [Service], and [Install] Sections
Every service unit has three main sections, though [Install] is only needed if you want the service to start automatically at boot.
- [Unit] – Contains metadata and dependencies. Common keys:
Description,After=,Requires=,Wants=.After=only orders the start; it does not create a dependency. If you need the service to start only after another service is up, useAfter=plusRequires=orWants=. - [Service] – Defines how the process runs. The most important key is
ExecStart=, which gives the full path to the executable plus arguments. For simple services, you also setType=(see below) and oftenRestart=to handle crashes. - [Install] – Used by
systemctl enableto create symlinks.WantedBy=multi-user.targetis the standard for services that should start in normal multi-user mode.
There is also a [Unit] key ConditionPathExists= that can prevent the service from starting if a file or directory is missing—useful for services that depend on a mount point.
Choosing the Right Type= and Restart= Settings
The Type= directive tells systemd how to detect that the service has started successfully. Getting this wrong is the most common reason a service fails to start or is reported as “activating” forever.
Type=simple– The process is considered started immediately afterExecStartforks. This is the default and works for long-running daemons that do not fork.Type=forking– The process forks and the parent exits. systemd waits for the parent to exit, then considers the service started. You usually need to setPIDFile=so systemd can track the child.Type=oneshot– The process runs once and exits; systemd considers the service started after the process exits. Use this for one-time setup tasks. Often combined withRemainAfterExit=yesso the service shows as “active” even after the process exits.Type=notify– The process sends a notification viasd_notify()when it is ready. This is the best type for services that need to signal readiness, but the program must be written to support it.
The Restart= directive controls what happens when the process exits. Common values:
Restart=on-failure– Restart only if the exit code is non-zero or the process was killed by a signal (except for a few signals like SIGTERM). This is a good default for most services.Restart=always– Restart regardless of exit status. Use for services that should never stop, but be careful with oneshot services.Restart=no– Do not restart. This is the default.
You can also add RestartSec= to set a delay between restarts (e.g., RestartSec=5).
Common Problems and How to Fix Them
When a service fails, the first thing to do is check its status: systemctl status myapp.service. This shows whether it is active, failed, or exited, and often includes the last few log lines. For full logs, use journalctl -u myapp.service.
Here are the most frequent issues:
- “Unit not found” – The file is not in a directory systemd scans, or the daemon was not reloaded. Run
daemon-reloadand check the path. - “Exec format error” – The
ExecStartpath is wrong, the file is not executable, or it lacks a shebang line if it is a script. Usechmod +xand verify the path withwhich. - Service starts then immediately stops – Often a
Typemismatch. If the process forks, useType=forking; if it runs in the foreground, useType=simple. - Permission denied – The user specified in
User=does not have execute permission on the binary or read permission on needed files. Check the file permissions and the user. - Environment variables missing – systemd does not source
/etc/profileor~/.bashrc. UseEnvironment=orEnvironmentFile=to set variables explicitly.
Overriding and Extending Existing Units
You should never edit unit files that come from packages, because updates will overwrite your changes. Instead, use drop-in overrides. Create a directory named myapp.service.d inside /etc/systemd/system/, then place a file ending in .conf there. For example:
sudo mkdir -p /etc/systemd/system/myapp.service.d sudo nano /etc/systemd/system/myapp.service.d/override.conf
In that file, you can add or override directives:
[Service] Restart=always Environment=MY_VAR=value
After saving, run systemctl daemon-reload and restart the service. This is the cleanest way to customize a service without losing changes on package updates.
Security and Hardening Options
systemd provides several directives to sandbox your service and reduce its attack surface. While not required, they are good practice for services exposed to untrusted input. Some useful ones:
User=andGroup=– Run the service as an unprivileged user instead of root.NoNewPrivileges=true– Prevents the process and its children from gaining new privileges (e.g., via setuid binaries).PrivateTmp=true– Gives the service a private/tmpand/var/tmp.ProtectSystem=strict– Makes the entire filesystem read-only except for the paths you explicitly whitelist withReadWritePaths=.ProtectHome=true– Makes/home,/root, and/run/userinaccessible.
These hardening options are not enabled by default, so you must add them yourself. They can break services that need to write to certain paths, so test thoroughly.
Frequently Asked Questions
How do I make a service start after a specific service?
Use After= in the [Unit] section to order the start, and Wants= or Requires= to create a dependency. For example, After=network-online.target ensures your service starts after the network is up, but you should also add Wants=network-online.target if you need the network to be actually configured.
What is the difference between systemctl enable and systemctl start?
start runs the service immediately, while enable creates the symlinks so it starts at boot. You can use enable --now to do both at once. A service can be started without being enabled, and vice versa.
How do I run a service as a specific user?
Add User=username and optionally Group=groupname in the [Service] section. The user must exist on the system. systemd will drop privileges before executing the command.
Why does my service fail with “Failed at step EXEC”?
This usually means the executable in ExecStart cannot be run. Check that the path is correct, the file exists, has execute permissions, and has a valid shebang if it is a script. Also verify that the user specified in User= has permission to execute it.
Can I create a service that runs a command with arguments?
Yes, ExecStart accepts the full command line, including arguments. For example, ExecStart=/usr/bin/python3 /opt/myscript.py --daemon. If the command contains spaces or special characters, you may need to quote them according to systemd’s parsing rules.
How do I remove a service I created?
Stop it with systemctl stop myapp.service, disable it with systemctl disable myapp.service, then delete the unit file and run systemctl daemon-reload. If you used drop-ins, remove those directories as well.
Related
Method: the standard formula for this calculation, rounded as each result states. See how the calculators work or report an error.
More cards in this drawer
- Campus Tech Innovation Hubs: What They Are
- Poco C51 Airtel Prepaid Locked Variant India?
- Jenkins vs GitHub Actions: Which CI/CD Fits Your Team?
- Turn Off Backup Encryption in iTunes?
- Shared VPS Hosting: Is It Better?
- Disk Defrag Pro: What It Does and Whether You Need It