# How to Implement Multi-Port Inbound with Different Protocols on Xray-Core

> Learn to implement multi-port inbound with different protocols on Xray-core. Configure separate inbound entries for diverse protocol handling and unique port ranges in your Xray-core setup.

- Repository: [Project X Community, Not Porn-jet X Hub/Xray-core](https://github.com/XTLS/Xray-core)
- Tags: how-to-guide
- Published: 2026-04-21

---

**You can run multiple inbound handlers on a single Xray-core instance by defining separate inbound entries in your configuration, each with unique tags, distinct port ranges in `receiver_settings`, and protocol-specific `proxy_settings` blocks.**

Xray-core supports running multiple proxy protocols simultaneously on different ports through its modular inbound handler architecture. Each listening endpoint consists of **ReceiverSettings** (defining where to listen) and **ProxySettings** (defining what protocol to run), managed independently by the inbound manager in [`app/proxyman/inbound/inbound.go`](https://github.com/XTLS/Xray-core/blob/main/app/proxyman/inbound/inbound.go). This allows a single Xray-core binary to handle VMess on port 443, VLESS on port 8443, and Shadowsocks on port 8388 simultaneously.

## Understanding the Inbound Architecture

Xray-core treats every listening endpoint as a discrete **inbound handler**. Understanding the relationship between receiver configuration and proxy configuration is essential for implementing multi-port setups.

### ReceiverConfig and Port Allocation

The **ReceiverConfig** (defined in `app/proxyman/config.proto`) specifies the network binding parameters:

- **listen**: The IP address to bind (e.g., `"0.0.0.0"` for all interfaces)
- **port_list**: A `PortList` message supporting single ports or ranges
- **stream_settings**: TLS, XTLS, or transport layer configuration

According to the source code in [`app/proxyman/inbound/always.go`](https://github.com/XTLS/Xray-core/blob/main/app/proxyman/inbound/always.go), the `AlwaysOnInboundHandler` iterates over every port in the `PortList` and creates independent workers (implementations of `tcpWorker`, `udpWorker`, or `dsWorker`) for each.

### ProxySettings and Protocol Selection

The **ProxySettings** (e.g., `vmess.inbound.Config`, `vless.inbound.Config`) define the protocol implementation and authentication:

- **type**: Protocol identifier (`"vmess"`, `"vless"`, `"trojan"`, `"shadowsocks"`, etc.)
- **users**: Authentication credentials specific to the protocol

Each inbound handler combines one `ReceiverConfig` with one `ProxySettings` message. Because these are paired one-to-one, you must define separate inbound entries to run different protocols on different ports.

## Configuration Examples

Xray-core supports JSON, YAML, TOML, and Protobuf configurations. Below are practical examples showing VMess on port 443 and VLESS on port 8443.

### JSON Configuration

```json
{
  "inbound": [
    {
      "tag": "vmess-443",
      "receiver_settings": {
        "@type": "pipe",
        "listen": "0.0.0.0",
        "port_list": {
          "range": [
            {
              "from": 443,
              "to": 443
            }
          ]
        },
        "sniffing_settings": {
          "enabled": true,
          "dest_override": ["http", "tls"]
        }
      },
      "proxy_settings": {
        "@type": "vmess",
        "users": [
          {
            "id": "11111111-1111-1111-1111-111111111111",
            "alterId": 64,
            "level": 0,
            "email": "user@example.com"
          }
        ]
      }
    },
    {
      "tag": "vless-8443",
      "receiver_settings": {
        "@type": "pipe",
        "listen": "0.0.0.0",
        "port_list": {
          "range": [
            {
              "from": 8443,
              "to": 8443
            }
          ]
        }
      },
      "proxy_settings": {
        "@type": "vless",
        "users": [
          {
            "id": "22222222-2222-2222-2222-222222222222",
            "encryption": "none",
            "level": 0,
            "email": "vless@example.com"
          }
        ]
      }
    }
  ],
  "outbound": [
    {
      "tag": "direct",
      "protocol": "freedom",
      "settings": {}
    }
  ]
}

```

### YAML Configuration

```yaml
inbound:
  - tag: vmess-443
    receiver_settings:
      type: pipe
      listen: 0.0.0.0
      port_list:
        range:
          - from: 443
            to: 443
      sniffing_settings:
        enabled: true
        dest_override:
          - http
          - tls
    proxy_settings:
      type: vmess
      users:
        - id: 11111111-1111-1111-1111-111111111111
          alterId: 64
          level: 0
          email: user@example.com

  - tag: vless-8443
    receiver_settings:
      type: pipe
      listen: 0.0.0.0
      port_list:
        range:
          - from: 8443
            to: 8443
    proxy_settings:
      type: vless
      users:
        - id: 22222222-2222-2222-2222-222222222222
          encryption: none
          level: 0
          email: vless@example.com

outbound:
  - tag: direct
    protocol: freedom
    settings: {}

```

**Critical configuration notes:**

- The `tag` field must be unique for each inbound handler; it serves as the key in `inbound.Manager`'s handler map.
- The `type: pipe` in `receiver_settings` resolves to `proxyman.ReceiverConfig`.
- A single `ReceiverConfig` cannot host multiple protocols; each protocol requires its own inbound entry.

## Internal Handler Wiring

Understanding how Xray-core processes your configuration helps debug multi-port setups.

### Handler Creation Flow

When Xray starts, the following sequence occurs according to [`app/proxyman/inbound/inbound.go`](https://github.com/XTLS/Xray-core/blob/main/app/proxyman/inbound/inbound.go):

1. **Config parsing**: `core.LoadConfig` unmarshals the configuration into `core.Config` (defined in [`core/config.pb.go`](https://github.com/XTLS/Xray-core/blob/main/core/config.pb.go)), which contains a slice of `InboundHandlerConfig` structs.
2. **Handler instantiation**: `proxyman.NewHandler(ctx, cfg)` (line 55) extracts:
   - `receiverSettings` as `*proxyman.ReceiverConfig`
   - `proxySettings` as `*serial.TypedMessage`
3. **Object creation**: The function calls `common.CreateObject` to instantiate the protocol-specific inbound handler (e.g., VMess or VLESS).
4. **Registration**: The handler is registered in `inbound.Manager` using its tag.

### Worker Generation and Port Binding

The `NewAlwaysOnInboundHandler` function in [`app/proxyman/inbound/always.go`](https://github.com/XTLS/Xray-core/blob/main/app/proxyman/inbound/always.go) (lines 27-66) manages the actual network listeners:

- Iterates through the `PortList` in the `ReceiverConfig`
- For each port, creates protocol-specific workers (`tcpWorker`, `udpWorker`, or `dsWorker` for domain sockets)
- Each worker calls `net.ListenTCP` or `net.ListenUDP` independently
- All workers share the same `proxy.Inbound` instance but maintain separate socket references

When `Manager.Start()` executes (lines 8-25 of [`inbound.go`](https://github.com/XTLS/Xray-core/blob/main/inbound.go)), every registered handler starts its workers, resulting in independent listeners on the configured ports.

## Practical Implementation Tips

- **Port ranges for single protocols**: If you need a wide port range for the same protocol, use a single `ReceiverConfig` with multiple ranges in `port_list`. This is more efficient than creating separate inbound entries.
  
- **Protocol-specific stream settings**: When different protocols require different TLS certificates or transport settings (WebSocket, gRPC, etc.), define separate inbound entries each with their own `stream_settings` under `receiver_settings`.

- **Debugging listener startup**: Enable debug logging (`loglevel: debug`) to verify worker creation. The `errors.LogDebug` calls in [`always.go`](https://github.com/XTLS/Xray-core/blob/main/always.go) (lines 31-33) output the specific address and port each worker binds to.

- **Tag uniqueness**: Duplicate tags cause registration failures in `inbound.Manager`. Always use descriptive, unique tags like `"vmess-tcp-443"` or `"trojan-ws-8443"`.

## Summary

- **Multi-port inbound** requires separate inbound entries in your Xray-core configuration, each with unique tags.
- **ReceiverSettings** (`type: pipe`) control where the server listens, supporting single ports or ranges via `port_list`.
- **ProxySettings** determine the protocol (VMess, VLESS, Trojan, etc.) and authentication for that specific listener.
- The **inbound manager** in [`app/proxyman/inbound/inbound.go`](https://github.com/XTLS/Xray-core/blob/main/app/proxyman/inbound/inbound.go) registers each handler, while `NewAlwaysOnInboundHandler` in [`always.go`](https://github.com/XTLS/Xray-core/blob/main/always.go) creates independent workers for every specified port.
- A single inbound entry cannot host multiple protocols; each protocol requires its own handler configuration.

## Frequently Asked Questions

### Can I run multiple protocols on the same port?

No. A single `ReceiverConfig` can only bind one protocol implementation. To handle multiple protocols on the same port (e.g., multiplexing TLS and non-TLS on 443), you must use a multiplexing transport like `splithttp` or place a reverse proxy (such as Nginx or HAProxy) in front of Xray-core to route traffic based on SNI or ALPN before it reaches the Xray listeners.

### How do I configure a port range for one protocol?

Specify a range in the `port_list` field under `receiver_settings`. For example, setting `from: 10000` and `to: 10100` creates 101 separate workers listening on ports 10000 through 10100, all using the same protocol configuration. This is handled efficiently by the `AlwaysOnInboundHandler` without requiring 101 separate inbound entries.

### Why does my multi-port configuration fail to start?

The most common causes are duplicate `tag` values (each inbound must have a unique tag) or port conflicts where another process is already bound to the specified port. Check the debug logs for errors from `inbound.Manager` or `NewAlwaysOnInboundHandler` regarding handler registration or `net.Listen` failures.

### Can different inbound handlers share the same outbound routing?

Yes. The `tag` in inbound configuration is used for routing decisions, but multiple inbounds can route to the same outbound tag. Define your outbound handlers (e.g., `"direct"`, `"blocked"`, or a remote server) once, then reference the appropriate outbound tag in your routing rules based on the inbound tag or domain matching.