Tüm eğitimler
Rehber Linux / Sistem systemd

systemd —
service, timer, journal.

cron'dan öteye — unit file anatomisi, timer'lar, journald, dependency graph ve sandboxing.

00 Giriş: sysvinit'ten systemd'ye

PID 1 olarak systemd, boot sürecini orkestra eder — sysvinit'in sıralı çalışma modelinin yerini parallel socket activation aldı.

Kernel, userspace'i başlatmak için ilk process olarak PID 1'i çalıştırır. Tarihsel olarak bu sysvinit'ti: /etc/init.d/ altındaki shell script'leri, S10-S99 gibi sayısal sıraya göre teker teker çalıştırıyordu. Her servis bir öncekinin bitmesini bekliyordu — sequential bir pipeline. 20 servis varsa, her biri 1 saniye sürse bile boot 20 saniyeydi.

systemd'nin çözümü socket activation'dır: servis başlamadan önce socket'i kernel açar. Bir servis başka bir servisi bekliyorsa, isteği socket'e gönderir; kernel tampon tutar; servis hazır olunca yanıt verir. Bu sayede bağımlı servisler bile parallel başlayabilir.

  sysvinit: S10_udev → S20_network → S30_dbus → S40_ssh  (sıralı, yavaş)

  systemd:  udev ─┬─ network ─┬─ dbus ─┬─ ssh     (parallel, hızlı)
                  └─ syslog   └─ ntp   └─ crond
    

systemd-analyze blame

Hangi servisin boot'u yavaşlattığını görmek için:

bash
systemd-analyze blame
# Örnek çıktı:
#   7.423s NetworkManager-wait-online.service
#   2.101s dev-sda1.device
#   1.832s initrd-switch-root.service
#     823ms systemd-journal-flush.service
#     612ms accounts-daemon.service
#     289ms ModemManager.service

systemd-analyze time
# Startup finished in 1.823s (kernel) + 4.201s (initrd) + 8.112s (userspace)
# = 14.136s
NOT

systemd tüm büyük dağıtımlarda varsayılan init sistemidir: Ubuntu 16.04+, Debian 8+, CentOS/RHEL 7+, Fedora 15+, Arch Linux. Embedded sistemlerde Buildroot ve Yocto ile de kullanılabilir; ancak çok kısıtlı hedeflerde busybox init veya s6 tercih edilebilir.

01 Unit türleri

systemd'de her yönetilen nesne bir "unit" — dosya uzantısı türü belirler.

Uzantı Ne yönetir Örnek
.serviceDaemon veya tek seferlik processnginx.service, sshd.service
.timerZamana bağlı tetikleyici (cron yerine)backup.timer, logrotate.timer
.socketIPC/network socket — on-demand activationsshd.socket, cups.socket
.targetSenkronizasyon noktası / grupmulti-user.target, network.target
.mountFilesystem mount noktasıhome.mount, mnt-data.mount
.pathDosya sistemi değişikliği izlemecups.path
.deviceudev tarafından expose edilen cihazsys-bus-usb.device
.scopeDışarıdan başlatılan process grubusession-1.scope
.slicecgroup kaynak yönetim hiyerarşisiuser.slice, system.slice

Unit listesi görüntüleme

bash
# çalışan tüm service unit'leri
systemctl list-units --type=service

# aktif timer'lar
systemctl list-units --type=timer

# FAILED durumundaki tüm unit'ler
systemctl --failed

# yüklü ama çalışmayan dahil — tüm bilinenleri listele
systemctl list-unit-files --type=service

Unit dosya konumları

/etc/systemd/system/Sistem yöneticisi (admin) tarafından yazılan unit'ler. En yüksek öncelik — distro unit'lerini override eder.
/usr/lib/systemd/system/Paket yöneticisi (apt, dnf, pacman) tarafından kurulan unit'ler. Güncellemede üzerine yazılır — doğrudan düzenleme önerilmez.
/run/systemd/system/Runtime'da oluşturulan geçici unit'ler. Reboot sonrası kaybolur.
~/.config/systemd/user/Kullanıcı unit'leri — root yetkisi gerektirmez, systemctl --user ile yönetilir.
NOT

Öncelik sırası: /etc/systemd/system/ > /run/systemd/system/ > /usr/lib/systemd/system/. Aynı isimli unit birden fazla yerde varsa en yüksek öncelikteki kazanır.

02 [Service] anatomisi

Service unit dosyası üç bölümden oluşur: [Unit], [Service], [Install].

Minimal bir servis: hello.service

/etc/systemd/system/hello.service
# [Unit] — metadata ve bağımlılıklar
[Unit]
Description=Hello World servisi
After=network.target

# [Service] — çalışma parametreleri
[Service]
Type=simple
User=nobody
Group=nogroup
WorkingDirectory=/opt/hello
ExecStart=/opt/hello/hello --port 8080
Restart=on-failure
RestartSec=5s
StandardOutput=journal
StandardError=journal

# [Install] — enable/disable için hangi target'a bağlanacak
[Install]
WantedBy=multi-user.target

[Service] parametreleri

ExecStart=Çalıştırılacak komut. Tam path zorunlu. Argümanlar aynı satıra yazılabilir. Birden fazla ExecStart= birden fazla komut zinciri oluşturmaz — sadece son satır geçerlidir (override için ExecStart= boş bırakıp yeniden yaz).
ExecStartPre=ExecStart öncesi çalışan komut(lar). Birden fazla yazılabilir. Başa - koyarsan başarısız olsa da devam eder: ExecStartPre=-/bin/mkdir -p /var/run/myapp
ExecStartPost=Servis başladıktan sonra çalışır. Sağlık kontrolü veya bildirim için kullanışlı.
ExecStop=Servis durdurulurken çalışacak komut. Belirtilmezse systemd SIGTERM gönderir.
ExecReload=systemctl reload çağrısında çalışır. Genellikle SIGHUP: ExecReload=/bin/kill -HUP $MAINPID
Type=simple (default): ExecStart fork etmez, PID=1 process'tir. forking: daemon double-fork yapar, PIDFile= ile ana PID bildirilir. notify: daemon sd_notify() ile hazır olduğunu söyler. oneshot: komut çalışıp çıkar (ExecStart biter = servis tamamlandı). idle: tüm işler bittikten sonra çalışır (boot mesajlarını karıştırmamak için).
Restart=no: restart yok (default). always: her çıkışta yeniden başlat. on-failure: sıfır dışı exit code veya signal'da yeniden başlat. on-abnormal: signal veya timeout'da yeniden başlat. unless-stopped: elle durdurulmadıkça her zaman yeniden başlat.
RestartSec=Restart denemesi öncesi bekleme süresi. Saniye veya "5s", "500ms" formatı.
User= / Group=Servisi çalıştıracak kullanıcı ve grup. Root yerine düşük yetkili bir kullanıcı belirt — en iyi pratik.
WorkingDirectory=Çalışma dizini. ~ kullanılabilir. Belirtilmezse / olur.
Environment=Ortam değişkeni tanımla: Environment="PORT=8080" "DEBUG=false"
EnvironmentFile=Dosyadan ortam değişkeni yükle: EnvironmentFile=/etc/default/myapp. Dosya KEY=VALUE formatında olmalı.
StandardOutput= / StandardError=journal (default): journald'a gönder. syslog: syslog'a gönder. console: /dev/console'a yaz. null: sil. append:/path: dosyaya ekle.

Type=notify örneği

Type=notify ile daemon, başlamaya hazır olduğunda systemd'ye bildirim gönderir. systemd bu bildirimi alana kadar servisi "starting" durumunda tutar — bağımlı servisler bekler.

notify-daemon.c
/* gcc -o notify-daemon notify-daemon.c -lsystemd */
#include <systemd/sd-daemon.h>
#include <stdio.h>
#include <unistd.h>

int main(void) {
    /* başlatma işlemleri — socket aç, DB bağlan, vs. */
    sleep(2);

    /* systemd'ye "hazırım" bildir */
    sd_notify(0, "READY=1\nSTATUS=Listening on port 8080\n");

    /* ana döngü */
    while (1) {
        sd_notify(0, "WATCHDOG=1"); /* watchdog ping */
        sleep(30);
    }
    return 0;
}
/etc/systemd/system/notify-daemon.service
[Unit]
Description=sd_notify örnek daemon

[Service]
Type=notify
ExecStart=/usr/local/bin/notify-daemon
WatchdogSec=60s
Restart=on-failure

[Install]
WantedBy=multi-user.target

Bu bölümde öğrendikleriniz

  • Unit dosyası [Unit], [Service], [Install] üç bölümünden oluşur
  • Type= değeri systemd'nin PID ve hazır olma durumunu nasıl takip ettiğini belirler
  • Restart= ve RestartSec= ile servis otomatik yeniden başlatılabilir
  • sd_notify() ile daemon, başlangıç tamamlandığında systemd'yi bilgilendirir

03 systemctl komutları

systemctl, systemd'yi yöneten CLI aracıdır — servis başlatmaktan override dosyası yazmaya kadar her şey buradan.

Temel lifecycle komutları

bash
# servisi başlat / durdur / yeniden başlat
systemctl start   nginx.service
systemctl stop    nginx.service
systemctl restart nginx.service

# reload: process'i öldürmeden konfigürasyonu yeniden yükle (ExecReload= çalışır)
systemctl reload  nginx.service

# restart gerekirse reload dene, olmazsa restart yap
systemctl reload-or-restart nginx.service

# durum — Active, Loaded, Main PID, CGroup, son log satırları
systemctl status  nginx.service

# boot'ta otomatik başlat / devre dışı bırak
systemctl enable  nginx.service
systemctl disable nginx.service

# enable + hemen başlat
systemctl enable --now nginx.service

# mask: enable/start çağrılarını engelle (kalıcı devre dışı)
systemctl mask   nginx.service
systemctl unmask nginx.service

# çalışıyor mu? (exit 0 = evet, 3 = hayır — script'lerde kullan)
systemctl is-active  nginx.service
systemctl is-enabled nginx.service
systemctl is-failed  nginx.service

daemon-reload ne zaman gerekli?

Unit dosyasını disk üzerinde değiştirdiğinde, systemd'nin bellekteki tanımı hâlâ eskidir. daemon-reload tüm unit dosyalarını yeniden okur:

bash
# /etc/systemd/system/myapp.service dosyasını düzenledikten sonra
systemctl daemon-reload
systemctl restart myapp.service
DİKKAT

daemon-reload çalışan servisleri durdurmaz. Ama reload yapmadan restart ederseniz eski konfigürasyon çalışmaya devam eder. Unit dosyası her değiştikten sonra daemon-reload zorunlu.

systemctl edit — override.conf

Paket yöneticisinin unit dosyasını doğrudan düzenlemek yerine, drop-in override kullan — orijinal korunur, güncellemede kaybolmaz:

bash
# editörü açar → /etc/systemd/system/nginx.service.d/override.conf oluşturur
systemctl edit nginx.service

# yoksa oluştur (--force), tam dosya düzenle (--full)
systemctl edit --force --full myapp.service

# override.conf örneği — sadece değiştirmek istediğin parametreler
/etc/systemd/system/nginx.service.d/override.conf
[Service]
# Restart politikasını değiştir — orijinal paket dosyasına dokunma
Restart=always
RestartSec=3s
LimitNOFILE=65536

Tam lifecycle örneği

bash
# 1. unit dosyasını yaz
vim /etc/systemd/system/myapp.service

# 2. systemd'ye bildir
systemctl daemon-reload

# 3. başlat ve durumu kontrol et
systemctl start myapp.service
systemctl status myapp.service

# 4. boot'ta otomatik başlat
systemctl enable myapp.service

# 5. konfigürasyon değiştikten sonra
systemctl daemon-reload
systemctl reload-or-restart myapp.service

# 6. servis yanıt vermiyorsa zorla durdur
systemctl kill -s SIGKILL myapp.service

# 7. kalıcı olarak devre dışı bırak
systemctl disable --now myapp.service

04 journald ve log yönetimi

journald, binary ve indexed yapıda log depolar — metadata ile sorgulama, syslog'un yapamadığı şeydir.

journald'ın syslog'a göre avantajları

Özelliksyslogjournald
FormatDüz metinBinary + structured
MetadataYok / sınırlıPID, UID, GID, cgroup, unit adı, boot ID
IndexlemeYokVar — hızlı cursor tabanlı sorgulama
Tamper evidenceYokForward-secure sealing (opsiyonel)
Filtrelemegrepjournalctl field= sorguları
Rate limitingYokRateLimitBurst=, RateLimitInterval=

journalctl temel kullanım

bash
# servis loglarını takip et (tail -f gibi)
journalctl -u myapp.service -f

# son 50 satır
journalctl -u myapp.service -n 50

# son 1 saat içindeki loglar
journalctl -u myapp.service --since "1 hour ago"

# zaman aralığı
journalctl -u nginx.service --since "2026-04-12 00:00" --until "2026-04-12 06:00"

# sadece err ve üstü (emerg, alert, crit, err)
journalctl -u myapp.service -p err

# kernel mesajları (dmesg gibi)
journalctl -k

# context ile birlikte — hata öncesi/sonrası satırlar
journalctl -xe

# JSON formatında çıktı (log analizi pipeline için)
journalctl -u myapp.service -o json | jq '.'

# belirli bir boot'un logları
journalctl -b -1 # bir önceki boot
journalctl -b # bu boot

Log öncelikleri

0 — emergSistem kullanılamaz durumda.
1 — alertHemen aksiyon alınması gerekiyor.
2 — critKritik hatalar.
3 — errHatalar. -p err bu ve üstünü gösterir.
4 — warningUyarılar.
5 — noticeNormal ama önemli olaylar.
6 — infoBilgi mesajları.
7 — debugDebug mesajları.

Kalıcı log: journald.conf

Varsayılan olarak journald logları RAM'de (/run/log/journal/) tutar — reboot'ta kaybolur. Kalıcı yapmak için:

/etc/systemd/journald.conf
[Journal]
Storage=persistent          # /var/log/journal/ dizinine yaz
Compress=yes               # LZ4 sıkıştırma
SystemMaxUse=2G            # /var/log/journal/ maksimum boyut
SystemKeepFree=512M        # diskte bırakılacak minimum boş alan
MaxRetentionSec=1month     # 1 aydan eski logları sil
RateLimitBurst=1000        # aralık başına maksimum mesaj
RateLimitInterval=30s
bash
# journald.conf değişikliği sonrası yeniden başlat
systemctl restart systemd-journald

# log kullanım alanı
journalctl --disk-usage

# 1 GB üstünü temizle
journalctl --vacuum-size=1G

# 2 haftadan eski logları temizle
journalctl --vacuum-time=2weeks

05 Timer unit'leri

systemd timer, cron'un yapamadığı şeyleri yapar: kaçırılan görevleri yakalar, log tutar, bağımlılık yönetir.

cron vs systemd timer

Özellikcronsystemd timer
Logstdout/stderr belirli değiljournald'a otomatik
Kaçırılan görevÇalışmazPersistent=true ile boot'ta çalışır
BağımlılıkYokWants=, After= kullanılabilir
Kaynak limitiYokCPUQuota=, MemoryLimit= gibi cgroup
Randomize delayYokRandomizedDelaySec=
Durum takibiYoksystemctl status backup.timer

backup.timer + backup.service ikilisi

/etc/systemd/system/backup.service
[Unit]
Description=Günlük yedekleme
After=network.target

[Service]
Type=oneshot
User=backup
ExecStart=/usr/local/bin/backup.sh
StandardOutput=journal
StandardError=journal
/etc/systemd/system/backup.timer
[Unit]
Description=Günlük yedekleme timer'ı

[Timer]
OnCalendar=*-*-* 03:00:00   # her gün 03:00
RandomizedDelaySec=300      # 0-5 dakika rastgele gecikme (çakışma önleme)
Persistent=true             # kaçırılan çalışma boot'ta tetiklenir
Unit=backup.service

[Install]
WantedBy=timers.target
bash
# timer'ı etkinleştir ve başlat
systemctl enable --now backup.timer

# bir sonraki tetikleme zamanını gör
systemctl list-timers backup.timer

# tüm aktif timer'lar
systemctl list-timers --all

OnCalendar sözdizimi

dailyHer gün 00:00:00. *-*-* 00:00:00 ile aynı.
weeklyHer pazartesi 00:00:00.
monthlyHer ayın 1'i 00:00:00.
Mon 03:00Her pazartesi saat 03:00.
Mon..Fri 09:00Pazartesi'den cuma'ya 09:00.
*-*-1 04:00:00Her ayın 1'i saat 04:00.
*-*-* 00/6:00:00Her 6 saatte bir (00:00, 06:00, 12:00, 18:00).
*:0/15Her 15 dakikada bir.

Monotonic timer'lar

OnBootSec=5minBoot'tan 5 dakika sonra bir kez çalıştır.
OnUnitActiveSec=1hServis son aktif olduğundan 1 saat sonra yeniden çalıştır.
OnStartupSec=10ssystemd başladıktan 10 saniye sonra.
OnActiveSec=30minTimer aktif olduğundan 30 dakika sonra.
bash — list-timers çıktısı
# systemctl list-timers çıktısı örneği:
# NEXT                         LEFT          LAST                         PASSED       UNIT
# Sun 2026-04-13 03:04:22 UTC  23h left      Sat 2026-04-12 03:01:15 UTC  58min ago    backup.timer
# Sun 2026-04-13 00:00:00 UTC  20h left      Sat 2026-04-12 00:00:00 UTC  4h ago       logrotate.timer
# Sun 2026-04-13 00:00:00 UTC  20h left      Sat 2026-04-12 00:00:00 UTC  4h ago       man-db.timer
NOT

Persistent=true çok kritik: sistem kapalıyken geçen bir çalışma zamanı varsa, boot'tan sonra hemen tetiklenir. Yedekleme ve güncelleme görevlerinde her zaman açık olmalı.

06 User units

Root yetkisi gerektirmeyen servisler kullanıcı kapsamında çalıştırılabilir — geliştirici araçları, agent'lar, kişisel daemon'lar için idealdir.

User unit dosyası

~/.config/systemd/user/myapp.service
[Unit]
Description=Kişisel uygulama

[Service]
Type=simple
ExecStart=%h/bin/myapp   # %h = $HOME
Restart=on-failure
Environment="PORT=9090"

[Install]
WantedBy=default.target
bash
# kullanıcı systemd instance'ı için daemon-reload
systemctl --user daemon-reload

# başlat / durdur / durum
systemctl --user start   myapp.service
systemctl --user stop    myapp.service
systemctl --user status  myapp.service

# boot'ta başlat
systemctl --user enable myapp.service

# kullanıcı logları
journalctl --user -u myapp.service -f

loginctl enable-linger

Varsayılan olarak kullanıcı systemd instance'ı, kullanıcı login olduğunda başlar ve logout'ta durur. enable-linger ile kullanıcı login olmadan da servis çalışmaya devam eder:

bash
# emirhan kullanıcısı için linger etkinleştir (root gerektirir)
loginctl enable-linger emirhan

# linger durumunu kontrol et
loginctl show-user emirhan | grep Linger

# devre dışı bırak
loginctl disable-linger emirhan

Önemli ortam değişkenleri

XDG_RUNTIME_DIRKullanıcıya özel geçici dizin: /run/user/1000/. systemd user socket'leri buradadır. Logout'ta temizlenir.
DBUS_SESSION_BUS_ADDRESSKullanıcı session bus adresi. unix:path=/run/user/1000/bus formatında. D-Bus kullanan uygulamalar için gerekli.
SYSTEMD_LOG_LEVELsystemd log seviyesi: debug, info, notice, warning, err. Sorun giderme için debug kullan.
DİKKAT

User unit'leri sistem unit'lerinden önce çalışır demek değildir. network.target gibi sistem target'larına After= ile bağımlılık tanımlamak user scope'ta çalışmaz. User unit'lerin network bağımlılığı için After=network-online.target yerine bağlantıyı servisin kendi içinde yönet.

07 Dependency management

Wants vs Requires, After vs Before — yanlış anlaşıldığında servisler yanlış sırada veya hiç başlamaz.

Wants vs Requires

Wants=BA, B'nin başlamasını ister. B başlamazsa veya başarısız olursa A yine de çalışır. Yumuşak bağımlılık.
Requires=BA, B olmadan çalışamaz. B başlamazsa veya durumda başarısız olursa A da durdurulur. Güçlü bağımlılık.
BindsTo=BRequires'dan daha güçlü: B herhangi bir nedenle dursa, A da durur (çalışırken bile).
PartOf=BA, B'nin parçasıdır. B restart/stop edilince A da restart/stop edilir. Ama A bağımsız başlayabilir.
Conflicts=BA çalışırken B çalışamaz (karşılıklı dışlama). shutdown.target servislerle çakışır.

After ve Before — sıralama

DİKKAT — yaygın yanlış anlama

After= ve Before= sadece başlama sırası belirler, bağımlılık oluşturmaz. After=network.target yazmak, network.target'ın başlatılacağını garanti etmez — sadece "eğer ikisi de başlatılacaksa, network önce başlasın" demektir. Bağımlılık için Wants= veya Requires= ile birlikte kullan.

/etc/systemd/system/myapp.service
[Unit]
Description=Uygulama servisi
# postgresql başlamadan çalışma, postgresql dursa dur
Requires=postgresql.service
After=postgresql.service

# redis opsiyonel — yoksa da çalış ama varsa sonra başla
Wants=redis.service
After=redis.service

[Service]
Type=notify
ExecStart=/opt/myapp/bin/server

[Install]
WantedBy=multi-user.target

Target zinciri

  sysinit.target          ── temel sistem bileşenleri: udev, journal, cryptsetup
       │
  basic.target            ── sockets, timers, paths hazır
       │
  multi-user.target       ── ağ, servisler — text mode login ekranı
       │
  graphical.target        ── X11/Wayland display manager
    
bash
# servisin bağımlılık ağacını göster
systemctl list-dependencies myapp.service

# tersine: hangi unit'ler myapp'e bağımlı?
systemctl list-dependencies --reverse myapp.service

# tüm bağımlılıkları (özyinelemeli) göster
systemctl list-dependencies --all myapp.service

# hangi target'ta çalışıyor?
systemctl get-default

08 Sandboxing ve güvenlik

systemd, unit dosyasına eklenen direktiflerle servisler için Linux security primitive'leri (namespace, capability, seccomp) etkinleştirir.

Temel sandboxing direktifleri

ProtectSystem=strict/usr, /boot, /etc read-only mount eder. Servis bu dizinlere yazamaz. strict en kapsamlı seçenek; full /usr ve /boot'u read-only yapar; true sadece /usr'u.
ProtectHome=true/home, /root, /run/user inaccessible. Servisin kullanıcı home dizinlerine erişimini engeller.
PrivateTmp=trueServise özel /tmp ve /var/tmp namespace'i oluşturur. Diğer servisler bu /tmp'yi göremez. Tmp race condition saldırılarını önler.
NoNewPrivileges=truesetuid binary'ler ve file capability'ler çalışamaz. Process'in privilege escalation yapması engellenir. Her servis için açık olmalı.
CapabilityBoundingSet=Servise verilecek capability listesi. Örnek: CAP_NET_BIND_SERVICE — 1024 altı port'a bind için. Tüm capability'leri kaldırmak için: CapabilityBoundingSet= (boş).
PrivateNetwork=trueAğ namespace'i izole eder — servis ağa erişemez. Sadece yerel işlem yapan servisler için.
ReadOnlyPaths=Belirli path'leri read-only yap: ReadOnlyPaths=/etc/myapp
ReadWritePaths=ProtectSystem=strict ile birlikte kullanılır — sadece belirtilen path'lere yazma izni ver: ReadWritePaths=/var/lib/myapp
ProtectKernelTunables=true/proc/sys, /sys read-only. Kernel parametrelerini değiştirmeyi engeller.
RestrictAddressFamilies=AF_INET AF_INET6Sadece belirtilen socket ailelerine izin ver. Unix socket kullanmıyorsa AF_UNIX'i çıkar.
SystemCallFilter=@system-serviceseccomp ile izin verilen syscall'ları filtrele. @system-service standart daemon'lar için güvenli bir preset'tir.

Güvenli bir web servisi örneği

/etc/systemd/system/secure-web.service
[Unit]
Description=Güvenli web servisi
After=network.target

[Service]
Type=simple
User=www-data
Group=www-data
ExecStart=/opt/web/server

# sandboxing
ProtectSystem=strict
ReadWritePaths=/var/lib/web /var/log/web
ProtectHome=true
PrivateTmp=true
NoNewPrivileges=true
CapabilityBoundingSet=CAP_NET_BIND_SERVICE
AmbientCapabilities=CAP_NET_BIND_SERVICE
ProtectKernelTunables=true
RestrictAddressFamilies=AF_INET AF_INET6
SystemCallFilter=@system-service

[Install]
WantedBy=multi-user.target

systemd-analyze security

bash
systemd-analyze security secure-web.service
# Örnek çıktı:
#   NAME                                                        DESCRIPTION                              EXPOSURE
#   ✓ CapabilityBoundingSet=~CAP_SYS_ADMIN                    Service has no administrator privileges
#   ✓ PrivateTmp=yes                                          Service has no access to other software's temporary files
#   ✓ NoNewPrivileges=yes                                     Service processes cannot acquire new privileges
#   ✗ MemoryDenyWriteExecute=                                 Service may create writable executable memory mappings   0.1
#   ✗ RestrictRealtime=                                       Service may acquire realtime scheduling                  0.1
#   → Overall exposure level for secure-web.service: 2.1 OK
DİKKAT

Sandboxing direktiflerini production'da bir anda ekleme. Her direktifi test ortamında tek tek ekle, servisin hâlâ çalıştığını doğrula. Özellikle ProtectSystem=strict + ReadWritePaths= kombinasyonu, atladığın bir path varsa servisi kırabilir. AuditType=yes veya strace ile erişilen path'leri önceden tespit et.

09 Debugging ve sorun giderme

Servis başlamıyorsa, doğru araçlarla sebebi dakikalar içinde bulunur.

İlk adım: systemctl status

bash
systemctl status myapp.service
# Örnek çıktı:
# ● myapp.service - Uygulama servisi
#      Loaded: loaded (/etc/systemd/system/myapp.service; enabled; vendor preset: enabled)
#      Active: failed (Result: exit-code) since Sat 2026-04-12 03:14:22 UTC; 5min ago
#     Process: 12483 ExecStart=/opt/myapp/bin/server (code=exited, status=1/FAILURE)
#    Main PID: 12483 (code=exited, status=1/FAILURE)
#         CPU: 102ms
#
# Apr 12 03:14:22 host myapp[12483]: FATAL: cannot open config file /etc/myapp/config.yaml

journalctl -xe — extended context

bash
# hata öncesi/sonrası bağlam ile genişletilmiş log
journalctl -xe -u myapp.service

# bu boot'ta sadece bu servise ait loglar
journalctl -b -u myapp.service

# tüm systemd mesajları (PID 1)
journalctl _PID=1

systemd-analyze — boot analizi

bash
# boot süresi özeti
systemd-analyze time

# en yavaş servisler
systemd-analyze blame

# kritik path — boot süresini belirleyen servis zinciri
systemd-analyze critical-chain

# belirli bir servisin critical chain'i
systemd-analyze critical-chain myapp.service

# tüm boot grafiğini SVG olarak dışa aktar
systemd-analyze plot > boot.svg

FAILED durumdan kurtarma

bash
# failed bayrağını temizle (log'u silmez)
systemctl reset-failed myapp.service

# hepsini temizle
systemctl reset-failed

# başarısız servis listesi
systemctl --failed

Sık karşılaşılan hatalar

HataNedenÇözüm
status=203/EXECExecStart yolu bulunamadı veya çalıştırılabilir değilTam path doğrula, chmod +x kontrol et
status=217/USERUser= alanında belirtilen kullanıcı yokuseradd ile kullanıcı oluştur
status=218/CAPABILITIESCapability talep edilemiyorAmbientCapabilities= veya User=root
start request repeated too quicklyServis çok hızlı yeniden başlıyor — rate limitStartLimitBurst= ve StartLimitIntervalSec= ayarla
Failed to connect to busUser unit için D-Bus erişimi yokloginctl enable-linger veya session içinde çalıştır
Permission deniedProtectSystem veya ReadOnlyPaths kısıtlamasıReadWritePaths= ile gerekli path'i ekle
Supervising process ... which is not our childType=forking ama PIDFile= yazılmamışPIDFile= ekle veya Type=simple kullan

Unit doğrulama

bash
# unit dosyasını sözdizimi açısından kontrol et
systemd-analyze verify /etc/systemd/system/myapp.service

# unit'in tam içeriğini (override'larla birlikte) göster
systemctl cat myapp.service

# unit property'lerini göster
systemctl show myapp.service

# belirli bir property
systemctl show myapp.service -p Restart

Bu bölümde öğrendikleriniz

  • systemctl status, ilk bakışta exit code ve son log satırlarını gösterir
  • journalctl -xe ile hata bağlamı genişletilir
  • systemd-analyze critical-chain, boot süresini belirleyen zinciri gösterir
  • reset-failed ile failed bayrağı temizlenip servis yeniden denenebilir
  • systemd-analyze verify, deploy öncesi unit dosyalarını kontrol eder