Skip to content

systemd — Quản Lý Service trên Linux Intermediate

Bạn deploy xong app lên server, chạy python app.py &, rồi tắt SSH. Sáng hôm sau khách hàng gọi: "App chết rồi!". Bạn SSH lại, chạy lại. Tuần sau — lặp lại.

Đây không phải lỗi của app. Đây là lỗi của cách bạn chạy app. Production server không chạy bằng nohup& — nó chạy bằng systemd.

systemd là thứ phân biệt giữa một developer "chạy được trên máy mình" và một engineer "deploy được lên production". Bài này sẽ biến bạn từ người thứ nhất thành người thứ hai.


1. systemd — Bộ Não Của Linux Server

🎯 Mục tiêu

  • Hiểu systemd là gì và tại sao nó là PID 1
  • Nắm mental model: units, targets, dependencies
  • Phân biệt các loại unit: service, timer, socket, mount

systemd là PID 1

Khi Linux boot, kernel khởi động đúng một process duy nhất — đó là systemd (PID 1). Mọi process khác trên hệ thống đều là con cháu của nó. systemd là process đầu tiên khởi độngcuối cùng tắt. Nó quản lý tất cả: services, timers, mounts, network, logging.

Nếu systemd chết → toàn bộ hệ thống chết. Đó là lý do nó được thiết kế cực kỳ robust.

Mental Model — Cấu Trúc systemd

systemd (PID 1)
  ├── Targets (giống "runlevels")
  │   ├── multi-user.target  ← Server mode (no GUI)
  │   ├── graphical.target   ← Desktop mode
  │   └── rescue.target      ← Emergency repair

  ├── Service Units (.service)
  │   ├── nginx.service
  │   ├── postgresql.service
  │   └── myapp.service       ← Your app!

  ├── Timer Units (.timer)     ← Cron replacement
  ├── Socket Units (.socket)   ← Socket activation
  └── Mount Units (.mount)     ← Filesystem mounts

Cách đọc sơ đồ này:

  • Targets = nhóm các services cần chạy cùng nhau. multi-user.target là target phổ biến nhất trên server — nó kéo theo tất cả service cần thiết để server hoạt động (network, SSH, database, app…).
  • Service Units = những chương trình chạy nền. Nginx phục vụ web, PostgreSQL chạy database, myapp.service chạy app của bạn.
  • Timer Units = thay thế cron, nhưng mạnh hơn (có logging, dependency management).
  • Socket Units = khởi động service chỉ khi có connection đến (lazy activation).
  • Mount Units = quản lý việc mount filesystem.

Target — "Bạn muốn server ở trạng thái nào?"

Target là cách systemd trả lời câu hỏi: "Server nên chạy những gì?"

TargetMô tảKhi nào dùng
multi-user.targetServer mode, không GUIProduction server (99% trường hợp)
graphical.targetDesktop đầy đủMáy dev có GUI
rescue.targetSingle-user, minimalSửa lỗi khẩn cấp
emergency.targetBare minimum, root shellHệ thống hỏng nặng
bash
# Xem target hiện tại
systemctl get-default
# → multi-user.target (trên server)

# Đổi default target
sudo systemctl set-default multi-user.target

⚠️ Cạm bẫy

Problem: Nhiều developer mới mắc hai sai lầm kinh điển:

  1. Dùng nohup& để chạy production app — không auto-restart khi crash, không log management, không dependency ordering. App chết lúc 3 giờ sáng, không ai biết cho đến khi khách hàng gọi.

  2. Khởi động service thủ công sau mỗi lần reboot — server reboot vì maintenance, quên chạy systemctl start myapp. Kết quả: app down hàng giờ cho đến khi ai đó phát hiện.

Root cause: Không hiểu rằng systemctl enable tạo symlink để service tự động start khi boot.

Solution: Luôn dùng systemd cho production services. Luôn enable sau khi setup.

bash
# ✅ Đúng: enable + start
sudo systemctl enable --now myapp.service

# ❌ Sai: chỉ start, quên enable → mất sau reboot
sudo systemctl start myapp.service

2. Viết .service File — Chạy App Như Service

🎯 Mục tiêu

  • Viết được unit file .service hoàn chỉnh cho production
  • Hiểu từng section: [Unit], [Service], [Install]
  • Áp dụng security hardening và resource limits

Ví dụ hoàn chỉnh — FastAPI Backend

Giả sử bạn có một FastAPI app cần chạy production trên server. Đây là file .service đầy đủ:

ini
# /etc/systemd/system/myapp.service
[Unit]
Description=MyApp FastAPI Backend
Documentation=https://github.com/team/myapp
After=network.target postgresql.service
Wants=postgresql.service

[Service]
Type=exec
User=deploy
Group=deploy
WorkingDirectory=/opt/myapp
Environment=PYTHONPATH=/opt/myapp
Environment=APP_ENV=production
EnvironmentFile=/opt/myapp/.env
ExecStart=/opt/myapp/.venv/bin/uvicorn main:app --host 0.0.0.0 --port 8000
ExecReload=/bin/kill -HUP $MAINPID
Restart=on-failure
RestartSec=5
StartLimitBurst=3
StartLimitIntervalSec=60

# Security hardening
NoNewPrivileges=true
ProtectSystem=strict
ProtectHome=true
ReadWritePaths=/var/log/myapp /opt/myapp/data

# Resource limits
MemoryMax=512M
CPUQuota=80%

[Install]
WantedBy=multi-user.target

Giải thích từng section

[Unit] — Metadata và Dependencies

DirectiveGiải thích
DescriptionMô tả ngắn, hiện trong systemctl status
DocumentationLink tài liệu (optional nhưng nên có)
AfterChỉ start sau khi các unit này đã start. Đây là thứ tự, không phải dependency
Wants"Tôi muốn unit này cũng chạy, nhưng nếu nó fail thì tôi vẫn start". Soft dependency

💡 After vs. Wants vs. Requires

  • After=X → "Start sau X" (ordering only, không bắt X phải chạy)
  • Wants=X → "Cố gắng start X, nhưng tôi vẫn chạy nếu X fail" (soft dependency)
  • Requires=X → "X phải chạy. Nếu X fail → tôi cũng fail" (hard dependency)

Trong thực tế, After + Wants là combo phổ biến nhất. Dùng Requires chỉ khi app thật sự không thể hoạt động thiếu dependency đó.

[Service] — Cách Chạy, Restart Policy, Security

DirectiveGiải thích
Type=execsystemd theo dõi main process. Dùng cho hầu hết app hiện đại
User=deployChạy với user deploy, KHÔNG BAO GIỜ chạy với root
WorkingDirectorycd vào thư mục này trước khi chạy
EnvironmentSet biến môi trường. Mỗi biến một dòng
EnvironmentFileLoad biến từ file .env (giữ secrets ra khỏi unit file)
ExecStartCommand chạy app. Phải dùng absolute path
ExecReloadCommand reload config mà không restart (graceful)
Restart=on-failureAuto-restart khi exit code ≠ 0
RestartSec=5Đợi 5 giây trước khi restart (tránh restart storm)
StartLimitBurst=3Tối đa 3 lần restart…
StartLimitIntervalSec=60…trong 60 giây. Vượt quá → systemd ngưng restart

Security hardening — những dòng này ngăn service làm điều ngoài ý muốn:

DirectiveGiải thích
NoNewPrivileges=trueKhông thể escalate privileges (quan trọng!)
ProtectSystem=strictFilesystem read-only, trừ /dev, /proc, /sys
ProtectHome=trueKhông truy cập được /home, /root, /run/user
ReadWritePaths=...Whitelist các path được phép ghi

Resource limits — ngăn service ăn hết tài nguyên server:

DirectiveGiải thích
MemoryMax=512MGiới hạn RAM. Vượt quá → OOM kill
CPUQuota=80%Giới hạn CPU usage

[Install] — Khi Nào Start

ini
[Install]
WantedBy=multi-user.target

Dòng này nghĩa là: "Khi systemctl enable myapp, hãy tạo symlink để myapp tự động start cùng multi-user.target (tức là khi server boot vào chế độ bình thường)."

📌 Quy trình tạo service mới

bash
# 1. Tạo file service
sudo nano /etc/systemd/system/myapp.service

# 2. Reload systemd để nó biết có file mới
sudo systemctl daemon-reload

# 3. Enable (tự start khi boot) + start ngay
sudo systemctl enable --now myapp.service

# 4. Kiểm tra
sudo systemctl status myapp.service

3. systemctl — Điều Khiển Service

🎯 Mục tiêu

  • Thành thạo các lệnh systemctl thiết yếu
  • Hiểu workflow: edit → daemon-reload → restart
  • Đọc được output của systemctl status

Các lệnh thiết yếu

bash
# ═══════════════════════════════════════════════
# RELOAD — Sau khi sửa .service file
# ═══════════════════════════════════════════════
sudo systemctl daemon-reload    # BẮT BUỘC sau mỗi lần edit .service

# ═══════════════════════════════════════════════
# LIFECYCLE — Start / Stop / Restart
# ═══════════════════════════════════════════════
sudo systemctl start myapp      # Khởi động service
sudo systemctl stop myapp       # Dừng service (graceful)
sudo systemctl restart myapp    # Stop + Start (có downtime)
sudo systemctl reload myapp     # Reload config, KHÔNG restart (zero-downtime)

# ═══════════════════════════════════════════════
# BOOT — Enable / Disable tự động start
# ═══════════════════════════════════════════════
sudo systemctl enable myapp     # Tự start khi boot
sudo systemctl disable myapp    # Không tự start khi boot
sudo systemctl enable --now myapp  # Enable + Start ngay (tiện!)

# ═══════════════════════════════════════════════
# STATUS — Lệnh QUAN TRỌNG NHẤT
# ═══════════════════════════════════════════════
sudo systemctl status myapp

Đọc output của systemctl status

bash
$ sudo systemctl status myapp
 myapp.service - MyApp FastAPI Backend
     Loaded: loaded (/etc/systemd/system/myapp.service; enabled; preset: disabled)
     Active: active (running) since Mon 2024-01-15 08:30:00 UTC; 2h ago
   Main PID: 12345 (uvicorn)
      Tasks: 4 (limit: 4096)
     Memory: 128.5M (max: 512.0M)
        CPU: 1min 23.456s
     CGroup: /system.slice/myapp.service
             ├─12345 /opt/myapp/.venv/bin/python /opt/myapp/.venv/bin/uvicorn main:app
             └─12350 /opt/myapp/.venv/bin/python /opt/myapp/.venv/bin/uvicorn main:app

Jan 15 08:30:00 prod-server systemd[1]: Started MyApp FastAPI Backend.
Jan 15 08:30:01 prod-server uvicorn[12345]: INFO:     Uvicorn running on http://0.0.0.0:8000

Cách đọc:

DòngÝ nghĩa
Active: active (running)✅ Service đang chạy bình thường
enabled✅ Sẽ tự start khi boot
Main PID: 12345Process ID chính — dùng để kill nếu cần
Memory: 128.5M (max: 512.0M)RAM đang dùng / giới hạn
Tasks: 4Số threads/processes
Các dòng log cuốiNhững dòng log gần nhất từ journalctl

Lệnh nâng cao cho troubleshooting

bash
# Liệt kê TẤT CẢ service đang chạy
systemctl list-units --type=service --state=running

# Liệt kê service bị fail
systemctl list-units --type=service --state=failed

# Xem dependency tree của service
systemctl list-dependencies myapp.service

# Xem property cụ thể
systemctl show myapp.service -p MainPID,MemoryCurrent,RestartCount

# Mask service (ngăn start bằng mọi cách — kể cả dependency)
sudo systemctl mask dangerous-service
sudo systemctl unmask dangerous-service

⚠️ daemon-reload — Đừng bao giờ quên!

Sau khi edit file .service, BẮT BUỘC phải chạy sudo systemctl daemon-reload trước khi restart. Nếu không, systemd vẫn dùng config cũ trong memory.

Đây là nguồn gốc của vô số phiên debug: "Tôi sửa config rồi mà sao không có tác dụng?" → Quên daemon-reload.


4. journalctl — Đọc Log Như Detective

🎯 Mục tiêu

  • Dùng journalctl để đọc log service hiệu quả
  • Filter theo thời gian, severity, service
  • Biết khi nào dùng -f, khi nào dùng --since

journalctl là công cụ đọc log tập trung của systemd. Mọi output (stdout, stderr) từ service đều được capture vào journal — không cần config log file riêng.

Các lệnh journalctl thiết yếu

bash
# ═══════════════════════════════════════════════
# XEM LOG CỦA MỘT SERVICE
# ═══════════════════════════════════════════════
journalctl -u myapp.service              # Tất cả log của myapp

# ═══════════════════════════════════════════════
# FOLLOW REAL-TIME (như tail -f)
# ═══════════════════════════════════════════════
journalctl -u myapp.service -f           # Xem log real-time
# Ctrl+C để thoát

# ═══════════════════════════════════════════════
# FILTER THEO THỜI GIAN
# ═══════════════════════════════════════════════
journalctl -u myapp.service -b           # Log từ lần boot gần nhất
journalctl -u myapp.service --since "1 hour ago"
journalctl -u myapp.service --since "2024-01-15 08:00" --until "2024-01-15 09:00"
journalctl -u myapp.service --since today

# ═══════════════════════════════════════════════
# FILTER THEO SEVERITY
# ═══════════════════════════════════════════════
journalctl -u myapp.service -p err       # Chỉ errors
journalctl -u myapp.service -p warning   # Warnings trở lên
# Các level: emerg, alert, crit, err, warning, notice, info, debug

# ═══════════════════════════════════════════════
# SỐ DÒNG & FORMAT
# ═══════════════════════════════════════════════
journalctl -u myapp.service -n 50        # 50 dòng gần nhất
journalctl -u myapp.service -o json | head -5   # JSON (cho parsing)
journalctl -u myapp.service --no-pager   # Không dùng pager (cho scripts)

Ví dụ thực tế — Debug service crash

Bạn nhận alert lúc 2 giờ sáng: "myapp down". Đây là workflow debug:

bash
# Bước 1: Service đang ở trạng thái gì?
sudo systemctl status myapp
# → Active: failed (Result: exit-code)

# Bước 2: Xem log gần nhất
journalctl -u myapp -n 30
# → Thấy traceback Python

# Bước 3: Xem log quanh thời điểm crash
journalctl -u myapp --since "2 hours ago" -p err
# → Thấy lỗi bắt đầu từ 01:45 AM

# Bước 4: Xem log chi tiết hơn
journalctl -u myapp --since "01:40" --until "01:50"
# → Phát hiện OOM kill vì memory leak

💡 journalctl vs log files truyền thống

Trước systemd: Mỗi app ghi log vào file riêng (/var/log/myapp.log). Phải tự setup log rotation, permission, format.

Với journalctl: Log tập trung, tự rotate, có index để tìm nhanh, filter được theo time/severity/unit. Không cần cấu hình thêm gì.

Tuy nhiên, nhiều app production vẫn ghi log vào file (để ship sang ELK Stack, Datadog…). Trong trường hợp đó, journalctl vẫn capture stdout/stderr, còn app tự ghi file riêng.


5. 🔥 Production Incident — "Service Restart Vô Hạn"

🔥 Incident thực tế: Restart Loop

Scenario: Sau deployment, team nhận alert Slack: CPU spike trên prod-server-01. Khi SSH vào kiểm tra:

bash
$ systemctl status myapp
 myapp.service - MyApp FastAPI Backend
     Loaded: loaded (/etc/systemd/system/myapp.service; enabled)
     Active: activating (auto-restart) (Result: exit-code)
    Process: 15234 ExecStart=/opt/myapp/.venv/bin/uvicorn main:app ... (code=exited, status=1/FAILURE)
   Main PID: 15234 (code=exited, status=1/FAILURE)
     Status: "Restarts: 42"

Service đang trong trạng thái activating (auto-restart) — khởi động, crash, restart, crash, restart… 42 lần và đang tiếp tục.

Investigation

bash
# Bước 1: Xem tại sao service crash
$ journalctl -u myapp -n 30 --no-pager
...
Jan 15 14:32:01 prod-server uvicorn[15234]: Traceback (most recent call last):
Jan 15 14:32:01 prod-server uvicorn[15234]:   File "/opt/myapp/main.py", line 3
Jan 15 14:32:01 prod-server uvicorn[15234]: ModuleNotFoundError: No module named 'pydantic'
Jan 15 14:32:01 prod-server uvicorn[15234]: ERROR: Application startup failed.
Jan 15 14:32:06 prod-server systemd[1]: myapp.service: Main process exited, code=exited, status=1/FAILURE
Jan 15 14:32:06 prod-server systemd[1]: myapp.service: Scheduled restart job, restart counter is at 42.

Root Cause

Deploy script đã update code (git pull) nhưng quên chạy pip install -r requirements.txt trong virtualenv. Version mới của code import pydantic v2, nhưng virtualenv vẫn có pydantic v1 (hoặc không có).

→ App crash ngay khi import → exit code 1 → systemd thấy Restart=on-failure → restart → crash lại → restart loop vô hạn.

Tại sao nó loop?

Nếu .service file không có StartLimitBurstStartLimitIntervalSec:

crash → restart (5s) → crash → restart (5s) → crash → restart (5s) → ...

Vòng lặp này không bao giờ dừng. Mỗi lần restart, app crash trong < 1 giây, rồi systemd đợi RestartSec=5 rồi thử lại. CPU spike vì liên tục spawn process mới.

Fix

bash
# Bước 1: DỪNG service ngay (ngắt restart loop)
sudo systemctl stop myapp

# Bước 2: Fix root cause — cài dependencies
cd /opt/myapp
source .venv/bin/activate
pip install -r requirements.txt

# Bước 3: Test thủ công trước khi start service
/opt/myapp/.venv/bin/uvicorn main:app --host 0.0.0.0 --port 8000
# → Chạy OK? Ctrl+C

# Bước 4: Start service lại
sudo systemctl start myapp
sudo systemctl status myapp
# → Active: active (running) ✅

Phòng ngừa

Thêm StartLimitBurstStartLimitIntervalSec vào .service file:

ini
[Service]
Restart=on-failure
RestartSec=5
StartLimitBurst=3           # Tối đa 3 lần restart...
StartLimitIntervalSec=60    # ...trong 60 giây

Nghĩa là: nếu service crash 3 lần trong 60 giây → systemd ngừng restart và đánh dấu service là failed. Bạn sẽ thấy rõ trong monitoring thay vì để nó loop ngầm.

✅ Bài học rút ra

  1. Luôn kiểm tra journalctl TRƯỚC khi làm bất cứ điều gì khi service lỗi
  2. Deploy script phải bao gồm cài dependencies, không chỉ pull code
  3. StartLimitBurst + StartLimitIntervalSec là bắt buộc cho production services
  4. Test thủ công trước khi start service lại — đừng "start rồi cầu nguyện"

6. systemd Timers — Thay Thế Cron

🎯 Mục tiêu

  • Hiểu systemd timers là gì và tại sao tốt hơn cron
  • Viết được một timer unit cơ bản

Timer Unit — Ví dụ backup database hàng ngày

Mỗi timer cần hai file: một .timer (lịch chạy) và một .service (việc cần làm).

File timer:

ini
# /etc/systemd/system/backup.timer
[Unit]
Description=Daily database backup

[Timer]
OnCalendar=*-*-* 02:00:00
Persistent=true

[Install]
WantedBy=timers.target

File service tương ứng:

ini
# /etc/systemd/system/backup.service
[Unit]
Description=Run database backup script

[Service]
Type=oneshot
User=backup
ExecStart=/opt/scripts/backup-db.sh
bash
# Kích hoạt timer
sudo systemctl enable --now backup.timer

# Kiểm tra tất cả timers
systemctl list-timers --all

# Xem khi nào timer chạy lần tiếp theo
systemctl list-timers backup.timer

Tại sao timers tốt hơn cron?

Tính năngcronsystemd timer
LoggingPhải tự redirect (>> /var/log/...)Tự động vào journalctl
DependenciesKhông hỗ trợAfter=, Wants= đầy đủ
Resource limitsKhông cóMemoryMax, CPUQuota
Missed runsBỏ qua nếu máy tắtPersistent=true chạy bù
MonitoringPhải đọc log filesystemctl list-timers thấy ngay
Error handlingEmail (nếu config)OnFailure= trigger unit khác

💡 Khi nào vẫn dùng cron?

Cron vẫn OK cho:

  • Script đơn giản, chạy nhanh, không cần monitoring
  • Môi trường không có systemd (container minimal, một số embedded systems)
  • User-level cron jobs (crontab -e) khi không có quyền root

Nhưng cho production scheduled tasks → luôn dùng systemd timers.


7. Exercises — Luyện Tập

🎯 Mục tiêu

  • Spot the Bug: tìm lỗi trong .service file
  • Diagnose: đọc systemctl status output và xác định nguyên nhân

🐛 Spot the Bug — Tìm lỗi trong .service file

Đọc file .service sau và tìm 3 lỗi sẽ khiến service không hoạt động đúng trên production:

ini
# /etc/systemd/system/webapp.service
[Unit]
Description=Web Application
After=network.target

[Service]
ExecStart=uvicorn main:app --host 0.0.0.0 --port 8000
Restart=always
User=root

[Install]
WantedBy=multi-user.target
🔍 Xem đáp án

Lỗi 1: ExecStart không dùng absolute path

ini
# ❌ Sai
ExecStart=uvicorn main:app --host 0.0.0.0 --port 8000

# ✅ Đúng — phải dùng absolute path
ExecStart=/opt/webapp/.venv/bin/uvicorn main:app --host 0.0.0.0 --port 8000

systemd yêu cầu absolute path cho ExecStart. Nó không dùng $PATH như shell bình thường.

Lỗi 2: Thiếu WorkingDirectory

ini
# ✅ Thêm dòng này
WorkingDirectory=/opt/webapp

Không có WorkingDirectory, uvicorn sẽ tìm main:app/ (root directory) → ModuleNotFoundError.

Lỗi 3: User=root — KHÔNG BAO GIỜ chạy app với root

ini
# ❌ Sai — security risk nghiêm trọng
User=root

# ✅ Đúng — dùng user riêng với quyền tối thiểu
User=deploy
Group=deploy

Chạy app với root nghĩa là nếu app bị hack, attacker có full control hệ thống. Luôn tạo user riêng với quyền tối thiểu (principle of least privilege).

Bonus: Restart=always nên là Restart=on-failure

Restart=always sẽ restart kể cả khi bạn cố ý systemctl stop. Thường on-failure là đủ — chỉ restart khi app crash (exit code ≠ 0).

🔍 Diagnose — Đọc status output

Bạn nhận được output sau khi kiểm tra service. Chẩn đoán nguyên nhân:

bash
$ systemctl status webapp
 webapp.service - Web Application
     Loaded: loaded (/etc/systemd/system/webapp.service; enabled)
     Active: failed (Result: exit-code) since Mon 2024-01-15 10:00:05 UTC
    Process: 8901 ExecStart=/opt/webapp/.venv/bin/uvicorn main:app (code=exited, status=217/USER)
   Main PID: 8901 (code=exited, status=217/USER)

Jan 15 10:00:05 prod-server systemd[1]: webapp.service: Failed with result 'exit-code'.
🔍 Xem đáp án

Nguyên nhân: Exit status 217/USER

Status code 217/USER nghĩa là user được chỉ định trong .service file không tồn tại trên hệ thống.

bash
# Kiểm tra
id deploy
# → id: 'deploy': no such user

# Fix: tạo user
sudo useradd --system --no-create-home --shell /usr/sbin/nologin deploy

# Restart service
sudo systemctl restart webapp

Tip: Tra cứu systemd exit codes tại man systemd.exec hoặc search "systemd exit status 217". Mỗi con số có ý nghĩa cụ thể:

  • 200 = Chung chung
  • 203 = EXEC (binary không tìm thấy)
  • 217 = USER (user không tồn tại)
  • 226 = NAMESPACE (namespace setup failed)

8. Production Anti-Patterns

⚠️ Cạm bẫy

Problem: Chạy app production bằng nohup&:

bash
# ❌ "Production deployment" kiểu amateur
ssh prod-server
cd /opt/myapp
nohup python app.py > /var/log/myapp.log 2>&1 &
echo "Deployed! 🎉"
exit

Tại sao đây là thảm họa chờ xảy ra:

Vấn đềnohup &systemd
App crashChết luôn. Không ai biếtAuto-restart với Restart=on-failure
Server rebootApp không tự startenable → tự start khi boot
Log rotationLog file phình to vô hạnjournalctl tự rotate
Resource limitsKhông có — app ăn hết RAM → OOMMemoryMax, CPUQuota
DependenciesKhông có — app start trước databaseAfter=, Wants= ordering
SecurityThường chạy với user SSH (có thể là root!)User=deploy, NoNewPrivileges
Monitoringps aux | grep app 🤮systemctl status, alerting integration

Worst case thực tế: Bạn SSH vào server bằng user root, chạy nohup python app.py &. App có bug cho phép file upload. Attacker upload reverse shell → có quyền root → game over.

Solution: Luôn dùng systemd. Không có ngoại lệ cho production.

⚡ Performance — Phân tích boot time

Dùng systemd-analyze để tìm service nào khởi động chậm nhất:

bash
# Tổng thời gian boot
systemd-analyze

# Service nào chậm nhất? (sorted by time)
systemd-analyze blame
# → 12.345s postgresql.service
# →  8.234s docker.service
# →  3.456s nginx.service

# Critical chain — tìm bottleneck trong boot sequence
systemd-analyze critical-chain
# Hiện dependency chain, highlight service nào block boot

# Vẽ SVG diagram (mở trên browser)
systemd-analyze plot > boot-analysis.svg

Nếu server boot chậm, đây là công cụ đầu tiên bạn nên dùng. Thường thì culprit là database services hoặc disk check (fsck).


Tổng Kết

Khái niệmLệnh / FileKhi nào dùng
Tạo service/etc/systemd/system/myapp.serviceDeploy app mới
Reload configsystemctl daemon-reloadSau khi edit .service
Start + Enablesystemctl enable --now myappSetup lần đầu
Xem trạng tháisystemctl status myappLuôn luôn — lệnh đầu tiên khi debug
Đọc logjournalctl -u myapp -fDebug realtime
Log theo thời gianjournalctl -u myapp --since "1 hour ago"Post-mortem
Scheduled tasks.timer + .serviceThay cron cho production
Boot analysissystemd-analyze blameServer boot chậm

Nguyên tắc vàng: Trên production, mọi thứ chạy nền đều phải là systemd service. Không nohup, không screen, không tmux cho production workloads.