Systemd Unit File Configuration for Linux Service Management

Services that start manually or live until the first reboot are a risk for business. We configure systemd unit files so your applications start automatically, restart correctly after failures, and run stably. Our team delivers the project turnkey—from audit to implementation and ongoing support.

Our competencies:

Frequently Asked Questions

Latest works

  • Development of a web application for FEEDME
    Development of a web application for FEEDME
    1359
  • Development of an online store for the company FURNORO
    Development of an online store for the company FURNORO
    1330
  • B2B Advance company logo design
    B2B Advance company logo design
    776
  • Development of a web application for Enviok
    Development of a web application for Enviok
    1077
  • AIDER company logo development
    AIDER company logo development
    1022
  • CRM development for Chasseurs
    CRM development for Chasseurs
    1113

Systemd Unit File Configuration for Production Service Management

You deployed the application, but running it manually via nohup ./app & is not production. We've been through this: without systemd the service lives until the first reboot or crash. Systemd is the standard way to manage services on modern Linux systems, and the unit file defines everything: how to start, how to restart on failure, what resources to allocate, in which environment to run. Our engineers with 5+ years of experience will help you configure unit files turnkey — systemd really simplifies life. Order an audit of your unit files — it takes 2 days.

Why "just run" doesn't work reliably

Typical scenario: the service is a Node.js application, started by a start.sh script, runs as root, environment variables are hardcoded in the script. On server reboot — silence. The developer connects via SSH and launches manually. A month later everyone forgets about it. According to our statistics, over 80% of such services have autostart or resource leak issues. As a result, service downtime costs businesses tens of lost hours monthly.

The second problem is dependencies. If the application starts before PostgreSQL or Redis, it crashes with ECONNREFUSED and never recovers. Systemd can track dependencies via After= and Requires=, but without proper configuration of Type= and ExecStartPost= it works unreliably — the service started, but the database is not yet ready to accept connections. Implementing correct unit files reduces such incidents by 70%.

Anatomy of a production-grade unit file

# /etc/systemd/system/myapp.service
[Unit]
Description=My Application Service
After=network-online.target postgresql.service
Wants=network-online.target
Requires=postgresql.service

[Service]
Type=notify
User=myapp
Group=myapp
WorkingDirectory=/opt/myapp
EnvironmentFile=/etc/myapp/env
ExecStart=/opt/myapp/bin/myapp --config /etc/myapp/config.yaml
ExecReload=/bin/kill -HUP $MAINPID
Restart=on-failure
RestartSec=5s
TimeoutStartSec=30s
TimeoutStopSec=30s

# Resource limits
LimitNOFILE=65536
MemoryMax=2G
CPUQuota=80%

# Isolation
NoNewPrivileges=yes
ProtectSystem=strict
ProtectHome=yes
ReadWritePaths=/var/lib/myapp /var/log/myapp
PrivateTmp=yes

[Install]
WantedBy=multi-user.target

Several details that are often missed. Type=notify means the process itself tells systemd it's ready via sd_notify(3). If the application does not do this — use Type=simple or Type=forking. Wrong Type leads to dependent services starting before this one initializes.

EnvironmentFile=/etc/myapp/env is the correct way to pass environment variables. The file should contain lines like KEY=value, no export needed. Secrets are not stored in plain text in production — use either Vault with an agent or systemd credentials (LoadCredential=).

ProtectSystem=strict + ReadWritePaths= — minimal set for isolation. The service cannot write anywhere except explicitly allowed paths. This breaks poorly written applications that write logs to /tmp or ./logs — but that's a reason to fix them.

How socket activation works in systemd?

For services with high start-up load — socket activation allows systemd to keep the socket open while the application initializes:

# /etc/systemd/system/myapp.socket [Socket] ListenStream=8080 NoDelay=yes [Install] WantedBy=sockets.target 

Requests are not lost even if the service restarts — systemd buffers incoming connections. This gives up to 3x reliability improvement over normal setup. In practice, this approach reduces downtime during restarts by 95%.

What resources can be controlled in a unit file?

Systemd allows flexible resource limits via directives in the [Service] section:

Resource Directive Example
Memory MemoryMax= MemoryMax=2G
CPU CPUQuota= CPUQuota=80%
File descriptors LimitNOFILE= LimitNOFILE=65536
Number of processes TasksMax= TasksMax=100
Disk I/O bandwidth IOReadBandwidthMax=, IOWriteBandwidthMax= IOReadBandwidthMax=/var/log 1M

These limits prevent the "noisy neighbor" effect and protect the system from overload. In our projects, we often combine MemoryMax and CPUQuota for stable operation under constrained resources.

Typical mistakes when writing unit files

After=postgresql.service does not guarantee the database is ready to accept connections — only that the unit has started. For actual readiness check, use ExecStartPre= with a wait script:

ExecStartPre=/usr/bin/pg_isready -h localhost -p 5432 -U myapp -t 30 

Restart=always on configuration errors triggers an infinite restart loop. Systemd limits this via StartLimitIntervalSec= and StartLimitBurst=, but it's better to use Restart=on-failure and monitor via systemctl is-failed. Below is a comparison of popular restart policies:

Policy Behavior Recommendation
no Do not restart For one-shot tasks
on-failure Only on non-zero exit code Main choice for production
always On any exit Only for critical services with loop protection
on-abnormal On signals and crashes For services that can terminate gracefully

After modifying the unit file, always: systemctl daemon-reload before systemctl restart. Without it, systemd continues to use the old version of the file from memory.

Additional tips
  • Use systemd-analyze security myapp.service to evaluate the security of the unit file.
  • For debugging, view the log: journalctl -u myapp -f.
  • Set up failure notifications: OnFailure=myapp-failure-notify@%n.service.

Process of work

Audit of the current state: how services are started, whether unit files already exist, dependencies between components. Design the structure: users, directories, environment variables, restart policies. Write unit files with isolation and resource limits. Test on staging — systemd-analyze security myapp.service shows a security score, journalctl -u myapp -f shows real-time logs.

Scope Duration
1–3 services, simple dependencies 4–8 hours
5–10 services with socket activation 1–2 days
Full audit + rework + documentation 2–3 days

The cost is calculated individually after an audit of the current service configuration. Contact us — we will evaluate your project.

What is included in the work

  • Audit of existing unit files and startup scripts
  • Design of service structure (users, paths, environment)
  • Writing unit files with isolation and resource limits
  • Configuration of socket activation for high-load components
  • Testing on staging and setting up monitoring via journalctl and systemctl
  • Maintenance documentation and deployment instructions
  • Support for 30 days after delivery

Order systemd unit file configuration — your services will run stably without manual intervention.