fix(docker): make x-ui CLI menu work inside containers

check_status() only recognized a systemd service or Alpine's
/etc/init.d/x-ui, neither of which exists in a container where the panel
runs as the foreground main process (PID 1 via "exec /app/x-ui"). Every
CLI command therefore failed with "Please install the panel first", and
restart/restart-xray relied on rc-service/systemctl that aren't present.

Detect the container (/.dockerenv or XUI_IN_DOCKER) and, when inside one:
- resolve the panel binary under /app instead of /usr/local/x-ui
- derive status from the running process instead of a service file
- restart via SIGHUP and restart-xray via SIGUSR1 to the panel process
- show Docker-appropriate guidance for start/stop/enable/disable

The Dockerfile sets XUI_IN_DOCKER/XUI_MAIN_FOLDER so detection is
explicit even though /.dockerenv alone suffices.

Closes #4817
This commit is contained in:
MHSanaei 2026-06-02 21:26:47 +02:00
parent ac67c52278
commit f901cd42a5
No known key found for this signature in database
GPG key ID: 7E4060F2FBE5AB7A
2 changed files with 113 additions and 3 deletions

View file

@ -63,9 +63,9 @@ RUN chmod +x \
/app/x-ui \ /app/x-ui \
/usr/bin/x-ui /usr/bin/x-ui
ENV XUI_IN_DOCKER="true"
ENV XUI_MAIN_FOLDER="/app"
ENV XUI_ENABLE_FAIL2BAN="true" ENV XUI_ENABLE_FAIL2BAN="true"
# Database backend: set XUI_DB_TYPE=postgres and XUI_DB_DSN=postgres://... to use PostgreSQL.
# Default (unset) is SQLite stored under /etc/x-ui.
ENV XUI_DB_TYPE="" ENV XUI_DB_TYPE=""
ENV XUI_DB_DSN="" ENV XUI_DB_DSN=""
EXPOSE 2053 EXPOSE 2053

112
x-ui.sh
View file

@ -69,8 +69,17 @@ echo "The OS release is: $release"
os_version="" os_version=""
os_version=$(grep "^VERSION_ID" /etc/os-release | cut -d '=' -f2 | tr -d '"' | tr -d '.') os_version=$(grep "^VERSION_ID" /etc/os-release | cut -d '=' -f2 | tr -d '"' | tr -d '.')
running_in_docker="false"
if [[ -f /.dockerenv ]] || [[ "${XUI_IN_DOCKER}" == "true" ]]; then
running_in_docker="true"
fi
# Declare Variables # Declare Variables
xui_folder="${XUI_MAIN_FOLDER:=/usr/local/x-ui}" if [[ "${running_in_docker}" == "true" ]]; then
xui_folder="${XUI_MAIN_FOLDER:=/app}"
else
xui_folder="${XUI_MAIN_FOLDER:=/usr/local/x-ui}"
fi
xui_service="${XUI_SERVICE:=/etc/systemd/system}" xui_service="${XUI_SERVICE:=/etc/systemd/system}"
log_folder="${XUI_LOG_FOLDER:=/var/log/x-ui}" log_folder="${XUI_LOG_FOLDER:=/var/log/x-ui}"
mkdir -p "${log_folder}" mkdir -p "${log_folder}"
@ -400,6 +409,15 @@ start() {
echo "" echo ""
LOGI "Panel is running, No need to start again, If you need to restart, please select restart" LOGI "Panel is running, No need to start again, If you need to restart, please select restart"
else else
if [[ "${running_in_docker}" == "true" ]]; then
LOGE "Panel process is not running inside this container."
LOGI "In Docker the panel is the container's main process. Restart the container to bring it back up:"
LOGI " docker restart <container_name>"
if [[ $# == 0 ]]; then
before_show_menu
fi
return 0
fi
if [[ $release == "alpine" ]]; then if [[ $release == "alpine" ]]; then
rc-service x-ui start rc-service x-ui start
else else
@ -425,6 +443,15 @@ stop() {
echo "" echo ""
LOGI "Panel stopped, No need to stop again!" LOGI "Panel stopped, No need to stop again!"
else else
if [[ "${running_in_docker}" == "true" ]]; then
LOGI "In Docker the panel runs as the container's main process."
LOGI "To stop it, stop the container from the host:"
LOGI " docker stop <container_name>"
if [[ $# == 0 ]]; then
before_show_menu
fi
return 0
fi
if [[ $release == "alpine" ]]; then if [[ $release == "alpine" ]]; then
rc-service x-ui stop rc-service x-ui stop
else else
@ -445,6 +472,26 @@ stop() {
} }
restart() { restart() {
if [[ "${running_in_docker}" == "true" ]]; then
if signal_xui HUP; then
sleep 1
signal_xui USR1
LOGI "Restart signal sent to the panel and xray-core."
else
LOGE "Could not find the running panel process to signal."
fi
sleep 2
check_status
if [[ $? == 0 ]]; then
LOGI "x-ui and xray Restarted successfully"
else
LOGE "Panel restart failed, Please check the log information later"
fi
if [[ $# == 0 ]]; then
before_show_menu
fi
return 0
fi
if [[ $release == "alpine" ]]; then if [[ $release == "alpine" ]]; then
rc-service x-ui restart rc-service x-ui restart
else else
@ -463,6 +510,19 @@ restart() {
} }
restart_xray() { restart_xray() {
if [[ "${running_in_docker}" == "true" ]]; then
if signal_xui USR1; then
LOGI "xray-core Restart signal sent successfully, Please check the log information to confirm whether xray restarted successfully"
else
LOGE "Could not find the running panel process to signal."
fi
sleep 2
show_xray_status
if [[ $# == 0 ]]; then
before_show_menu
fi
return 0
fi
if [[ $release == "alpine" ]]; then if [[ $release == "alpine" ]]; then
rc-service x-ui reload rc-service x-ui reload
else else
@ -477,6 +537,13 @@ restart_xray() {
} }
status() { status() {
if [[ "${running_in_docker}" == "true" ]]; then
show_status
if [[ $# == 0 ]]; then
before_show_menu
fi
return 0
fi
if [[ $release == "alpine" ]]; then if [[ $release == "alpine" ]]; then
rc-service x-ui status rc-service x-ui status
else else
@ -488,6 +555,14 @@ status() {
} }
enable() { enable() {
if [[ "${running_in_docker}" == "true" ]]; then
LOGI "Autostart is controlled by the Docker restart policy (e.g. 'restart: unless-stopped' in docker-compose.yml)."
LOGI "There is no service to enable inside the container."
if [[ $# == 0 ]]; then
before_show_menu
fi
return 0
fi
if [[ $release == "alpine" ]]; then if [[ $release == "alpine" ]]; then
rc-update add x-ui default rc-update add x-ui default
else else
@ -505,6 +580,14 @@ enable() {
} }
disable() { disable() {
if [[ "${running_in_docker}" == "true" ]]; then
LOGI "Autostart is controlled by the Docker restart policy (e.g. 'restart: unless-stopped' in docker-compose.yml)."
LOGI "Set 'restart: no' for the container on the host to disable autostart."
if [[ $# == 0 ]]; then
before_show_menu
fi
return 0
fi
if [[ $release == "alpine" ]]; then if [[ $release == "alpine" ]]; then
rc-update del x-ui rc-update del x-ui
else else
@ -673,8 +756,31 @@ update_shell() {
fi fi
} }
xui_pid() {
ps -ef 2> /dev/null | grep -F "${xui_folder}/x-ui" | grep -v grep | awk 'NR==1 {print $1}'
}
signal_xui() {
local sig="$1" pid
pid="$(xui_pid)"
if [[ -z "${pid}" ]]; then
return 1
fi
kill -"${sig}" "${pid}" 2> /dev/null
}
# 0: running, 1: not running, 2: not installed # 0: running, 1: not running, 2: not installed
check_status() { check_status() {
if [[ "${running_in_docker}" == "true" ]]; then
if [[ ! -x "${xui_folder}/x-ui" ]]; then
return 2
fi
if [[ -n "$(xui_pid)" ]]; then
return 0
else
return 1
fi
fi
if [[ $release == "alpine" ]]; then if [[ $release == "alpine" ]]; then
if [[ ! -f /etc/init.d/x-ui ]]; then if [[ ! -f /etc/init.d/x-ui ]]; then
return 2 return 2
@ -761,6 +867,10 @@ show_status() {
} }
show_enable_status() { show_enable_status() {
if [[ "${running_in_docker}" == "true" ]]; then
echo -e "Start automatically: ${green}Managed by Docker${plain}"
return
fi
check_enabled check_enabled
if [[ $? == 0 ]]; then if [[ $? == 0 ]]; then
echo -e "Start automatically: ${green}Yes${plain}" echo -e "Start automatically: ${green}Yes${plain}"