## Install in one-line (recommended)

1. Install the necessary tools to run the script: `curl` (if needed)
2. Open a shell and enter this command:
```bash
bash <(curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.sh)
```
3. Go through the panel setup. The installer generates a **random username, password, and web base path** for you.
4. 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
bash <(curl -sSL https://get.docker.com)
```

### Using Docker Compose

3. Create a `panel` folder and go to it

```bash
mkdir panel
cd panel
```

4. Create and edit the `docker-compose.yml` file using `nano` or another editor. Insert the following contents:

`docker-compose.yml`
```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.

5. Start the container:

```bash
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:

```bash
docker compose --profile postgres up -d
```

6. 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:

```bash
docker compose down
docker compose pull
docker compose up -d
```

#### Delete

If you want to delete the container, execute:

```bash
docker compose down
docker system prune -a
rm panel -rf
```

### Using CLI

3. Execute the following command:

```bash
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:

```bash
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:

```bash
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:

```bash
docker container stop <container_id>
docker system prune -a
rm 3x-ui -rf
```

### Using development build

3. Install the `git` package using your package manager.

4. Clone the repository and go to its folder:

```bash
git clone https://github.com/MHSanaei/3x-ui.git
cd 3x-ui
```

5. Start the container with the command below. The repository's `docker-compose.yml` builds the image from source first and then starts the container:

```bash
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:
```bash
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`.

3. Go through the panel setup.

## Manual installation

To download the latest version of the compressed package directly to your server, run the following command:

```bash
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:

```bash
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.
