16 Installation
Sanaei edited this page 2026-06-03 00:39:58 +02:00
  1. Install the necessary tools to run the script: curl (if needed)
  2. Open a shell and enter this command:
bash <(curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.sh)
  1. Go through the panel setup. The installer generates a random username, password, and web base path for you.
  2. Once configured, go to http://<your-ip>:<your-port>/<your-path> and log in with the credentials issued by the panel after installation. Run x-ui at any time to reopen the management menu (start/stop the service, view or reset credentials, manage SSL certificates, and more).

Docker

  1. Before installing Docker, install the following tools using your package manager: curl, nano (if necessary)

  2. Install Docker using the official script

bash <(curl -sSL https://get.docker.com)

Using Docker Compose

  1. Create a panel folder and go to it
mkdir panel
cd panel
  1. Create and edit the docker-compose.yml file using nano or 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 ports block only publishes the panel port (2053). Your inbound (proxy) ports are not exposed automatically — add each one to the ports list, or replace the ports block with network_mode: host to expose every port the panel opens.

  1. 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
  1. Open http://<your-ip>:2053 and 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

  1. 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_RAW are required for the bundled Fail2ban to enforce per-client IP limits with iptables. --network=host exposes every port the panel opens (panel + all inbounds); to publish ports individually instead, drop --network=host and add -p 2053:2053 plus one -p per 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

  1. Install the git package using your package manager.

  2. Clone the repository and go to its folder:

git clone https://github.com/MHSanaei/3x-ui.git
cd 3x-ui
  1. Start the container with the command below. The repository's docker-compose.yml builds 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.

  1. Install the necessary tools to run the script: curl (if needed)

  2. 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.

  1. 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.