VPS üzerinde program çalıştırma: systemd servisi
Programların sunucu açılışında otomatik başlaması, çökünce yeniden başlatılması ve journal logları üzerinden takibi için systemd servisi kurulum rehberi.
systemd servisi nedir ve neden kullanılır
systemd servisi, sunucunun bir programı nasıl çalıştıracağını belirleyen küçük bir metin dosyasıdır: programı açılışta başlatır, çökerse yeniden başlatır ve çıktısını sistem günlüğüne gönderir. Görevi sadece budur. Bir SSH oturumunda manuel olarak başlatılan bir program, oturum kapatıldığında veya sunucu yeniden başlatıldığında sona erer. Bir systemd servisi içine alınmış program ise çalışmaya devam eder; çünkü program artık kabuk (shell) yerine sunucunun kendisi tarafından yönetilir.
systemd; Ubuntu, Debian, Fedora ve çoğu modern Linux sunucusunda kullanılan init sistemidir. Başlatılan ilk süreçtir ve diğer tüm süreçleri denetler. Bir servis dosyası yazdığınızda, programınızı bu denetleyiciye teslim etmiş olursunuz. Bu kılavuz; çalışan en küçük birimi, her ünitenin sahip olduğu üç bölümü, servisin nasıl etkinleştirileceğini ve günlüklerinin nasıl okunacağını, bir zamanlayıcı (timer) ile nasıl planlanacağını ve en düşük yetkiyle çalışması için nasıl kısıtlanacağını göstermektedir.
Çalışan en küçük servis
Bir servis dosyası /etc/systemd/system/ dizininde bulunur, .service uzantısına sahiptir ve sadece birkaç satır gerektirir. /usr/local/bin/myapp konumundaki bir program için bir servis dosyası oluşturun:
sudo nano /etc/systemd/system/myapp.service[Unit]
Description=My application
[Service]
ExecStart=/usr/local/bin/myapp
[Install]
WantedBy=multi-user.targetBu, çalışan tam bir ünitedir. ExecStart çalıştırılacak komuttur. WantedBy=multi-user.target, sunucu normal çok kullanıcılı çalışma moduna ulaştığında servisin başlatılmasını sağlar; bu da servisin açılışta çalışmasını sağlar. Diğer tüm ayarlar iyileştirme amaçlıdır.
Üç bölüm ve her birinin kullanım amacı
Her unit dosyası köşeli parantez içindeki bölümlere ayrılmıştır. Bir servis üç bölüm kullanır.
[Unit] servisi ve servisler arası ilişkileri tanımlar. En sık kullanılacak iki satır şunlardır:
[Unit]
Description=My application
After=network-online.target
Wants=network-online.targetDescription, systemctl status içerisinde görünen kullanıcı etiketidir. After=network-online.target, ağ bağlantısı kurulana kadar programın başlatılmamasını sağlar; bu durum, bir portu dinleyen veya dış bağlantı kuran tüm servisler için önemlidir.
[Service], programın nasıl çalıştırılacağını belirler. Ayarların çoğu burada yer alır:
[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ın root yerine yetkisiz bir kullanıcı ile çalıştırılmasını sağlar; bu, güvenlik için en kritik satırdır. Restart=on-failure ve RestartSec=5, servis oluşturma nedenlerinin çoğu olduğu için aşağıda kendi bölümlerine sahiptir.
[Install], servis etkinleştirildiğinde ne olacağını belirler:
[Install]
WantedBy=multi-user.targetWantedBy=multi-user.target, systemctl enable çalıştırıldığında servisin önyükleme (boot) sürecine dahil edilmesini sağlar. Bir [Install] bölümü olmadan servis manuel olarak başlatılabilir ancak yeniden başlatma sonrası otomatik olarak devreye girmez.
Etkinleştirin ve izleyin
Herhangi bir unit dosyasını yazdıktan veya düzenledikten sonra, değişikliğin okunması için systemd'yi reload edin. Ardından servisi tek adımda enable edin ve start edin:
sudo systemctl daemon-reload
sudo systemctl enable --now myapp.servicedaemon-reload genellikle unutulan adımdır: systemd unit dosyalarını önbelleğe alır, bu nedenle reload işlemi yapılana kadar düzenlemeler etkili olmaz. enable --now servisi hem açılış için enable eder hem de hemen start eder. 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 istenen çıktılardır. Programın çıktısını okumak için journal üzerinden sadece bu unit için sorgulama yapın:
sudo journalctl -u myapp.service -f-f, tail -f gibi yeni satırları geldikleri anda takip eder. Herhangi bir log yapılandırmasına gerek kalmadan, programınızın standard output veya standard error üzerine yazdığı her şey buraya düşer.
Hata durumunda yeniden başlatma, buradaki temel nedeniniz
Bir servisin temel avantajı, programınız çöktüğünde systemd'nin onu yeniden başlatmasıdır. Bu işlem iki satırla gerçekleştirilir:
[Service]
Restart=on-failure
RestartSec=5Restart=on-failure, program sıfırdan farklı bir kodla kapandığında veya SIGKILL veya SIGSEGV gibi bir çökme sinyali aldığında programı yeniden başlatır. Temiz bir çıkış veya SIGTERM, SIGINT, SIGHUP veya SIGPIPE ile durdurma işlemi bunu tetiklemez. RestartSec=5, denemeler arasında beş saniye bekler; böylece anında çöken bir programın sonsuz döngüye girmesi engellenir. Süreci sonlandırarak ve systemd'nin onu tekrar ayağa kaldırdığını izleyerek bunu doğrulayabilirsiniz. SIGKILL kullanın: varsayılan SIGTERM temiz bir durdurma sayılır, 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 active (running) gösterir. Özelliğin tamamı budur; bir servisin, bir programı tmux veya screen içinde çalıştırmaktan daha üstün olmasının nedeni budur.
Servisi yetkisiz bir kullanıcı olarak çalıştırın ve sıkılaştırın
Programın istismar edilmesi durumunda, root olarak çalışan bir servis sunucuda her türlü işlemi gerçekleştirebilir. Servisi kendi kullanıcısı ile çalıştırın ve systemd'ye onu sınırlandıracak direktifler verin. İlk olarak, oturum açma yetkisi ve home dizini olmayan 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 satır, programın ihtiyaç duymadığı bir özelliği devre dışı bırakır. NoNewPrivileges=true, sürecin bir setuid binary aracılığıyla bile yeni yetkiler kazanmasını engeller. PrivateTmp=true, sürece başka hiçbir sürecin göremeyeceği özel bir /tmp sağlar. ProtectSystem=strict, ReadWritePaths= ile belirtilen birkaç yol hariç tüm dosya sistemini salt okunur yapar. ProtectHome=true, /home öğesini tamamen gizler. Bu yaklaşım, bir servisi firewall arkasına koymakla aynı en az yetki prensibine dayanır: servise sadece ihtiyacı olan yetkileri verin. Eğer VPS üzerinde IPv6 firewall boşluklarını kapatma kılavuzunu okuduysanız, bu yöntem aynı mantığın ana makine üzerindeki kısmıdır. İnternete açık bir servis için bu sıkılaştırmayı, SSH önünde Fail2ban kullanımı ve varsayılan olarak engelleme (default-deny) modunda çalışan bir firewall ile birlikte kullanın.
Tüm bunları elle yazıp bir direktifi yanlış hatırlamak yerine, tam ve sıkılaştırılmış bir unit dosyası oluşturup kopyalayın:
Timers: modern cron
Bir systemd timer, bir servisi belirli bir program dahilinde çalıştırır; cron job yapısının modern alternatifidir. Bir timer iki dosyadan oluşur: işi yapan bir .service ve ne zaman çalışacağını belirten bir .timer. Her gün saat 03:00'te yedekleme yapmak istediğinizi varsayalım. Servis görevi bir kez yerine getirir ve kapanır:
# /etc/systemd/system/backup.service
[Unit]
Description=Nightly backup
[Service]
Type=oneshot
ExecStart=/usr/local/bin/backup.shType=oneshot, programın bellekte kalmak yerine çalışıp bittiğini systemd'ye bildirir. Timer ise çalışmasını 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 saat 03:00 anlamına gelir. Herhangi bir takvim ifadesini systemd-analyze calendar "*-*-* 03:00:00" ile test edebilirsiniz; bu komut ifadenin ayrıştırıldığını ve bir sonraki çalışma zamanlarını doğrular. Eğer sunucu saat 03:00'te kapalıysa, Persistent=true kaçırılan işi sunucu açılır açılmaz çalıştırır; cron bunu yapamaz. Bir timer'ın multi-user.target yerine timers.target aracılığıyla etkinleştirildiğine dikkat edin. Servisi değil, timer'ı etkinleştirin:
sudo systemctl daemon-reload
sudo systemctl enable --now backup.timer
systemctl list-timerslist-timers, her timer'ın bir sonraki ve son çalışma zamanını gösterir; böylece işin ne zaman çalışacağını hızlıca görebilirsiniz. Yukarıdaki generator, timer modu açıldığında sizin için eşleşen .service ve .timer dosyalarını oluşturur. Cron satırına kıyasla bir timer; journal üzerinde gerçek loglar, herhangi bir servisteki gibi sıkılaştırma direktifleri ve Persistent=true tarafından sağlanan kaçırılan çalıştırmaları yakalama özelliği sunar. Basit işler için cron hala uygundur; ancak iş kritik hale geldiğinde timer daha iyi bir araçtır.
FAQ
systemd service ile cron job arasındaki fark nedir?
Bir service, uzun süreli çalışan bir programı aktif tutar: sistem açılışında başlar, hata durumunda yeniden başlatılır ve journal'a log yazar. Cron job ise belirli bir takvime göre kısa bir komut çalıştırır ve ardından kapanır. Takvimleme özelliğine ek olarak journal logları, hardening ve kaçırılan çalıştırmaların telafisi isteniyorsa systemd timer kullanılmalıdır. Systemd timer, bir .timer takvimi ile bir oneshot service'i eşleştirir ve çoğu sunucu görevi için cron'un yerini alır.
systemd service dosyamı nereye koymalıyım?
Kendi unit dosyalarınızı, sonu .service ile biten bir isimle /etc/systemd/system/ dizinine koyun. Bu dizin, yöneticinin eklediği unit'ler içindir ve /lib/systemd/system/ içindeki paketlerle gelen unit'lere göre önceliklidir. Bir dosyayı oluşturduktan veya düzenledikten sonra, systemd'nin değişikliği algılaması için sudo systemctl daemon-reload komutunu çalıştırın.
Bir service çöktüğünde yeniden başlatılması nasıl sağlanır?
[Service] bölümüne Restart=on-failure ve RestartSec=5 ekleyin, ardından sudo systemctl daemon-reload komutunu çalıştırın ve service'i yeniden başlatın. Program sıfırdan farklı bir kodla kapandığında veya bir çökme sinyali aldığında systemd programı yeniden başlatır; denemeler arasında beş saniye bekler. sudo systemctl kill -s SIGKILL myapp.service ile test edin; varsayılan sinyal olan SIGTERM, temiz bir durdurma sayılır ve on-failure tetiklenmez. Birkaç saniye içinde systemctl status komutunun yeni bir PID gösterdiğini gözlemleyin.
Bir systemd service'i root olmayan bir kullanıcı olarak 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 ekleyin. Sürecin minimum erişimle çalışması için NoNewPrivileges=true, PrivateTmp=true ve ProtectSystem=strict parametrelerini ekleyin. Ayrıcalıksız bir kullanıcı olarak çalıştırmak, bir service'in güvenliği için yapılabilecek en önemli değişiklikdir.
Service neden başlatılamadı?
Özet için systemctl status myapp.service, tam çıktı için ise journalctl -u myapp.service komutunu çalıştırın. En yaygın nedenler; ExecStart içindeki hatalı bir yol, eksik bir WorkingDirectory, User= dosyasını okuyamadığı için oluşan yetki hatası veya düzenleme sonrası unutulan bir sudo systemctl daemon-reload parametresidir. Journal, genellikle sorunu doğrudan belirten programın kendi hata mesajını gösterir.