- Go 96.5%
- Dockerfile 2.8%
- Shell 0.7%
| cmd/xrayserver | ||
| internal | ||
| .dockerignore | ||
| .env.example | ||
| .gitignore | ||
| docker-compose.yml | ||
| docker-entrypoint.sh | ||
| Dockerfile | ||
| go.mod | ||
| go.sum | ||
| README.md | ||
| servers.json | ||
xrayserver
Server proxy berbasis Go yang membungkus xray-core sebagai library in-process,
dengan dukungan multi-route round-robin (atau random / leastPing) terhadap
banyak server Xray (VLESS / VMess / Trojan / Shadowsocks).
Tiap koneksi yang masuk ke inbound lokal (SOCKS5 / HTTP) akan dirutekan ke salah satu outbound oleh balancer bawaan Xray, sehingga path data tetap memakai engine Xray yang sudah teruji untuk beban tinggi.
Fitur
- Multi-route balancer:
roundRobin(default),random,leastPing(leastPingmengaktifkan observatory + probe URL otomatis). - Inbound SOCKS5 dan/atau HTTP, dengan auth opsional.
- Parser URL
vless://,vmess://,trojan://,ss://(termasuk Reality:pbk,sid,fp,sni,flow=xtls-rprx-vision). - Admin API ringan:
/healthz,/info,/metrics(format Prometheus). - Tuning runtime:
GOMAXPROCS=NumCPU,GOGC=50, TCP keepalive sockopt, buffer per-koneksi, sniffing untuk routing yang akurat. - Graceful shutdown via
SIGINT/SIGTERM.
Struktur
cmd/xrayserver/main.go entry point
internal/config loader servers.json
internal/proxyurl parser vless/vmess/trojan/ss
internal/xrayconf generator konfigurasi xray-core
internal/runner wrapper core.Instance (load via JSON resmi xray)
internal/adminapi HTTP health / metrics
servers.json contoh konfigurasi
Build
Butuh Go 1.23 atau lebih baru.
go mod tidy
go build -o xrayserver.exe ./cmd/xrayserver
Untuk Linux:
GOOS=linux GOARCH=amd64 go build -o xrayserver ./cmd/xrayserver
Docker
Project ini sudah include Dockerfile (multi-stage, distroless-style Alpine,
non-root, CGO disabled karena pakai modernc.org/sqlite murni Go).
Build & run langsung:
docker build -t xrayserver:latest .
mkdir -p data
echo "ADMIN_PASSWORD=changeme" > data/.env
docker run -d --name xrayserver \
-p 3245:3245 -p 9090:9090 \
-p 10808:10808 -p 10808:10808/udp -p 10809:10809 \
-v "$(pwd)/data:/data" \
-v "$(pwd)/servers.json:/app/servers.json:ro" \
--ulimit nofile=1048576:1048576 \
--restart unless-stopped \
xrayserver:latest
Atau pakai docker compose:
mkdir -p data
echo "ADMIN_PASSWORD=changeme" > data/.env
docker compose up -d --build
Volume ./data menampung:
xrayserver.db(SQLite, sumber data tunggal daftar server).env(berisiADMIN_PASSWORD)
servers.json dimount read-only sebagai konfigurasi runtime (listen, balancer,
log, policy). Untuk mengganti password admin: edit data/.env lalu
docker compose restart.
Port yang dibuka:
3245— web admin (login + dashboard)9090— admin API (/healthz,/info,/metrics,/stats)10808— SOCKS5 proxy (TCP + UDP)10809— HTTP proxy
Konfigurasi (servers.json)
{
"listen": {
"socks": { "address": "0.0.0.0", "port": 10808, "udp": true },
"http": { "address": "0.0.0.0", "port": 10809 }
},
"balancer": {
"strategy": "roundRobin",
"probeUrl": "https://www.google.com/generate_204",
"probeIntervalSec": 60
},
"api": { "address": "127.0.0.1", "port": 9090 },
"log": {
"level": "info",
"format": "text",
"file": "",
"access": "",
"error": "",
"statsIntervalSec": 30,
"logPerRoute": true
},
"policy": {
"handshakeSec": 4,
"connIdleSec": 600,
"uplinkOnlySec": 5,
"downlinkOnlySec": 10,
"bufferSizeKB": 512
},
"servers": [
"vless://uuid@host1:443?type=tcp&security=reality&sni=cloudflare.com&fp=chrome&pbk=...&sid=...&flow=xtls-rprx-vision#node-1",
"vless://uuid@host2:443?type=tcp&security=reality&sni=cloudflare.com&fp=chrome&pbk=...&sid=...&flow=xtls-rprx-vision#node-2",
"trojan://password@host3:443?security=tls&sni=example.com#node-3"
]
}
Pilihan balancer.strategy:
roundRobin(default) — bagi rata per koneksi.random— pilih outbound acak.leastPing— pilih outbound dengan latensi terendah berdasarkan probe keprobeUrlsetiapprobeIntervalSecdetik.
Auth opsional pada inbound:
"socks": {
"address": "0.0.0.0", "port": 10808, "udp": true,
"auth": { "user": "alice", "pass": "secret" }
}
Menjalankan
.\xrayserver.exe -c servers.json
Lalu set client (browser / curl / aplikasi) ke:
- SOCKS5:
127.0.0.1:10808 - HTTP:
127.0.0.1:10809
Verifikasi cepat:
curl -x socks5h://127.0.0.1:10808 https://ifconfig.me
curl -x http://127.0.0.1:10809 https://ifconfig.me
Setiap request akan keluar lewat outbound berikutnya sesuai strategi balancer.
Web admin (port 3245)
Server menyediakan dashboard admin di http://127.0.0.1:3245. Password disimpan
di file .env:
ADMIN_PASSWORD=changeme
Halaman:
GET /login— form login (password).GET /— dashboard tabelid, server, ip, country, latency, expired_at, is_active.GET /logout— hapus session.
REST API (JSON, perlu cookie session):
| Method | Path | Aksi |
|---|---|---|
| GET | /api/servers |
List semua server |
| POST | /api/servers |
Tambah server {url, expiredAt, active} |
| PATCH | /api/servers/{id} |
Aktif/nonaktif {active: bool} |
| DELETE | /api/servers/{id} |
Hapus server |
| POST | /api/check/{id} |
Cek 1 server (IP, country, latency) |
| POST | /api/check-all |
Cek semua server paralel |
Setiap perubahan (create/delete/setActive) memicu reload xray-core otomatis, sehingga balancer langsung menggunakan daftar terbaru tanpa restart proses.
Database (SQLite — sumber data tunggal)
Daftar server proxy disimpan di SQLite (xrayserver.db), bukan di JSON.
servers.json hanya menyimpan konfigurasi runtime: listen, balancer, log, policy.
Field servers di servers.json opsional dan hanya dipakai sebagai seed
pertama kali — saat tabel servers di DB masih kosong, isinya akan diimpor.
Setelah itu boleh dihapus / dikosongkan.
Path DB bisa diubah lewat flag -db (default xrayserver.db).
Skema tabel servers:
id INTEGER PK
url TEXT (vless://, vmess://, trojan://, ss://)
label TEXT (dari fragment URL #...)
ip TEXT (hasil checker)
country TEXT (kode ISO 2 huruf, dari ip-api.com)
latency_ms INTEGER (TCP dial latency, -1 jika belum / gagal)
expired_at TIMESTAMP (opsional)
is_active INTEGER (0/1)
checked_at TIMESTAMP
created_at TIMESTAMP
Saat DB kosong dan tidak ada seed
Server tetap start: inbound SOCKS/HTTP listen normal, tapi seluruh trafik
diarahkan ke outbound block. Begitu Anda menambah server lewat web admin,
xray-core langsung di-reload dan trafik mulai mengalir.
Checker
Untuk setiap server:
- Resolve hostname → IP (IPv4 dulu, fallback IPv6).
- TCP dial ke
IP:portdengan timeout 5 detik → catat latency (ms). - IP → country lewat
http://ip-api.com/json/(best-effort, gratis tanpa key).
Hasil disimpan ke kolom ip, country, latency_ms, checked_at.
Flag CLI
-c path config (default: servers.json)
-env path .env (default: .env)
-db path SQLite (default: xrayserver.db)
-web address:port web admin (default: :3245)
Logging
Logger aplikasi memakai log/slog (text default, bisa json). Yang dilog:
- Startup: identitas tiap
outbound(tag, label dari URL#fragment, alamat, network, security). - Inbound listener (SOCKS / HTTP) saat siap menerima koneksi.
- Status balancer (strategy + jumlah outbound).
- Tiap interval
statsIntervalSec: ringkasan total bytes inbound, plus rincian per-outbound (bytes total + rate B/s) jikalogPerRoute=true. Ini yang akan memvisualisasikan distribusi round-robin. - Sinyal shutdown.
Selain itu, log internal Xray-core (access log) akan mencatat tiap koneksi
masuk dengan tujuan dan outbound mana yang dipilih balancer. Konfigurasi:
"log": {
"level": "info", // debug | info | warn | error
"format": "text", // text | json
"file": "logs/app.log", // opsional, file untuk app log (selain stdout)
"access": "", // ""=stdout, "none"=matikan, atau path file
"error": "", // ""=stdout, "none"=matikan, atau path file
"statsIntervalSec": 30, // 0 untuk matikan sampler
"logPerRoute": true // log distribusi per outbound saat tick
}
Contoh baris log saat berjalan:
time=... level=INFO msg="server registered" tag=out-0 label=node-uk protocol=vless address=uk.host:443
time=... level=INFO msg="balancer ready" strategy=roundRobin outbounds=2
2026/.../[Info] app/dispatcher: taking detour [out-0] for [tcp:example.com:443]
time=... level=INFO msg="traffic summary" inboundUp="1.20 MiB" inboundDown="14.50 MiB" intervalSec=30
time=... level=INFO msg="route traffic" outbound=out-0 up="612.0 KiB" down="7.20 MiB" upRate="20.4 KiB/s" downRate="245.8 KiB/s"
time=... level=INFO msg="route traffic" outbound=out-1 up="610.5 KiB" down="7.30 MiB" upRate="20.3 KiB/s" downRate="249.3 KiB/s"
Policy (timeout & buffer)
Mengontrol lifetime koneksi dan ukuran buffer. Default sudah dinaikkan untuk mendukung long-lived connection (WebSocket, streaming, polling panjang):
"policy": {
"handshakeSec": 4, // timeout handshake awal
"connIdleSec": 600, // idle 10 menit sebelum ditutup (default Xray 300)
"uplinkOnlySec": 5, // timeout saat hanya upload tanpa download
"downlinkOnlySec": 10, // timeout saat hanya download tanpa upload
"bufferSizeKB": 512 // buffer per koneksi (KiB)
}
Untuk use case tertentu:
- WebSocket / SSE: naikkan
connIdleSecke1800(30 menit) atau lebih. - Streaming video: naikkan
downlinkOnlySecke30. - Upload file besar: naikkan
uplinkOnlySecke30. - Throughput tinggi: naikkan
bufferSizeKBke1024atau2048(trade-off: konsumsi RAM per koneksi naik).
Penanganan beban tinggi
- Engine data path adalah
xray-core(concurrency native Go, non-blocking I/O). GOMAXPROCSdi-set ke jumlah CPU;GOGC=50agar GC lebih agresif saat banyak koneksi pendek.sockopt.tcpKeepAliveInterval=30di outbound mencegah idle stuck.policy.levels.0.bufferSize=512(KiB) memberi buffer cukup besar tanpa memboroskan memori per koneksi.- Sniffing diaktifkan agar routing akurat untuk HTTPS/QUIC.
Untuk produksi disarankan:
- Naikkan
ulimit -n(Linux) atau gunakan service yang mengangkat handle limit. - Jalankan di belakang reverse proxy / firewall jika port di-expose publik.
- Aktifkan auth pada inbound SOCKS/HTTP bila bukan di jaringan tepercaya.
Catatan
- Format URL VLESS Reality yang Anda contohkan didukung penuh, termasuk
parameter
flow=xtls-rprx-vision,pbk,sid,sni,fp. - Konfigurasi map yang dibangun oleh
internal/xrayconfdi-marshal ke JSON lalu dimuat memakai loader resmi Xray (infra/conf/serial.LoadJSONConfig), jadi seluruh validasi schema Xray tetap berjalan.