Linux VPS üzerinde systemd servisi nasıl oluşturulur?
Bir programı systemd servisi olarak yapılandırarak sunucu açılışında otomatik başlatılmasını ve çökme durumunda yeniden çalışmasını sağlayın. Unit dosyası yazımı ve log takibi.
systemd servisi nedir ve neden gereklidir
systemd servisi, sunucunuza bir programı nasıl çalıştıracağını söyleyen küçük bir metin dosyasıdır: programı açılışta başlatır, çökerse yeniden başlatır ve çıktılarını sistem günlüğüne gönderir. Tüm işlevi budur. Bir SSH oturumunda manuel olarak başlattığınız bir program, oturumu kapattığınız veya sunucu yeniden başladığı anda sonlanır. systemd servisi ile sarmalanmış bir program ise çalışmaya devam eder; çünkü programın sahibi kabuğunuz değil, sunucunun kendisidir.
systemd; Ubuntu, Debian, Fedora ve çoğu modern Linux sunucusunda kullanılan init sistemidir. Başlatılan ilk süreçtir ve diğer her şeyi denetler. Bu durum her zaman böyle değildi; systemd'nin kendisinden önceki init betiklerinin yerini nasıl aldığı konusu, bir unit dosyasının ne işe yaradığını anladıktan sonra okunmaya değerdir. Bir servis dosyası yazdığınızda, programınızı bu denetleyiciye teslim etmiş olursunuz. Bu kılavuz, çalışan en küçük birimi, her unit dosyasının sahip olduğu üç bölümü, servisi nasıl etkinleştireceğinizi ve günlüklerini nasıl okuyacağınızı, bir timer ile zamanlanmış görevleri nasıl çalıştıracağınızı ve en az yetkiyle çalışması için servisi nasıl kısıtlayacağınızı göstermektedir.
Çalışan en küçük servis
Bir servis dosyası /etc/systemd/system/ dizininde bulunur, .service ile biter ve yalnızca birkaç satıra ihtiyaç duyar. /usr/local/bin/myapp konumundaki bir program için bir tane oluşturun:
sudo nano /etc/systemd/system/myapp.service[Unit]
Description=My application
[Service]
ExecStart=/usr/local/bin/myapp
[Install]
WantedBy=multi-user.targetBu, eksiksiz ve çalışan bir birimdir. ExecStart çalıştırılacak komuttur. WantedBy=multi-user.target, sunucu normal çok kullanıcılı çalışma moduna ulaştığında bu servisi başlat anlamına gelir; servisin açılışta çalışmasını sağlayan budur. Geriye kalan her şey iyileştirmedir.
Üç bölüm ve her birinin amacı
Her unit dosyası köşeli parantez içindeki bölümlere ayrılır. Bir servis üç bölüm kullanır.
[Unit] servisi ve ilişkilerini tanımlar. En çok kullanacağınız iki satır şunlardır:
[Unit]
Description=My application
After=network-online.target
Wants=network-online.targetDescription, systemctl status içinde gördüğünüz insan tarafından okunabilir etikettir. After=network-online.target, sistemd'ye programınızı ağ hazır olana kadar başlatmamasını söyler; bu, bir portu dinleyen veya dışa doğru bağlantı kuran her şey için önemlidir.
[Service] programın nasıl çalışacağını belirler. Ayarlarınızın çoğu buraya girilir:
[Service]
ExecStart=/usr/local/bin/myapp --config /etc/myapp/config.toml
WorkingDirectory=/opt/myapp
User=myapp
Restart=on-failure
RestartSec=5
Environment=LOG_LEVEL=infoUser=myapp, programı root yerine yetkisiz bir hesapla çalıştırır; bu, güvenlik açısından en önemli satırdır. Burada bir Type= satırı yoktur, bu yüzden sistemd simple değerine geri döner ve ExecStart sürecinin ön planda kaldığını varsayar; arka plana çatallanan (fork) bir program, başlatılma şekline uygun Type= değerine ihtiyaç duyar, aksi takdirde gerçek daemon çoktan kapanmış olsa bile unit aktif olarak raporlanır. Restart=on-failure ve RestartSec=5 kendi bölümlerini aşağıda alır, çünkü çoğu insanın bir servis yazma nedeni bunlardır.
[Install], servisi etkinleştirdiğinizde ne olacağını belirler:
[Install]
WantedBy=multi-user.targetWantedBy=multi-user.target, systemctl enable komutunu çalıştırdığınızda servisi önyükleme sürecine bağlayan kısımdır. Bir [Install] bölümü olmadan servis elle başlatılabilir ancak yeniden başlatma sonrasında kendiliğinden ayağa kalkmaz.
Servisi etkinleştirme ve izleme
Herhangi bir unit dosyasını yazdıktan veya düzenledikten sonra, değişikliklerin algılanması için systemd yapılandırmasını yeniden yükleyin, ardından servisi tek adımda etkinleştirip başlatın:
sudo systemctl daemon-reload
sudo systemctl enable --now myapp.servicedaemon-reload, kullanıcıların genellikle unuttuğu bir adımdır: systemd unit dosyalarını önbelleğe alır, bu nedenle yeniden yükleme yapılmadığı sürece düzenlemeler etkisiz kalır. enable --now komutu hem servisi açılışta çalışacak şekilde etkinleştirir hem de hemen başlatır. Durumu kontrol edin:
sudo systemctl status myapp.service* myapp.service - My application
Loaded: loaded (/etc/systemd/system/myapp.service; enabled)
Active: active (running) since Wed 2026-07-15 22:40:11 UTC; 3s ago
Main PID: 4123 (myapp)Active: active (running) ve enabled görmeniz gereken çıktılardır. Programın çıktısını okumak için journal üzerinden yalnızca bu birime ait kayıtları sorgulayın:
sudo journalctl -u myapp.service -f-f bayrağı, tail -f komutunda olduğu gibi yeni satırları geldikçe takip eder. Programınızın standart çıktıya (stdout) veya standart hata akışına (stderr) yazdığı her şey, herhangi bir ek günlük kaydı yapılandırmasına gerek kalmadan burada görünür.
Hata durumunda yeniden başlatma, buraya geliş nedeniniz
Bir servisin temel avantajı, programınız kapandığında systemd'nin onu otomatik olarak yeniden başlatmasıdır. Bunu iki satır sağlar:
[Service]
Restart=on-failure
RestartSec=5Restart=on-failure, program sıfır olmayan bir kodla çıktığında veya SIGKILL ya da SIGSEGV gibi bir çökme sinyaliyle kapandığında programı yeniden başlatır. Temiz bir çıkış veya SIGTERM, SIGINT, SIGHUP ya da SIGPIPE sinyalleriyle durdurulma durumu, yeniden başlatmayı tetiklemez. RestartSec=5, denemeler arasında beş saniye bekler; böylece anında çöken bir programın sürekli döngüye girmesi engellenir. Bunu, süreci sonlandırıp systemd'nin onu geri getirmesini izleyerek doğrulayın. SIGKILL kullanın: varsayılan SIGTERM temiz bir durdurma olarak kabul edilir, bu nedenle on-failure servisi yeniden başlatmayacaktır:
sudo systemctl kill -s SIGKILL myapp.service
sudo systemctl status myapp.serviceBeş saniye içinde durum, yeni bir Main PID ve tekrar active (running) değerini gösterir. Özelliğin tamamı budur ve bir servisin, programı tmux veya screen içinde çalışır durumda bırakmaktan daha üstün olmasının nedeni de budur.
Servisi yetkisiz bir kullanıcı olarak çalıştırın ve sıkılaştırın
root yetkileriyle çalışan bir servis, programın istismar edilmesi durumunda sunucunuz üzerinde her türlü işlemi yapabilir. Servisi kendi kullanıcısı altında çalıştırın ve systemd'ye onu sınırlandıracak birkaç direktif verin. Öncelikle giriş yetkisi olmayan ve ev dizini bulunmayan bir sistem hesabı oluşturun:
sudo useradd --system --no-create-home --shell /usr/sbin/nologin myappArdından User=myapp ayarını yapın ve [Service] dosyasına sıkılaştırma satırlarını ekleyin:
[Service]
User=myapp
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ProtectHome=trueHer bir satır, programın ihtiyaç duymadığı bir özelliği kaldırır. NoNewPrivileges=true, setuid binary dosyaları aracılığıyla bile olsa sürecin yeni yetkiler kazanmasını engeller. PrivateTmp=true, sürece başka hiçbir sürecin göremediği özel bir /tmp alanı sağlar. ProtectSystem=strict, ReadWritePaths= ile belirttiğiniz birkaç yol haricinde tüm dosya sistemini salt okunur hale getirir. ProtectHome=true, /home dizinini süreçten tamamen gizler. Bu, bir servisi güvenlik duvarı arkasına almakla aynı en az yetki prensibidir: servise yalnızca ihtiyaç duyduğu kaynakları verin. VPS üzerinde IPv6 güvenlik duvarı boşluğunu kapatma rehberini okuduysanız, bu işlemin aynı mantığın sunucu içi karşılığı olduğunu göreceksiniz. İnternete açık bir servis için bu sıkılaştırma yöntemini SSH önünde Fail2ban ve varsayılan olarak reddeden bir güvenlik duvarı ile birlikte kullanın.
Bunların hepsini elle yazıp bir direktifi yanlış hatırlama riski almak yerine, eksiksiz ve sıkılaştırılmış bir unit dosyası oluşturun ve kopyalayın:
Zamanlayıcılar: modern cron
Bir systemd zamanlayıcısı, bir servisi belirli bir takvime göre çalıştırır ve cron işlerinin modern karşılığıdır. Bir zamanlayıcı iki dosyadan oluşur: işi yapan bir .service ve zamanlamayı belirten bir .timer. Her gün sabah 03:00'te bir yedekleme almak istediğinizi varsayalım. Servis, görevi bir kez yapar ve sonlanır:
# /etc/systemd/system/backup.service
[Unit]
Description=Nightly backup
[Service]
Type=oneshot
ExecStart=/usr/local/bin/backup.shType=oneshot, systemd'ye programın sürekli çalışmak yerine bir kez çalışıp bittiğini bildirir. Zamanlayıcı ise bunu şu şekilde planlar:
# /etc/systemd/system/backup.timer
[Unit]
Description=Run the nightly backup
[Timer]
OnCalendar=*-*-* 03:00:00
Persistent=true
[Install]
WantedBy=timers.targetOnCalendar=*-*-* 03:00:00, her gün sabah 03:00 anlamına gelir. Herhangi bir takvim ifadesini, ayrıştırıldığını doğrulayan ve bir sonraki çalışma zamanlarını listeleyen systemd-analyze calendar "*-*-* 03:00:00" ile test edebilirsiniz. Persistent=true, sunucu saat 03:00'te kapalıysa, açıldığı anda kaçırılan işi hemen çalıştırır; cron bunu yapamaz. Bir zamanlayıcının multi-user.target ile değil, timers.target ile etkinleştirildiğine dikkat edin. Servisi değil, zamanlayıcıyı etkinleştirin:
sudo systemctl daemon-reload
sudo systemctl enable --now backup.timer
systemctl list-timerslist-timers, her zamanlayıcıyı bir sonraki ve son çalışma zamanlarıyla birlikte gösterir; böylece işinizin ne zaman tetikleneceğini bir bakışta görebilirsiniz. Yukarıdaki oluşturucu, zamanlayıcı modunu açtığınızda sizin için eşleşen .service ve .timer dosyalarını hazırlar. Bir cron satırına kıyasla zamanlayıcı, size journal içinde gerçek günlük kayıtları, herhangi bir servisle aynı güvenlik yönergelerini ve Persistent=true tarafından sağlanan kaçırılan işi yakalama özelliğini sunar. Basit işler için cron hala yeterlidir; ancak iş önem kazandığında zamanlayıcı daha iyi bir araçtır.
FAQ
systemd servisi ile cron işi arasındaki fark nedir?
Bir servis, uzun süre çalışan bir programı ayakta tutar: sistem açılışında başlar, hata durumunda yeniden başlatılır ve günlük kayıtlarını journal içine yazar. Bir cron işi ise belirli bir zamanlamaya göre kısa bir komut çalıştırır ve ardından sonlanır. Zamanlama özelliğinin yanı sıra journal günlüklerine, güvenlik sıkılaştırmasına ve kaçırılan işlerin telafisine ihtiyaç duyduğunuzda, çoğu sunucu görevinde cron yerine geçen ve bir .timer zamanlamasını bir oneshot servisi ile eşleştiren systemd timer yapısını kullanın.
systemd servis dosyamı nereye koymalıyım?
Kendi unit dosyalarınızı /etc/systemd/system/ dizinine, .service ile biten bir isimle yerleştirin. Bu dizin yönetici tarafından eklenen unit dosyaları içindir ve /lib/systemd/system/ dizinindeki paketler tarafından sağlanan unit dosyalarına göre önceliklidir. Burada bir dosya oluşturduktan veya düzenledikten sonra, systemd'nin değişiklikleri algılaması için sudo systemctl daemon-reload komutunu çalıştırın.
Bir servis çöktüğünde nasıl yeniden başlatılmasını sağlarım?
[Service] bölümüne Restart=on-failure ve RestartSec=5 satırlarını ekleyin, ardından sudo systemctl daemon-reload komutunu çalıştırıp servisi yeniden başlatın. Program sıfır olmayan bir kodla sonlandığında veya bir çökme sinyali nedeniyle kapandığında systemd programı yeniden başlatır ve denemeler arasında beş saniye bekler. Bunu sudo systemctl kill -s SIGKILL myapp.service ile test edin; varsayılan sinyal olan SIGTERM temiz bir durdurma olarak kabul edilir ve on-failure tetiklemez. Birkaç saniye içinde systemctl status üzerinde yeni bir PID göründüğünü gözlemleyin.
Bir systemd servisini root olmayan bir kullanıcıyla nasıl çalıştırırım?
sudo useradd --system --no-create-home --shell /usr/sbin/nologin myapp ile bir sistem hesabı oluşturun, ardından [Service] bölümüne User=myapp satırını ekleyin. Sürecin yalnızca ihtiyaç duyduğu kadar erişimle çalışması için NoNewPrivileges=true, PrivateTmp=true ve ProtectSystem=strict ayarlarını ekleyin. Ayrıcalıksız bir kullanıcı ile çalıştırmak, bir servisin güvenliği için yapabileceğiniz en önemli değişikliktir.
Servisim neden başlamadı?
Özet için systemctl status myapp.service, tam çıktı için journalctl -u myapp.service komutunu çalıştırın. En yaygın nedenler; ExecStart içinde yanlış bir yol, eksik bir WorkingDirectory, User= dosya okuyamadığı için oluşan bir izin hatası veya düzenleme sonrası unutulan sudo systemctl daemon-reload komutudur. Journal, genellikle sorunu doğrudan belirten programın kendi hata mesajını gösterir.