Your Own Unit File

Writing a Unit File for Your Own Application

The application is a PHP 8.5 script behind the built-in web server, php -S, which the PHP manual says not to expose to the internet: treat it as an internal endpoint. The unit is the same for a queue worker.

/srv/hello-api/public/index.phpPHP
<?php // hello-api: one JSON status document, served by PHP's built-in web server
header('Content-Type: application/json');
echo json_encode(['app' => 'hello-api', 'env' => getenv('APP_ENV') ?: 'unset',
    'pid' => getmypid(), 'php' => PHP_VERSION,
    'user' => posix_getpwuid(posix_geteuid())['name']]), "\n";
/etc/systemd/system/hello-api.serviceShell
[Unit]
Description=hello-api JSON status endpoint
After=network.target
[Service]
Type=exec
User=helloapi
Group=helloapi
WorkingDirectory=/srv/hello-api
Environment=APP_ENV=production PORT=8105
ExecStart=/usr/bin/php -S 127.0.0.1:${PORT} -t /srv/hello-api/public
Restart=on-failure
RestartSec=2
[Install]
WantedBy=multi-user.target

User= runs it as a system account with no login, never as root. Type=exec, which the manual recommends for long-running services, reports success only once the binary has really executed, so a mistyped path fails start instead of passing as it does under the default Type=simple. The program must stay in the foreground; systemd 142,543 fills in ${PORT} from Environment=.

Installing and starting the unitShell
sudo useradd --system --no-create-home -d /srv/hello-api -s /usr/sbin/nologin helloapi
sudo install -d -m 755 /srv/hello-api/public        # then save index.php there
systemd-analyze verify /etc/systemd/system/hello-api.service
sudo systemctl daemon-reload
sudo systemctl enable --now hello-api
curl -s http://127.0.0.1:8105/
Output
Created symlink '/etc/systemd/system/multi-user.target.wants/hello-api.service' →
  '/etc/systemd/system/hello-api.service'.
{"app":"hello-api","env":"production","pid":1908,"php":"8.5.4","user":"helloapi"}

verify printed nothing, so the file parsed and its binaries exist. Run daemon-reload after any edit to a unit file; forgetting it is why a change "does nothing".