Install in one-line (recommended)
- Install the necessary tools to run the script:
curl(if needed) - Open a shell and enter this command:
bash <(curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.sh)
- Go through the panel setup. The installer generates a random username, password, and web base path for you.
- Once configured, go to
http://<your-ip>:<your-port>/<your-path>and log in with the credentials issued by the panel after installation. Runx-uiat any time to reopen the management menu (start/stop the service, view or reset credentials, manage SSL certificates, and more).
Docker
-
Before installing Docker, install the following tools using your package manager:
curl,nano(if necessary) -
Install Docker using the official script
bash <(curl -sSL https://get.docker.com)
Using Docker Compose
- Create a
panelfolder and go to it
mkdir panel
cd panel
- Create and edit the
docker-compose.ymlfile usingnanoor another editor. Insert the following contents:
docker-compose.yml
services:
3xui:
image: ghcr.io/mhsanaei/3x-ui:latest
container_name: 3xui_app
# hostname: yourhostname <- optional
# The bundled Fail2ban (XUI_ENABLE_FAIL2BAN below) enforces the per-client IP
# limit with iptables, which needs NET_ADMIN. Without these caps a ban is
# logged and shown in fail2ban status but never actually applied.
# NET_RAW covers ip6tables. If you disable Fail2ban you can drop cap_add.
cap_add:
- NET_ADMIN
- NET_RAW
volumes:
- $PWD/db/:/etc/x-ui/
- $PWD/cert/:/root/cert/
environment:
XRAY_VMESS_AEAD_FORCED: "false"
XUI_ENABLE_FAIL2BAN: "true"
# To use PostgreSQL instead of the default SQLite, run:
# docker compose --profile postgres up -d
# and uncomment the two lines below.
# XUI_DB_TYPE: "postgres"
# XUI_DB_DSN: "postgres://xui:xui@postgres:5432/xui?sslmode=disable"
tty: true
ports:
- "2053:2053"
restart: unless-stopped
# Optional PostgreSQL backend — only started with: docker compose --profile postgres up -d
postgres:
image: postgres:16-alpine
container_name: 3xui_postgres
profiles: ["postgres"]
environment:
POSTGRES_USER: xui
POSTGRES_PASSWORD: xui
POSTGRES_DB: xui
volumes:
- $PWD/pgdata/:/var/lib/postgresql/data
restart: unless-stopped
Tip
The
portsblock only publishes the panel port (2053). Your inbound (proxy) ports are not exposed automatically — add each one to theportslist, or replace theportsblock withnetwork_mode: hostto expose every port the panel opens.
- Start the container:
docker compose up -d
To use the bundled PostgreSQL backend instead of SQLite, uncomment the two XUI_DB_* lines above and start with the profile:
docker compose --profile postgres up -d
- Open
http://<your-ip>:2053and log in to the panel. The default credentials are:
- 👤 Username:
admin - 🔑 Password:
admin
Caution
After logging in, immediately change the administrator credentials in the panel settings (
Panel Settings > Authentication)
Tip
It is also recommended to set up two-factor authentication and configure a custom panel web path for complete security
Update
If an update is needed, stop the container and pull the new image:
docker compose down
docker compose pull
docker compose up -d
Delete
If you want to delete the container, execute:
docker compose down
docker system prune -a
rm panel -rf
Using CLI
- Execute the following command:
docker run -itd \
-e XRAY_VMESS_AEAD_FORCED=false \
-e XUI_ENABLE_FAIL2BAN=true \
-v $PWD/db/:/etc/x-ui/ \
-v $PWD/cert/:/root/cert/ \
--cap-add=NET_ADMIN \
--cap-add=NET_RAW \
--network=host \
--restart=unless-stopped \
--name 3x-ui \
ghcr.io/mhsanaei/3x-ui:latest
Note
--cap-add=NET_ADMIN --cap-add=NET_RAWare required for the bundled Fail2ban to enforce per-client IP limits with iptables.--network=hostexposes every port the panel opens (panel + all inbounds); to publish ports individually instead, drop--network=hostand add-p 2053:2053plus one-pper inbound port.
Update
To update, find the container ID, stop it, and pull the new image:
docker ps -a
With this command, we find out the ID of the container the panel is running on. Next, stop the container and pull the image:
docker container stop <container_id>
docker image pull ghcr.io/mhsanaei/3x-ui
After that, run the command from step 3 again.
Delete
If you want to delete the container, execute:
docker container stop <container_id>
docker system prune -a
rm 3x-ui -rf
Using development build
-
Install the
gitpackage using your package manager. -
Clone the repository and go to its folder:
git clone https://github.com/MHSanaei/3x-ui.git
cd 3x-ui
- Start the container with the command below. The repository's
docker-compose.ymlbuilds the image from source first and then starts the container:
docker compose up -d
Install Legacy version
Caution
This method is not recommended. Always use the latest version.
-
Install the necessary tools to run the script:
curl(if needed) -
Open a shell and enter this command:
VERSION=v2.5.5 && bash <(curl -Ls "https://raw.githubusercontent.com/mhsanaei/3x-ui/$VERSION/install.sh") $VERSION
The required version is specified in the VERSION variable, e.g. v2.5.5.
- Go through the panel setup.
Manual installation
To download the latest version of the compressed package directly to your server, run the following command:
ARCH=$(uname -m)
case "${ARCH}" in
x86_64 | x64 | amd64) XUI_ARCH="amd64" ;;
i*86 | x86) XUI_ARCH="386" ;;
armv8* | armv8 | arm64 | aarch64) XUI_ARCH="arm64" ;;
armv7* | armv7) XUI_ARCH="armv7" ;;
armv6* | armv6) XUI_ARCH="armv6" ;;
armv5* | armv5) XUI_ARCH="armv5" ;;
s390x) XUI_ARCH="s390x" ;;
*) XUI_ARCH="amd64" ;;
esac
wget https://github.com/MHSanaei/3x-ui/releases/latest/download/x-ui-linux-${XUI_ARCH}.tar.gz
Once the compressed package is downloaded, execute the following commands to install or upgrade x-ui:
ARCH=$(uname -m)
case "${ARCH}" in
x86_64 | x64 | amd64) XUI_ARCH="amd64" ;;
i*86 | x86) XUI_ARCH="386" ;;
armv8* | armv8 | arm64 | aarch64) XUI_ARCH="arm64" ;;
armv7* | armv7) XUI_ARCH="armv7" ;;
armv6* | armv6) XUI_ARCH="armv6" ;;
armv5* | armv5) XUI_ARCH="armv5" ;;
s390x) XUI_ARCH="s390x" ;;
*) XUI_ARCH="amd64" ;;
esac
# Detect OS release
if [[ -f /etc/os-release ]]; then
source /etc/os-release
release=$ID
elif [[ -f /usr/lib/os-release ]]; then
source /usr/lib/os-release
release=$ID
else
echo "Failed to detect OS"
exit 1
fi
cd /root/
rm -rf x-ui/ /usr/local/x-ui/ /usr/bin/x-ui
tar zxvf x-ui-linux-${XUI_ARCH}.tar.gz
chmod +x x-ui/x-ui x-ui/bin/xray-linux-* x-ui/x-ui.sh
cp x-ui/x-ui.sh /usr/bin/x-ui
# Copy the appropriate service file based on OS
if [ -f "x-ui/x-ui.service" ]; then
cp -f x-ui/x-ui.service /etc/systemd/system/
else
case "${release}" in
ubuntu | debian | armbian)
if [ -f "x-ui/x-ui.service.debian" ]; then
cp -f x-ui/x-ui.service.debian /etc/systemd/system/x-ui.service
else
echo "Service file not found in archive, downloading..."
curl -fLo /etc/systemd/system/x-ui.service https://raw.githubusercontent.com/MHSanaei/3x-ui/main/x-ui.service.debian
fi
;;
arch | manjaro | parch)
if [ -f "x-ui/x-ui.service.arch" ]; then
cp -f x-ui/x-ui.service.arch /etc/systemd/system/x-ui.service
else
echo "Service file not found in archive, downloading..."
curl -fLo /etc/systemd/system/x-ui.service https://raw.githubusercontent.com/MHSanaei/3x-ui/main/x-ui.service.arch
fi
;;
*)
if [ -f "x-ui/x-ui.service.rhel" ]; then
cp -f x-ui/x-ui.service.rhel /etc/systemd/system/x-ui.service
else
echo "Service file not found in archive, downloading..."
curl -fLo /etc/systemd/system/x-ui.service https://raw.githubusercontent.com/MHSanaei/3x-ui/main/x-ui.service.rhel
fi
;;
esac
fi
mv x-ui/ /usr/local/
systemctl daemon-reload
systemctl enable x-ui
systemctl restart x-ui
Note
Alpine Linux uses OpenRC instead of systemd, so the systemd steps above don't apply. Use the one-line installer on Alpine, which sets up an OpenRC service automatically.
3X-UI
Overview
Setup
- ⬇️ Installation (One-line)
- ⬇️ Installation (Docker)
- ⬇️ Install Legacy version
- ⬇️ Manual installation
Configuration
- 🔐 Getting SSL certificate
- 🛡️ Setting Fail2Ban
- 🗝️ Environment variables
- ➡️ Using Reverse proxy
- 🛜 API Documentation
- 🌎 Geosites
- 🧩 Custom Geo sources