Marzban Panel Setup: Complete Guide to the Modern Xray Management Panel

10 min read
Intermediate Marzban Xray Proxy Panel Censorship Guide

Prerequisites

  • A VPS with Ubuntu 22.04/24.04
  • A domain name (for TLS and subscription links)
  • Root/sudo access

Quick Answer: Install: sudo bash -c "$(curl -sL https://github.com/Gozargah/Marzban/raw/main/script.sh)" @ install. Access panel at https://your-ip:8443/dashboard. Create admin, add inbounds (VLESS+Reality, VMess+WS, etc.), create users, share subscription links. Marzban handles everything through a clean web UI.

Need a VPS? Vultr (free credit), DigitalOcean ($200 free credit), or RackNerd (cheap annual deals).


What Is Marzban?

Marzban is a modern, open-source proxy management panel built on top of Xray-core. It provides a clean web UI for managing proxy servers, users, and subscriptions — similar to 3X-UI but with a different architecture and feature set.

What It Does

  • Web dashboard — manage everything from a browser
  • Multi-protocol — VLESS, VMess, Trojan, Shadowsocks in one panel
  • Reality support — VLESS+Reality for maximum DPI resistance
  • User management — create users with data limits, expiry dates, connection limits
  • Subscription links — each user gets a URL that auto-updates their client configs
  • Multi-server (Nodes) — one panel controls multiple VPS servers
  • Telegram bot — create users, check stats, get notifications from your phone
  • REST API — automate everything programmatically
  • Docker-based — clean install, easy updates, no dependency conflicts

Marzban vs 3X-UI

Feature Marzban 3X-UI
Architecture Docker + API-first Single binary
UI Modern React dashboard Classic web UI
User management Centralized (one user = all protocols) Per-inbound users
Subscription links Built-in, auto-format for all clients Built-in
Multi-server Native "Node" system Manual setup
Telegram bot Built-in Built-in
Hysteria2 Supported (bundled binary) Supported (bundled binary)
API Full REST API Limited API
Install method Docker Compose Shell script
Resource usage Higher (Docker overhead) Lower
Best for Multi-server operators, API users Single server, simpler setups

Choose Marzban when: You manage multiple servers, need API access, want centralized user management across protocols, or prefer a modern UI.

Choose 3X-UI when: You run a single server, want lower resource usage, or prefer simpler setup.


Note: This guide covers Marzban v0.5+. Check your version with marzban --version.

How Marzban Works

Users (phone/laptop)
    |
    | Connect via proxy link or subscription
    |
    v
Marzban Panel (your VPS)
    ├── Xray-core (handles all proxy protocols)
    ├── Web Dashboard (manage users, view stats)
    ├── REST API (automation)
    ├── Subscription endpoint (auto-update client configs)
    └── Telegram bot (remote management)

Key concept: In Marzban, you create inbounds (protocol configurations like VLESS+Reality on port 443) and then create users who can connect to ANY inbound. This is different from 3X-UI where users are tied to specific inbounds.

Marzban approach:
  Inbound 1: VLESS+Reality :443
  Inbound 2: VMess+WS :2083      →  User "alice" can use ALL of these
  Inbound 3: Trojan+WS :2087     →  User "bob" can use ALL of these
  
3X-UI approach:
  Inbound 1: VLESS+Reality :443  →  alice1 (separate user)
  Inbound 2: VMess+WS :2083     →  alice2 (separate user for same person)

This makes user management much simpler when you run multiple protocols.


Part 1: Install Marzban

Prerequisites

# Update system
sudo apt update && sudo apt upgrade -y

# Install Docker (if not installed)
curl -fsSL https://get.docker.com | sh
sudo usermod -aG docker $USER

Install

sudo bash -c "$(curl -sL https://github.com/Gozargah/Marzban/raw/main/script.sh)" @ install

The installer:

  1. Downloads Marzban Docker images
  2. Sets up Docker Compose
  3. Creates config files at /opt/marzban/
  4. Starts the panel

Create Admin Account

marzban cli admin create --sudo

Enter a username and password. This is your panel login.

Access the Dashboard

Open your browser: https://YOUR_SERVER_IP:8443/dashboard

Accept the self-signed certificate warning (we'll set up proper SSL later).

Log in with the admin credentials you just created.


Part 2: Get a TLS Certificate

For production use, you need a real SSL certificate:

# Install certbot
sudo apt install certbot -y

# Get certificate (stop Marzban first to free port 80)
marzban down
sudo certbot certonly --standalone -d panel.yourdomain.com
marzban up

Configure Marzban to Use the Certificate

Edit /opt/marzban/.env:

UVICORN_SSL_CERTFILE=/etc/letsencrypt/live/panel.yourdomain.com/fullchain.pem
UVICORN_SSL_KEYFILE=/etc/letsencrypt/live/panel.yourdomain.com/privkey.pem

Important: You also need to add the certificate path as a volume mount in Marzban's docker-compose.yml:

volumes:
  - /etc/letsencrypt:/etc/letsencrypt:ro

Then restart:

marzban restart

Auto-renew certificates (they expire in 90 days):

(crontab -l 2>/dev/null; echo '0 3 * * * certbot renew --quiet --deploy-hook "cd /opt/marzban && docker compose restart"') | crontab -

Now access at https://panel.yourdomain.com:8443/dashboard with a valid certificate.


Part 3: Configure Inbounds (Protocols)

Inbounds define which proxy protocols your server offers. Go to Dashboard → Settings → Inbound Settings (or edit the Xray config directly).

VLESS + Reality (Recommended — Best DPI Resistance)

This is the most censorship-resistant TCP protocol. Traffic looks like a legitimate HTTPS connection to a real website.

In Dashboard → Xray Configuration, add this inbound:

{
  "tag": "VLESS-Reality",
  "listen": "0.0.0.0",
  "port": 443,
  "protocol": "vless",
  "settings": {
    "clients": [],
    "decryption": "none"
  },
  "streamSettings": {
    "network": "tcp",
    "tcpSettings": {},
    "security": "reality",
    "realitySettings": {
      "show": false,
      "dest": "www.yahoo.com:443",
      "xver": 0,
      "serverNames": ["www.yahoo.com"],
      "privateKey": "YOUR_PRIVATE_KEY",
      "shortIds": ["abcd1234"]
    }
  },
  "sniffing": {
    "enabled": true,
    "destOverride": ["http", "tls", "quic"]
  }
}

Generate Reality keys:

docker exec marzban-marzban-1 xray x25519
# Output: Private key and Public key
# Use Private key in server config
# Users need Public key in their client

VLESS + WebSocket (For Cloudflare CDN)

Use this when you want to hide your server IP behind Cloudflare:

{
  "tag": "VLESS-WS",
  "listen": "0.0.0.0",
  "port": 2083,
  "protocol": "vless",
  "settings": {
    "clients": [],
    "decryption": "none"
  },
  "streamSettings": {
    "network": "ws",
    "wsSettings": {
      "path": "/vless-ws"
    },
    "security": "none"
  },
  "sniffing": {
    "enabled": true,
    "destOverride": ["http", "tls"]
  }
}

Then put Cloudflare in front with orange cloud enabled. Cloudflare handles TLS.

For finding clean Cloudflare IPs: cfray Guide

VMess + WebSocket (Legacy — Wide Client Support)

{
  "tag": "VMess-WS",
  "listen": "0.0.0.0",
  "port": 2087,
  "protocol": "vmess",
  "settings": {
    "clients": []
  },
  "streamSettings": {
    "network": "ws",
    "wsSettings": {
      "path": "/vmess-ws"
    },
    "security": "none"
  }
}

Trojan + WebSocket

{
  "tag": "Trojan-WS",
  "listen": "0.0.0.0",
  "port": 2096,
  "protocol": "trojan",
  "settings": {
    "clients": []
  },
  "streamSettings": {
    "network": "ws",
    "wsSettings": {
      "path": "/trojan-ws"
    },
    "security": "none"
  }
}

Shadowsocks 2022

{
  "tag": "Shadowsocks",
  "listen": "0.0.0.0",
  "port": 8388,
  "protocol": "shadowsocks",
  "settings": {
    "clients": [],
    "network": "tcp,udp",
    "method": "2022-blake3-aes-128-gcm"
  }
}

Open Firewall

sudo ufw allow 443/tcp      # VLESS+Reality
sudo ufw allow 2083/tcp     # VLESS+WS (CDN)
sudo ufw allow 2087/tcp     # VMess+WS
sudo ufw allow 2096/tcp     # Trojan+WS
sudo ufw allow 8388/tcp     # Shadowsocks
sudo ufw allow 8443/tcp     # Marzban dashboard

Part 4: User Management

Create a User

Dashboard → Users → Add User:

Field Description
Username Unique identifier (e.g., "alice")
Data Limit Total bandwidth quota (e.g., 10 GB, 50 GB, unlimited)
Expiry Date When access expires
Protocols Which inbounds the user can access (all by default)

Click Create — the user gets a unique UUID that works across all configured inbounds.

Subscription Link

Every user automatically gets a subscription URL:

https://panel.yourdomain.com:8443/sub/USER_TOKEN

Share this with the user. They paste it into their client app (v2rayNG, Hiddify, etc.) and all proxy configs auto-download and auto-update.

Subscription formats supported:

  • V2Ray Base64 (v2rayNG, v2rayN)
  • Clash YAML (Clash Meta, Clash Verge)
  • sing-box JSON (Hiddify, sing-box)
  • Outline (Shadowsocks)

The client auto-detects the correct format.

Bulk User Creation

# Via CLI
marzban cli user create --username alice --data-limit 10GB --expire 30d
marzban cli user create --username bob --data-limit 50GB --expire 90d

# Via API (for automation)
curl -X POST https://panel.yourdomain.com:8443/api/user \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"username": "charlie", "data_limit": 10737418240, "expire": 1798761600}'

User Monitoring

The dashboard shows for each user:

  • Current data usage vs limit
  • Days remaining until expiry
  • Active connections (online/offline)
  • Last connection time
  • Per-protocol usage breakdown

Part 5: Telegram Bot

Marzban has a built-in Telegram bot for remote management.

Setup

  1. Create a bot with @BotFather on Telegram — get the bot token
  2. Edit /opt/marzban/.env:
TELEGRAM_API_TOKEN=your-bot-token-here
TELEGRAM_ADMIN_ID=your-telegram-user-id

Get your Telegram user ID from @userinfobot.

  1. Restart: marzban restart

Bot Commands

Command What It Does
Create user Interactive wizard with limits
User info Data usage, expiry, status
Delete user Remove access
System status Server load, connections, bandwidth
User list All users with stats

The bot also sends automatic notifications:

  • User expired
  • User hit data limit
  • Server down/up
  • High resource usage

Part 6: Multi-Server (Nodes)

Marzban's killer feature: run one panel that controls multiple VPS servers.

Marzban Panel (main VPS)
    |
    ├── Node 1 (VPS in Germany)
    ├── Node 2 (VPS in Netherlands)
    └── Node 3 (VPS in US)
    
Users connect to ANY node using the same subscription link.
If one node gets blocked, they automatically switch to another.

Add a Node

On the Node VPS:

# Install Marzban-node
sudo bash -c "$(curl -sL https://github.com/Gozargah/Marzban-node/raw/main/script.sh)" @ install

On the Panel:

  1. Dashboard → Nodes → Add Node
  2. Enter the Node server IP and port
  3. Copy the certificate from the panel to the node
  4. The node connects and starts accepting traffic

Why Nodes Matter

  • Redundancy — if one server gets blocked, others still work
  • Geographic diversity — servers in different countries
  • Load distribution — spread traffic across servers
  • One subscription — users get all nodes in one link, clients auto-select the best

Part 7: Cloudflare CDN Integration

Hide your server IP behind Cloudflare:

Setup

  1. Add your domain to Cloudflare (free plan)
  2. Create A record pointing to your VPS IP — orange cloud ON
  3. Use VLESS+WS or VMess+WS inbound (NOT Reality — Reality doesn't work with CDN)
  4. Set Cloudflare SSL to Full

Ports

Cloudflare only proxies these HTTPS ports:

  • 443, 2053, 2083, 2087, 2096, 8443

Your WS inbounds should use one of these ports.

Clean IPs

If Cloudflare IPs are throttled in your region, use cfray to find working ones. Users set the clean IP in their client instead of the domain.


Part 8: Configuration Files

Main Config: /opt/marzban/.env

# Dashboard
UVICORN_HOST=0.0.0.0
UVICORN_PORT=8443

# SSL
UVICORN_SSL_CERTFILE=/etc/letsencrypt/live/panel.yourdomain.com/fullchain.pem
UVICORN_SSL_KEYFILE=/etc/letsencrypt/live/panel.yourdomain.com/privkey.pem

# Subscription
XRAY_SUBSCRIPTION_URL_PREFIX=https://panel.yourdomain.com:8443

# Telegram
TELEGRAM_API_TOKEN=your-bot-token
TELEGRAM_ADMIN_ID=your-telegram-id

# Database (default: SQLite, can use MySQL)
# SQLALCHEMY_DATABASE_URL = mysql+pymysql://user:pass@localhost/marzban

Xray Config: /opt/marzban/xray_config.json

This is the full Xray configuration with inbounds, outbounds, and routing. Edit through the dashboard or directly.


Part 9: Marzban CLI

marzban up          # Start
marzban down        # Stop
marzban restart     # Restart
marzban logs        # View logs
marzban update      # Update to latest version

# Admin management
marzban cli admin create --sudo
marzban cli admin list
marzban cli admin delete USERNAME

# User management
marzban cli user create --username NAME
marzban cli user list
marzban cli user delete USERNAME
marzban cli user info USERNAME

Part 10: Security Hardening

Change Dashboard Port

The default 8443 is widely known. Change it in .env:

UVICORN_PORT=9443

Restrict Dashboard Access

Add firewall rules to only allow your IP:

# Only allow your IP to access the dashboard
sudo ufw allow from YOUR_IP to any port 9443

Use Fortify

Fortify auto-detects Marzban and Xray:

bash <(curl -sL https://github.com/SamNet-dev/fortify/raw/main/install.sh)
fortify

Block Abuse

Add to Xray routing in the dashboard to block spam and torrents:

{
  "type": "field",
  "outboundTag": "block",
  "port": "25,465,587"
}

Part 11: Backup and Restore

Backup

# Backup everything
tar czf marzban-backup-$(date +%Y%m%d).tar.gz /opt/marzban/

# Or just the database (most important)
cp /opt/marzban/db.sqlite3 /backups/marzban-db-$(date +%Y%m%d).sqlite3

Restore

marzban down
tar xzf marzban-backup-20260408.tar.gz -C /
marzban up

Automate

# Daily backup at 3 AM
(crontab -l 2>/dev/null; echo "0 3 * * * cp /opt/marzban/db.sqlite3 /backups/marzban-db-\$(date +\%Y\%m\%d).sqlite3") | crontab -

Troubleshooting

# Check status
marzban logs

# Check Xray specifically
docker logs marzban-marzban-1 2>&1 | tail -30

# Check if ports are open
ss -tlnp | grep -E "443|2083|2087|8443"

# Test Xray config
docker exec marzban-marzban-1 xray run -test -c /etc/xray/config.json
Problem Fix
Can't access dashboard Check firewall: ufw status. Check port: `ss -tlnp \ grep 8443`
Users can't connect Check inbound config. Check firewall opens the right ports
Certificate error Cert expired. Renew: certbot renew. Restart: marzban restart
Subscription not updating Check XRAY_SUBSCRIPTION_URL_PREFIX in .env matches your domain
High memory usage Normal for Docker. Minimum 1GB RAM recommended
Node disconnected Check node logs: marzban-node logs. Check network between panel and node
Telegram bot not working Verify bot token and admin ID in .env. Restart after changes
"Xray failed to start" Config syntax error. Check logs for the exact JSON error

Related Guides

Related Tools