Resolving Systemd Service Remaining in Failed State After Stopping Minecraft Server


2 views

When managing a Minecraft server through systemd on CentOS 7, you might encounter an issue where stopping the service leaves it in a "failed" state despite successful termination. Here's a typical symptom:

systemctl status minecraftd.service
minecraftd.service - Minecraft Server
   Loaded: loaded (/usr/lib/systemd/system/minecraftd.service; disabled)
   Active: failed (Result: exit-code) since Mon 2015-06-01 16:01:37 UTC; 3s ago
  Process: 20975 ExecStart=/bin/java -Xmx1024M -Xms1024M -jar minecraft_server.jar nogui (code=exited, status=143)
 Main PID: 20975 (code=exited, status=143)

The exit code 143 indicates the process received SIGTERM (Termination signal), which is normal behavior when stopping a service. However, systemd interprets any non-zero exit code as a failure by default.

Java applications don't typically return exit code 0 when terminated by SIGTERM. This conflicts with systemd's expectation that a properly stopped service should return exit code 0.

Modify your service file to recognize exit code 143 as successful termination:

[Unit]
Description=Minecraft Server
After=syslog.target network.target

[Service]
Type=simple
WorkingDirectory=/root/Minecraft
ExecStart=/bin/java -Xmx1024M -Xms1024M -jar minecraft_server.jar nogui
SuccessExitStatus=143
Restart=on-failure

[Install]
WantedBy=multi-user.target

For more control, implement a shutdown hook in your Java application or wrapper script:

#!/bin/bash

cleanup() {
  echo "Shutting down Minecraft server..."
  # Send stop command to Minecraft console
  screen -S minecraft -X stuff "stop^M"
  wait $PID
  exit 0
}

trap cleanup SIGTERM

cd /root/Minecraft
java -Xmx1024M -Xms1024M -jar minecraft_server.jar nogui &
PID=$!
wait $PID

Add an explicit stop command to your service file:

[Service]
...
ExecStop=/bin/kill -s TERM $MAINPID
KillMode=process
...

After implementing changes, test the service behavior:

systemctl daemon-reload
systemctl start minecraftd.service
systemctl stop minecraftd.service
systemctl status minecraftd.service

The status should now show as "inactive" rather than "failed" when properly stopped.

For production environments, consider these enhancements:

  • Use a dedicated user account instead of root
  • Implement proper logging with StandardOutput/StandardError directives
  • Consider using screen or tmux for better session management

When working with systemd services on CentOS 7, you might encounter situations where services remain in a "failed" state after being manually stopped, even though the service actually stopped successfully. This issue commonly occurs with Java-based services like Minecraft servers.

The following service unit demonstrates the issue:

[Unit]
Description=Minecraft Server
After=syslog.target network.target

[Service]
Type=simple
WorkingDirectory=/root/Minecraft
ExecStart=/bin/java -Xmx1024M -Xms1024M -jar minecraft_server.jar nogui
Restart=on-failure

[Install]
WantedBy=multi-user.target

After stopping the service with systemctl stop, checking the status shows:

Active: failed (Result: exit-code)
Process: 20975 ExecStart=... (code=exited, status=143)
Main PID: 20975 (code=exited, status=143)

The service enters failed state because:

  • Java process receives SIGTERM (signal 15) when stopped
  • The process exits with status 143 (128 + SIGTERM)
  • Systemd interprets this as a failure due to non-zero exit code

Here are three approaches to resolve this:

1. Using SuccessExitStatus

Add this to your service file:

[Service]
...
SuccessExitStatus=143

2. Implementing Clean Shutdown

Modify the service to handle SIGTERM properly:

[Service]
...
ExecStop=/bin/kill -15 $MAINPID
TimeoutStopSec=30
KillSignal=15

3. Using ExecStop for Graceful Termination

For Minecraft specifically, implement RCON shutdown:

[Service]
...
ExecStop=/usr/bin/mcrcon -H 127.0.0.1 -p password "stop"
TimeoutStopSec=30

Here's the complete improved service file:

[Unit]
Description=Minecraft Server
After=network.target

[Service]
User=minecraft
Group=minecraft
WorkingDirectory=/opt/minecraft/server
ExecStart=/usr/bin/java -Xmx1024M -Xms1024M -jar server.jar nogui
ExecStop=/usr/bin/mcrcon -H 127.0.0.1 -p password "stop"
Restart=on-failure
SuccessExitStatus=143
TimeoutStopSec=30

[Install]
WantedBy=multi-user.target

After implementing changes:

  1. systemctl daemon-reload
  2. systemctl start minecraftd
  3. systemctl stop minecraftd
  4. Check status with systemctl status minecraftd
  • For services that must not be restarted after manual stop, use Restart=on-failure
  • Consider implementing RemainAfterExit=yes for certain service types
  • Always test with systemctl --no-pager status for complete information