MaruOpsInfrastructure and AI notes

About (Korean)Contact (Korean)

Use systemd: Create, Edit and Remove Services and Read journalctl Logs

Workspace with laptop and external monitor

Reference photo · Workspace with laptop and external monitor · Photo: SuperBlobMonster / Source / CC BY-SA 3.0 · Resized for listings; illustrative photo, not the tested environment

A program that must keep running after SSH disconnects needs an execution account, restart policy and log handling. A systemd service unit records those settings together with its start command and boot behavior. Follow a custom service through creation, changes, diagnosis and removal.

Here, we create maru-heartbeat.service, which does not open ports to the outside and only logs periodically. The target is Rocky Linux 9, Ubuntu 24.04·26.04’s system service. The management scope is different from systemctl --user in the user session.

Linux operation articles seen together · Rocky 9 Fixed IP · Ubuntu 24.04 Netplan · Ubuntu 26.04·nmtui·systemd service·log·LVM root extension·general partition root Expand

On this page

1. start and enable are different settings

commandWhat you doPoints to Remember
start / stopStart/stop the process nowIndependent of auto-start on boot setting
enable / disableRegister and cancel dependencies such as bootingIf –now is not added, the current execution state will not be changed.
restartStop and restartThere may be brief interruptions.
reloadAsk the app to reread its settingsService must support reload
daemon-reloadsystemd rereads unit definitionThis command does not automatically restart running apps

disable is not a function that prevents execution. It can be re-executed through manual startup or as a dependency on another unit. A mask that blocks the startup itself has a larger impact area, so identify its purpose and use it. Detailed operation can be checked based on the systemctl manual .

systemctl --version
systemctl list-units --type=service --state=running
systemctl list-unit-files --type=service
systemctl --failed

2. Prepare execution account and program

First check if the name below already exists. If you already have the same account or service, choose a different lab name and change the path afterwards as well. Only administrators can change accounts and executable files, and services run under an account with limited privileges.

getent passwd maruops-demo
systemctl cat maru-heartbeat.service

If it has not been created yet, it is expected that the account/unit will not be found in the above command. Proceed as follows only if you are creating a new one.

sudo useradd --system --user-group --no-create-home \
  --shell /usr/sbin/nologin maruops-demo
sudoedit /usr/local/bin/maru-heartbeat
#!/usr/bin/bash
set -eu

interval="${HEARTBEAT_INTERVAL:-15}"
case "$interval" in
  ''|*[!0-9]*)
    printf 'HEARTBEAT_INTERVAL must be a positive integer\n' >&2
    exit 2
    ;;
esac
if [ "$interval" -lt 1 ]; then
  printf 'HEARTBEAT_INTERVAL must be at least 1\n' >&2
  exit 2
fi

trap 'printf "heartbeat stopping\n"; exit 0' TERM INT
while :; do
  printf 'heartbeat time=%s interval=%s\n' "$(date --iso-8601=seconds)" "$interval"
  sleep "$interval" &
  wait "$!"
done

The script leaves the time on standard output and terminates normally when a termination signal is received. & used by Bash to handle signals while waiting for sleep. This is different from a configuration that runs the main process of the actual service in the background and exits systemd.

sudo chown root:root /usr/local/bin/maru-heartbeat
sudo chmod 755 /usr/local/bin/maru-heartbeat
sudo bash -n /usr/local/bin/maru-heartbeat
sudoedit /etc/maru-heartbeat.env
HEARTBEAT_INTERVAL=15
sudo chown root:root /etc/maru-heartbeat.env
sudo chmod 644 /etc/maru-heartbeat.env

In this environment file, only the interval values that can be made public are entered. EnvironmentFile is not a source file like a regular shell script, so no export or arbitrary shell commands are written. Do not assume that secrets such as passwords and tokens are safely hidden only in environment variables; follow the application’s secret management method. Please refer to the configuration description of systemd.exec.

3. Create and register a unit file

sudoedit /etc/systemd/system/maru-heartbeat.service
[Unit]
Description=MaruOps heartbeat demonstration
StartLimitIntervalSec=60
StartLimitBurst=5

[Service]
Type=exec
User=maruops-demo
Group=maruops-demo
EnvironmentFile=/etc/maru-heartbeat.env
ExecStart=/usr/local/bin/maru-heartbeat
Restart=on-failure
RestartSec=3s
TimeoutStopSec=10s
StandardOutput=journal
StandardError=journal
SyslogIdentifier=maru-heartbeat
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ProtectHome=true

[Install]
WantedBy=multi-user.target
settingsReason for choosing
Type=execUsed to treat failure of the step that runs the specified executable as a startup error.
User / GroupDo not grant unnecessary root privileges to services
Restart=on-failureRestart in case of abnormal termination. Administrator’s stop is not subject to automatic restart
StartLimitIntervalSec / BurstLimit infinite restarts to short periods of time
StandardOutput / ErrorView app output in journal
ProtectSystem / ProtectHomeThis log-only demo restricts access as it does not require modification to the file system or home.

Just because it is started with Type=exec does not mean that the app’s API or DB connection is ready. To elaborately notify that preparation is complete, review the methods supported by the program, such as notify. In an actual app that writes files, you will need to design StateDirectory or write permission paths, so do not copy and apply the security settings in this demo. Service type/restart policy and unit common settings are the standard documents.

This program doesn’t use any external network, so I didn’t put the network-online.target dependency. After is a start order and is not a function to pull the other unit, and network online judgment does not guarantee a response from a specific DB or Internet service.

sudo chmod 644 /etc/systemd/system/maru-heartbeat.service
sudo systemd-analyze verify /etc/systemd/system/maru-heartbeat.service
sudo systemctl daemon-reload
sudo systemctl enable --now maru-heartbeat.service
systemctl is-enabled maru-heartbeat.service
systemctl is-active maru-heartbeat.service
sudo systemctl status maru-heartbeat.service --no-pager

In Rocky, if the execute permissions are correct but are denied, check the SELinux context. You can restore it to sudo restorecon -v /usr/local/bin/maru-heartbeat according to the default path. Do not fix it by turning off SELinux without determining the cause of the rejection.

4. Choose a reflection method according to the changed object.

What I changedreflection order
App script/environment file contentsRestart the service
unit body·drop-inAfter daemon-reload, restart the service
Settings in the app itselfReload if the app supports reload, otherwise restart
Runs at boot timeenable/disable; If you want to change now –now

To change the log interval, change the value of /etc/maru-heartbeat.env to 5 and restart. Since the path or definition of the unit has not changed, daemon-reload is not necessary in this case. We have not implemented the reload behavior in this demo.

sudoedit /etc/maru-heartbeat.env
sudo systemctl restart maru-heartbeat.service
sudo journalctl -u maru-heartbeat.service -n 20 --no-pager

Overwrite some settings with drop-in

When changing the unit provided by a distribution or package, use drop-in rather than directly overwriting the original. You can practice the same method in the demo. Place the following two lines in the editing area of ​​the command below and save. If you write outside the “save area” indicated in the editor, it may not be reflected.

sudo systemctl edit maru-heartbeat.service
[Service]
RestartSec=10s
sudo systemctl daemon-reload
sudo systemctl restart maru-heartbeat.service
systemctl cat maru-heartbeat.service
systemctl show maru-heartbeat.service \
  -p FragmentPath -p DropInPaths -p RestartUSec

A typical storage path is /etc/systemd/system/maru-heartbeat.service.d/override.conf. When changing an ExecStart from a drop-in, you must first specify the new ExecStart with the line ExecStart=, which clears the existing list. Before deleting the demo unit you created yourself, double-check the actual path with cat·FragmentPath.

5. View only necessary logs in journalctl

sudo journalctl -u maru-heartbeat.service -n 50 --no-pager
sudo journalctl -u maru-heartbeat.service -f

-f continues waiting for new logs. Ctrl+C ends log viewing and the service continues to run. Run each of the following commands separately as needed.

sudo journalctl -u maru-heartbeat.service --since today --no-pager
sudo journalctl -u maru-heartbeat.service --since '1 hour ago' -o short-iso --no-pager
sudo journalctl -u maru-heartbeat.service -b --no-pager
sudo journalctl --list-boots
sudo journalctl -u maru-heartbeat.service -b -1 --no-pager
sudo journalctl -k -b -p warning --no-pager

-b -1 reads archived logs from previous boots. If there are no previous logs left, they cannot be viewed. -p warning filters by the severity recorded in the journal, so simply writing ERROR in the app’s output sentence does not result in an error level. You can check the meaning of the filter in the journalctl manual.

To keep logs across reboots

sudo journalctl --disk-usage
sudo install -d -m 755 /etc/systemd/journald.conf.d
sudoedit /etc/systemd/journald.conf.d/60-maruops-retention.conf
[Journal]
Storage=persistent
SystemMaxUse=200M
SystemKeepFree=1G
MaxRetentionSec=14day
sudo systemctl restart systemd-journald.service
sudo journalctl --flush
sudo journalctl --disk-usage

This example applies to the entire system journal. Set capacity and retention to match the operating policy. If the size limit is reached before the configured age limit, older logs may be removed sooner. Also check the distribution’s default Storage behavior. Use the journald storage and size-limit documentation to balance retention requirements with available disk space.

If you need to clean up existing logs, store the necessary records externally and then execute them according to the policy, such as sudo journalctl --rotate --vacuum-time=14d. This operation deletes the old archive journal and is irreversible. This is not a command that selects and deletes the logs of just one service.

6. Where to look in a failed state

sudo systemctl status maru-heartbeat.service --no-pager -l
sudo journalctl -u maru-heartbeat.service -b -n 100 --no-pager
systemctl show maru-heartbeat.service \
  -p Result -p ExecMainStatus -p NRestarts
signWhat to check first
203/EXECExecution path·permission·shebang·SELinux·line break format
217/USERDoes a User·Group account exist?
200/CHDIRWorkingDirectory path and access permissions
start-limit-hitCauses of repeat failure and limits on number of restarts

If the start limit is reached after correcting the cause, initialize the failure status/counter of the unit with sudo systemctl reset-failed maru-heartbeat.service and start again. reset-failed does not fix executables or incorrect settings.

7. Delete the service you created yourself

The following is a procedure for removing only the files you created in this article. Services installed by the package follow the package’s removal/suspension policy. Stop running and auto-starting before deleting files.

sudo systemctl disable --now maru-heartbeat.service
sudo rm -f /etc/systemd/system/maru-heartbeat.service.d/override.conf
sudo rmdir --ignore-fail-on-non-empty /etc/systemd/system/maru-heartbeat.service.d
sudo rm -f /etc/systemd/system/maru-heartbeat.service
sudo systemctl daemon-reload
systemctl list-unit-files maru-heartbeat.service

If you did not create a drop-in, rmdir may output an error because the directory does not exist. Directories containing other files are not forcibly deleted. It is expected that unit is not found in systemctl status. Even if you delete the unit file, past journal or app data is not automatically deleted.

Remove the executable and environment files created just for this demo when they are no longer needed. To delete an account, make sure that no other processes or files are using the account. Since real applications have different lifetimes for executable files, settings, data, and logs, it is best to distinguish between deregistering services and deleting data.

Verification range · 2026-10-18: unit syntax was confirmed in systemd 252 of Rocky Linux 9.8 and systemd 259 of Ubuntu 26.04.1. Service creation/execution, environment file/drop-in reflection, automatic restart after abnormal termination, journal search/permanent storage were performed on systemd 255 of Ubuntu 24.04.5 VM. The demo’s permission restrictions don’t necessarily mean they will fit every app’s file access needs.

Download the network·systemd example file·Netplan YAML, bundled scripts that read service·environment files and storage structures. Change the address and device name to match the actual environment. It does not include scripts to auto-expand disks.