15%

Save 15% on All Hosting Services

Test your skills and get Discount on any hosting plan

Use code:

Skills
Get Started
06.11.2024

How to Use systemd to Start a Linux Service at Boot

Ensuring that critical services start automatically when your server reboots is one of the most fundamental responsibilities of any Linux systems administrator. Whether you're running a web application, a database engine, or a custom daemon on a VPS Hosting environment, an unexpected reboot should never mean prolonged downtime. With systemd — the modern init system powering the majority of today's Linux distributions — you can define precisely how, when, and as whom your services launch, all through a clean, declarative unit file.

This comprehensive guide walks you through every step: understanding what systemd is, creating a production-ready service unit file, verifying permissions, testing your executable, and managing the service lifecycle. By the end, your custom application will survive every reboot automatically.

What Is systemd and Why Does It Matter?

systemd is an init system and service manager that replaced older alternatives such as SysVinit and Upstart across most major Linux distributions, including Ubuntu, Debian, CentOS, Rocky Linux, AlmaLinux, and Fedora. Rather than executing a sequential chain of shell scripts at boot, systemd parallelizes service startup, resolves dependency ordering, and manages the entire lifecycle of system processes from a single, unified interface.

Key advantages of systemd for server environments

FeatureBenefit
Parallel service startupFaster boot times on multi-service servers
Dependency managementServices start only after their prerequisites are ready
Automatic restart policiesCrashed services recover without manual intervention
Centralized logging via journaldAll service output captured in a queryable journal
Cgroup-based resource controlCPU and memory limits enforced per service
Socket and device activationServices start on demand, not unconditionally

For anyone managing applications on Dedicated Servers or VPS instances, these capabilities translate directly into higher uptime and more predictable behavior under load.

Understanding systemd Unit Files

Everything systemd manages is represented by a unit file — a plain-text configuration file divided into sections. A .service unit file tells systemd:

  • What the service is (metadata, description, dependencies)
  • How to run it (executable path, working directory, user context)
  • When to start it (boot target, ordering relative to other units)
  • What to do if it fails (restart policy, restart delay)

Service unit files for system-wide services live in /etc/systemd/system/. Files placed here take precedence over vendor-supplied units in /lib/systemd/system/ and persist across package upgrades.

Step-by-Step: Creating a systemd Service Unit

The following walkthrough uses a fictional application called myapp. Replace every instance of myapp, myuser, and /usr/bin/myapp with values appropriate for your own environment.

Step 1: Prepare the Working Directory

Before writing the unit file, decide where your application will run from. A dedicated working directory keeps configuration files, logs, and runtime data organized and makes permission management straightforward.

Create the directory if it does not already exist:

sudo mkdir -p /opt/myapp

> Why /opt/ instead of /etc/systemd/?

> The /etc/systemd/ tree is reserved for systemd's own configuration. Placing application data there is non-standard and can cause confusion. Use /opt/myapp, /srv/myapp, or /var/lib/myapp depending on the Filesystem Hierarchy Standard category that best fits your workload.

Assign ownership to the user that will run the service:

sudo useradd --system --no-create-home --shell /usr/sbin/nologin myuser
sudo chown -R myuser:myuser /opt/myapp

Using a dedicated system account (no login shell, no home directory) is a security best practice. It limits the blast radius if the application is ever compromised.

Verify the result:

ls -ld /opt/myapp
# Expected output: drwxr-xr-x 2 myuser myuser 4096 Jan 1 00:00 /opt/myapp

Step 2: Create the Service Unit File

Open a new unit file with your preferred editor:

sudo nano /etc/systemd/system/myapp.service

Paste the following content, adjusting values to match your application:

[Unit]
Description=My Custom Application
Documentation=https://example.com/docs
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
ExecStart=/usr/bin/myapp --config /opt/myapp/myapp.conf
ExecReload=/bin/kill -HUP $MAINPID
Restart=on-failure
RestartSec=5s
User=myuser
Group=myuser
WorkingDirectory=/opt/myapp
StandardOutput=journal
StandardError=journal
SyslogIdentifier=myapp

# Security hardening
NoNewPrivileges=true
ProtectSystem=strict
ProtectHome=true
PrivateTmp=true

[Install]
WantedBy=multi-user.target

Save the file with Ctrl+O, then exit with Ctrl+X.

Step 3: Understanding Every Directive

#### [Unit] Section

DirectivePurpose
DescriptionHuman-readable name shown in systemctl status output
DocumentationURL or man page reference for the service
AfterEnsures the service starts *after* the listed unit is active
WantsSoft dependency — systemd will *try* to start the listed unit but won't fail if it's unavailable

> network-online.target vs network.target: Use network-online.target (combined with Wants=network-online.target) for services that genuinely need a fully configured network interface — for example, an application that connects to a remote database or an external API at startup. network.target only guarantees that the network subsystem has been *started*, not that interfaces are up and addresses are assigned.

#### [Service] Section

DirectivePurpose
Type=simpleThe process started by ExecStart is the main process (default for most applications)
ExecStartFull path to the binary and any arguments. Always use absolute paths.
ExecReloadCommand to reload configuration without a full restart (optional but recommended)
Restart=on-failureRestart the service only when it exits with a non-zero code or is killed by a signal. Use always if you want restarts even on clean exits.
RestartSecSeconds to wait before attempting a restart
User / GroupRun the process as this user/group instead of root
WorkingDirectorySet the current directory before executing ExecStart
StandardOutput / StandardErrorRoute stdout/stderr to the systemd journal
SyslogIdentifierTag used to filter journal entries for this service
NoNewPrivilegesPrevents the process from gaining additional privileges via setuid binaries
ProtectSystem=strictMounts /usr, /boot, and /etc as read-only for the service
ProtectHomeMakes /home, /root, and /run/user inaccessible
PrivateTmpGives the service its own private /tmp namespace

#### [Install] Section

DirectivePurpose
WantedBy=multi-user.targetEnables the service when the system reaches the standard multi-user (non-graphical) runlevel. This is the correct target for virtually all server daemons.

Step 4: Verify File and Directory Permissions

Before reloading systemd, confirm that the service user can actually access everything it needs:

# Check working directory ownership and permissions
ls -ld /opt/myapp

# Confirm the executable exists and is executable
ls -l /usr/bin/myapp

# Verify the config file is readable by myuser
sudo -u myuser cat /opt/myapp/myapp.conf

If any of these checks fail, correct the permissions before proceeding:

# Make the binary executable
sudo chmod +x /usr/bin/myapp

# Grant read access to the config file
sudo chmod 640 /opt/myapp/myapp.conf
sudo chown myuser:myuser /opt/myapp/myapp.conf

Step 5: Test the Executable Manually

Always verify that your application runs correctly *before* handing control to systemd. This isolates application bugs from systemd configuration issues:

sudo -u myuser /usr/bin/myapp --config /opt/myapp/myapp.conf

If the application starts without errors, press Ctrl+C to stop it and proceed. If it fails, troubleshoot the application itself — check that all dependencies are installed, environment variables are set, and required ports are available.

Step 6: Reload systemd and Enable the Service

After saving the unit file, instruct systemd to re-read its configuration:

sudo systemctl daemon-reload

Enable the service so it starts automatically at every subsequent boot:

sudo systemctl enable myapp.service

This command creates a symlink in the appropriate .wants directory, linking your unit file into the multi-user.target boot sequence. You should see output similar to:

Created symlink /etc/systemd/system/multi-user.target.wants/myapp.service → /etc/systemd/system/myapp.service.

Start the service immediately without rebooting:

sudo systemctl start myapp.service

Step 7: Verify the Service Is Running

Check the current status of the service:

sudo systemctl status myapp.service

A healthy service produces output like this:

● myapp.service - My Custom Application
     Loaded: loaded (/etc/systemd/system/myapp.service; enabled; vendor preset: disabled)
     Active: active (running) since Wed 2025-01-01 12:00:00 UTC; 5s ago
   Main PID: 12345 (myapp)
      Tasks: 4 (limit: 4915)
     Memory: 12.3M
        CPU: 45ms
     CGroup: /system.slice/myapp.service
             └─12345 /usr/bin/myapp --config /opt/myapp/myapp.conf

Key fields to verify:

  • Loaded — confirms the unit file was parsed successfully and shows whether it is enabled or disabled for boot
  • Active: active (running) — the service is currently running
  • Main PID — the process ID of your application

Step 8: Monitor Logs with journalctl

systemd routes all service output to the journal, a structured, queryable log store. Use journalctl to inspect it:

# View all logs for myapp (most recent last)
journalctl -u myapp.service

# Follow live log output (like tail -f)
journalctl -u myapp.service -f

# Show only logs since the last boot
journalctl -u myapp.service -b

# Show the last 50 lines
journalctl -u myapp.service -n 50

# Show logs since a specific time
journalctl -u myapp.service --since "2025-01-01 12:00:00"

If your service fails to start, the journal almost always contains the exact error message explaining why. This is the first place to look before making any changes.

Step 9: Test the Boot Behavior

To confirm the service survives a reboot without manually inspecting the boot sequence, you can simulate it:

# Reboot the server (only if safe to do so)
sudo reboot

After the server comes back online, check the service status again:

sudo systemctl status myapp.service

If it shows active (running), your service is correctly configured for automatic startup.

Managing the Service Lifecycle

Once your service is running, you'll use the following commands regularly:

Common systemctl Commands

# Start the service
sudo systemctl start myapp.service

# Stop the service gracefully
sudo systemctl stop myapp.service

# Restart the service (stop + start)
sudo systemctl restart myapp.service

# Reload configuration without restarting (if ExecReload is defined)
sudo systemctl reload myapp.service

# Enable automatic startup at boot
sudo systemctl enable myapp.service

# Disable automatic startup at boot
sudo systemctl disable myapp.service

# Check whether the service is enabled
sudo systemctl is-enabled myapp.service

# Check whether the service is currently active
sudo systemctl is-active myapp.service

# View the full unit file as systemd interprets it
sudo systemctl cat myapp.service

# Edit the unit file and reload in one step
sudo systemctl edit --full myapp.service

Troubleshooting Common Issues

Service fails to start: "No such file or directory"

This usually means ExecStart points to a non-existent binary or WorkingDirectory does not exist. Verify both paths:

which myapp
ls -l /opt/myapp

Service starts but immediately exits

Check the journal for the application's own error output:

journalctl -u myapp.service -n 100 --no-pager

Also verify that Type=simple is correct for your application. If your binary forks itself into the background, use Type=forking instead.

"Permission denied" errors

The service user lacks access to a required file or directory. Use ls -l to audit permissions and sudo -u myuser to test access interactively.

Port already in use

Another process is bound to the port your application needs. Identify it with:

sudo ss -tlnp | grep :<port>

Service restarts in a loop

If Restart=on-failure causes rapid restart loops, systemd will eventually throttle restarts. Check StartLimitIntervalSec and StartLimitBurst in the [Unit] section to tune this behavior, and always investigate the root cause in the journal.

Advanced systemd Patterns for Production Servers

Environment Variables and Environment Files

Never hardcode secrets in unit files. Use an environment file instead:

[Service]
EnvironmentFile=/etc/myapp/myapp.env
ExecStart=/usr/bin/myapp

Create /etc/myapp/myapp.env with chmod 600 and chown root:myuser to restrict access:

DATABASE_URL=postgresql://user:password@localhost/mydb
API_KEY=supersecretkey

Service Dependencies and Ordering

If your application depends on a database service (e.g., PostgreSQL or MySQL), declare that dependency explicitly:

[Unit]
After=network-online.target postgresql.service
Requires=postgresql.service

Requires is a hard dependency — if PostgreSQL fails to start, systemd will not attempt to start your service either.

Watchdog Integration

For mission-critical services, enable systemd's built-in watchdog to detect hung processes:

[Service]
WatchdogSec=30s
Restart=on-watchdog

Your application must call sd_notify(0, "WATCHDOG=1") periodically to reset the watchdog timer. If it fails to do so within WatchdogSec, systemd kills and restarts the service.

Choosing the Right Hosting Environment

The systemd configuration patterns described in this guide apply equally across all Linux server types, but your choice of hosting infrastructure affects how much control you have over the init system and service management.

  • VPS Hosting — Full root access, complete systemd control, ideal for custom application deployments. AlexHost VPS plans run on KVM virtualization, giving you a genuine isolated kernel environment.
  • Dedicated Servers — Maximum performance and isolation for resource-intensive services. Full hardware access with no noisy neighbors.
  • VPS with cPanel — Combines root-level systemd access with a graphical control panel for teams that manage both system services and web hosting from a single interface.
  • Shared Web Hosting — Suitable for standard web applications that don't require custom system services. No root access, but zero server management overhead.

If you're deploying a custom daemon, microservice, or any application that needs to survive reboots automatically, a VPS or dedicated server with full root access is the appropriate choice.

Quick Reference: Complete systemd Service Checklist

Before considering your service production-ready, verify every item on this checklist:

  • [ ] Unit file saved to /etc/systemd/system/myapp.service
  • [ ] ExecStart uses an absolute path to a valid, executable binary
  • [ ] WorkingDirectory exists and is owned by the service user
  • [ ] Service runs as a dedicated non-root system account
  • [ ] sudo systemctl daemon-reload executed after any unit file change
  • [ ] sudo systemctl enable myapp.service confirms the symlink was created
  • [ ] sudo systemctl status myapp.service shows active (running)
  • [ ] journalctl -u myapp.service shows no errors
  • [ ] Server rebooted and service confirmed running post-boot
  • [ ] Security hardening directives (NoNewPrivileges, ProtectSystem, PrivateTmp) applied

Conclusion

systemd is the definitive service management layer for modern Linux servers. By creating a well-structured .service unit file, you gain automatic startup at boot, intelligent restart policies, centralized logging, and fine-grained security controls — all without writing a single line of shell script.

The process is straightforward: prepare a dedicated working directory and system user, write the unit file with the correct [Unit], [Service], and [Install] sections, reload the systemd daemon, enable and start the service, then verify with systemctl status and journalctl. Apply the security hardening directives discussed above, and your service will be both resilient and appropriately sandboxed from the rest of the system.

Whether you're deploying a Node.js API, a Python worker, a Go binary, or any other custom application on an AlexHost VPS Hosting or Dedicated Servers plan, this systemd workflow ensures your service is always running when your users need it — even after an unexpected reboot.

15%

Save 15% on All Hosting Services

Test your skills and get Discount on any hosting plan

Use code:

Skills
Get Started