Chuyện gì đang xảy ra
Bạn chạy systemctl start myapp.service và systemd trả về:
Job for myapp.service failed because the control process exited with error code.
See "systemctl status myapp.service" and "journalctl -xe" for details.
Thông báo đó về bản chất không có nhiều thông tin — nó chỉ là một thông báo chung có nghĩa là "tiến trình thoát với mã khác không." Nguyên nhân thực sự nằm ở tầng sâu hơn. Đây là cách tìm ra nó.
Bước 1: Lấy lỗi thực sự
Hai lệnh sau, hãy chạy cả hai:
systemctl status myapp.service
journalctl -u myapp.service -n 50 --no-pager
Lệnh đầu tiên hiển thị một đoạn log ngắn và mã thoát. Lệnh thứ hai xuất 50 dòng log gần nhất trực tiếp ra terminal. Nếu lỗi vừa xảy ra, journalctl -xe cũng hoạt động và bổ sung thêm ngữ cảnh cấp kernel xung quanh thời điểm xảy ra lỗi.
Tìm các dòng được đánh dấu FAILED, error, hoặc output thô của tiến trình — đó là nơi chứa thông báo lỗi thực sự.
Bước 2: Xác định nguyên nhân gốc rễ
Sáu nguyên nhân sau chiếm phần lớn các trường hợp. Kiểm tra theo thứ tự này:
Đường dẫn binary sai hoặc file thực thi bị thiếu
ExecStart= trỏ đến đường dẫn không tồn tại, hoặc file không có quyền thực thi.
# Kiểm tra file unit
systemctl cat myapp.service
# Xác minh binary tồn tại
which myapp
ls -la /usr/local/bin/myapp
Sửa đường dẫn trong file unit, hoặc cài đặt binary còn thiếu.
Permission denied
Khi một service chạy với tư cách người dùng không phải root (qua User=), người dùng đó cần quyền đọc/thực thi đối với binary, file cấu hình và thư mục làm việc. Thiếu bất kỳ quyền nào cũng gây ra crash ngay lập tức.
# Kiểm tra service chạy với người dùng nào
grep -E 'User=|Group=|WorkingDirectory=' /etc/systemd/system/myapp.service
# Kiểm tra quyền truy cập thủ công
sudo -u serviceuser /usr/local/bin/myapp --config /etc/myapp/config.yaml
Điều chỉnh quyền sở hữu hoặc phân quyền:
chown serviceuser:serviceuser /etc/myapp/config.yaml
chmod 750 /var/lib/myapp
Port đã được sử dụng
Việc bind vào một port đang bị chiếm sẽ thất bại ngay khi khởi động. Nguyên nhân thường gặp: một instance khác của cùng ứng dụng, hoặc tiến trình còn sót lại từ lần crash trước.
ss -tlnp | grep :8080
# hoặc
lsof -i :8080
Tắt tiến trình xung đột, hoặc cập nhật port trong cấu hình ứng dụng.
Thiếu biến môi trường hoặc cấu hình sai
Ứng dụng phụ thuộc vào EnvironmentFile= sẽ thoát ngay lập tức nếu file đó bị thiếu hoặc chứa giá trị không hợp lệ.
# Kiểm tra EnvironmentFile có tồn tại không
grep EnvironmentFile /etc/systemd/system/myapp.service
ls -la /etc/myapp/.env
Tạo hoặc sửa file env, sau đó xác minh binary thực sự khởi động được khi chạy thủ công với tư cách người dùng của service:
sudo -u serviceuser bash -c 'source /etc/myapp/.env && /usr/local/bin/myapp'
Người dùng hoặc nhóm không tồn tại
Systemd sẽ từ chối khởi động service nếu User= hoặc Group= trong file unit không tồn tại trên hệ thống. Trường hợp này xảy ra khá nhiều sau khi cài đặt mới.
id serviceuser
Tạo nếu chưa có:
useradd --system --no-create-home --shell /usr/sbin/nologin serviceuser
Thiếu thư viện chia sẻ
Một binary có thể tồn tại trên đĩa nhưng vẫn không thể khởi chạy nếu không tìm thấy thư viện .so cần thiết lúc runtime.
ldd /usr/local/bin/myapp
Bất kỳ thư viện nào hiển thị not found đều cần được cài đặt. Cài package còn thiếu, hoặc thêm đường dẫn thư viện vào LD_LIBRARY_PATH thông qua chỉ thị Environment= trong file unit.
Bước 3: Áp dụng bản sửa và reload
Đã chỉnh sửa file unit tại /etc/systemd/system/myapp.service? Luôn reload daemon trước — systemd lưu file unit vào bộ nhớ và sẽ không nhận thay đổi nếu không reload:
systemctl daemon-reload
systemctl restart myapp.service
Chỉ thay đổi file cấu hình, phân quyền, hoặc file env (không phải file unit)? Bỏ qua daemon-reload và restart trực tiếp:
systemctl restart myapp.service
Xác minh
Kiểm tra trạng thái:
systemctl status myapp.service
Active: active (running) nghĩa là đã thành công. Theo dõi log trực tiếp khoảng 30 giây để bắt các lỗi xuất hiện muộn:
journalctl -u myapp.service -f
Muốn service tự khởi động sau khi reboot? Kích hoạt nó:
systemctl enable myapp.service
Bảng tóm tắt lệnh debug nhanh
systemctl status myapp.service— vài dòng log gần nhất + mã thoátjournalctl -u myapp.service -n 100— toàn bộ log gần đây của unitjournalctl -xe— ngữ cảnh toàn hệ thống xung quanh thời điểm lỗisystemctl cat myapp.service— hiển thị file unit đang được sử dụngsystemd-analyze verify myapp.service— kiểm tra cú pháp file unit
Bài học rút ra
"Control process exited with error code" là cách systemd nói: tiến trình trả về mã khác không. Chỉ vậy thôi. Hãy đi thẳng đến journalctl -u servicename mỗi lần — log riêng của unit hữu ích hơn nhiều so với journalctl -xe cho loại lỗi này. Và nếu bạn đang viết service của riêng mình, hãy ghi log ra stderr hoặc stdout. Systemd bắt cả hai, nên thông báo lỗi của bạn sẽ hiển thị ngay lập tức trong journalctl.

