3X-UI is a web-based control panel for managing Xray-core servers. It's a Go application using Gin web framework with embedded static assets and SQLite database. The panel manages VPN/proxy inbounds, monitors traffic, and provides Telegram bot integration.
//go:embeddatabase/model/*gin.Context)Embedded Resources: All web assets (HTML, CSS, JS, translations) are embedded at compile time using embed.FS:
web/assets → assetsFSweb/html → htmlFSweb/translation → i18nFSDual Server Design: Main web panel + subscription server run concurrently, managed by web/global package
Xray Integration: Panel generates config.json for Xray binary, communicates via gRPC API for real-time traffic stats
Signal-Based Restart: SIGHUP triggers graceful restart. Critical: Always call service.StopBot() before restart to prevent Telegram bot 409 conflicts
Database Seeders: Uses HistoryOfSeeders model to track one-time migrations (e.g., password bcrypt migration)
# Build (creates bin/3x-ui.exe)
go run tasks.json → "go: build" task
# Run with debug logging
XUI_DEBUG=true go run ./main.go
# Or use task: "go: run"
# Test
go test ./...
The main.go accepts flags for admin tasks:
-reset - Reset all panel settings to defaults-show - Display current settings (port, paths)config.GetDBPath(), typically /etc/x-ui/x-ui.dbdatabase/model/model.go - Auto-migrated on startupHistoryOfSeeders to prevent re-running migrationsweb/service/tgbot.go (3700+ lines)telego library with long pollingservice.StopBot() before any server restart to prevent 409 bot conflictstelegohandler.BotHandler for routingi18nFS passed to bot startupServices inject dependencies (like xray.XrayAPI) and operate on GORM models:
type InboundService struct {
xrayApi xray.XrayAPI
}
func (s *InboundService) GetInbounds(userId int) ([]*model.Inbound, error) {
// Business logic here
}
Controllers use Gin context and inherit from BaseController:
func (a *InboundController) getInbounds(c *gin.Context) {
// Use I18nWeb(c, "key") for translations
// Check auth via checkLogin middleware
}
XUI_DEBUG, XUI_LOG_LEVEL, XUI_MAIN_FOLDERconfig/version, config/nameconfig.GetLogLevel(), config.GetDBPath() helpersweb/translation/translate.*.tomlI18nWeb(c, "pages.login.loginAgain") in controllerslocale.I18nType enum (Web, Api, etc.)xray-{os}-{arch}) to bin folderconfig.json dynamically from inbound/outbound settingsxray/process.goxray/api.go using google.golang.org/grpc{bin_folder}/xray-{os}-{arch}{bin_folder}/config.json{bin_folder}/geoip.dat, geosite.dat{log_folder}/3xipl.log, 3xipl-banned.logUses robfig/cron/v3 for periodic tasks:
xray_traffic_job.gocheck_cpu_usage.gocheck_client_ip_job.goldap_sync_job.goJobs registered in web/web.go during server initialization
Both install.sh and x-ui.sh follow these patterns:
$release variable (ubuntu, debian, centos, arch, etc.)is_port_in_use() using ss/netstat/lsof.service.debian, .service.arch, .service.rhel)Multi-stage Dockerfile:
DockerInit.sh to download Xray binary/usr/local/x-ui//etc/x-ui/x-ui.db/var/log/x-ui//etc/systemd/system/x-ui.service.*XUI_DEBUG=true for detailed loggingx-ui.sh script provides menu for status/logs3xipl.log for IP limit trackingHistoryOfSeeders tablegin-contrib/sessions with cookie store