- Docker & Docker Compose (recommended) OR Go 1.25+
- 3x-ui panel (https://github.com/MHSanaei/3x-ui) — up and running
- Telegram Bot Token from @BotFather
- Minimum resources: 1 CPU core, 128MB RAM, 1GB disk (for up to 10k users)
Send a message to @userinfobot on Telegram to get your ID.
- Create a VLESS+Reality+Vision inbound in your 3x-ui panel
- Note the inbound ID (shown in the inbounds list, usually
1) - Make sure the panel API is accessible from the bot host:
Should return JSON with
curl -H "Authorization: Bearer <api_token>" http://your-panel-ip:2053/panel/api/server/statussuccess: true. The<api_token>is the panel API token generated in 3x-ui Security settings; in production it is stored in thenodestable (nodes.api_token).
mkdir -p rs8kvn_bot
cd rs8kvn_botcp .env.example .env
# Edit .env with your values — see Configuration table below
nano .env # or use your editorImportant: Set at minimum:
TELEGRAM_BOT_TOKEN=your_bot_token_here
TELEGRAM_ADMIN_ID=123456789
GLOBAL_SUB_URL=https://vpn.example.com/sub/Node configuration (panel host, API token, inbound IDs) is stored in the nodes DB table — see Adding Nodes via SQL below.
mkdir -p data
chmod 755 data# Pull from GitHub Container Registry
docker pull ghcr.io/kereal/rs8kvn_bot:latest
# Run container
docker run -d \
--name rs8kvn_bot \
--restart unless-stopped \
--security-opt no-new-privileges:true \
-v $(pwd)/.env:/app/.env:ro \
-v $(pwd)/data:/app/data \
-p 127.0.0.1:8880:8880 \
ghcr.io/kereal/rs8kvn_bot:latestdocker logs -f rs8kvn_botLook for: Bot started successfully
Open Telegram, start the bot: /start
docker stop rs8kvn_bot
docker start rs8kvn_botCreate docker-compose.yml:
version: '3.8'
services:
rs8kvn_bot:
image: ghcr.io/kereal/rs8kvn_bot:latest
container_name: rs8kvn_bot
restart: unless-stopped
# Security: Run as non-root user (matches UID/GID in Dockerfile)
user: "1000:1000"
volumes:
# Read-only config file
- ./.env:/app/.env:ro
# Persistent data directory (must be writable)
- ./data:/app/data
environment:
- TZ=Europe/Moscow
# Go runtime memory optimization (optional, already set in Dockerfile)
- GOMEMLIMIT=67108864
- GOGC=40
# Expose health check port for monitoring (optional)
ports:
- "127.0.0.1:8880:8880"
# Security hardening
security_opt:
- no-new-privileges:true
# Health check - verify application is ready
healthcheck:
test: ["CMD-SHELL", "wget -q -O - http://127.0.0.1:8880/healthz > /dev/null"]
interval: 30s
timeout: 10s
retries: 3
start_period: 30s
# Resource limits for production
deploy:
resources:
limits:
cpus: "0.5"
memory: 128M
reservations:
cpus: "0.1"
memory: 32M
# Logging
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
# Graceful shutdown
stop_grace_period: 90s
stop_signal: SIGTERMRun:
docker-compose up -d
docker-compose logs -f# Clone repository
git clone https://github.com/kereal/rs8kvn_bot.git
cd rs8kvn_bot
# Install dependencies
go mod download
# Copy and configure environment
cp .env.example .env
nano .env # set required vars
# Build
go build -ldflags="-s -w -X main.version=$(git describe --tags --abbrev=0 2>/dev/null || echo dev) -X main.commit=$(git rev-parse --short HEAD) -X main.buildTime=$(date -u +'%Y-%m-%dT%H:%M:%SZ')" -o rs8kvn_bot ./cmd/bot
# Run
./rs8kvn_botOr run directly without building:
go run ./cmd/botgo install github.com/air-verse/air@latest
airAir will automatically rebuild and restart the bot when you save changes to Go files.
| Variable | Description | Default | Required | Notes |
|---|---|---|---|---|
| Telegram | ||||
TELEGRAM_BOT_TOKEN |
Bot token from @BotFather | — | ✅ | Format: 123456:ABC-def... |
TELEGRAM_ADMIN_ID |
Admin Telegram ID for notifications | 0 |
✅ | Must be positive; get from @userinfobot |
CONTACT_USERNAME |
Support Telegram username | (empty) | ❌ | Without @ |
| Subscription Server | ||||
GLOBAL_SUB_URL |
Base URL for subscription links | — | ✅ | Constructed as GLOBAL_SUB_URL + <sub_id> (e.g. https://vpn.example.com/sub/abc123); must be valid http/https URL, HTTPS in production |
SUBSERVER_ACCESS_LOG |
/sub/{id} access log file path |
(empty) | ❌ | Set empty to disable; space-separated line with quote-wrapped values; startup continues if the file cannot be opened |
| Database | ||||
DATABASE_PATH |
SQLite database file path | ./data/rs8kvn.db |
❌ | Directory must exist |
| Logging | ||||
LOG_FILE_PATH |
Log file path | ./data/bot.log |
❌ | Rotated automatically |
LOG_LEVEL |
Log level | info |
❌ | debug, info, warn, error |
| Monitoring | ||||
HEARTBEAT_URL |
URL for heartbeat POST (optional) | — | ❌ | Receives {} every 5 min; must be valid http/https URL if set |
HEARTBEAT_INTERVAL |
Heartbeat interval (seconds) | 300 |
❌ | Min 10s |
SENTRY_DSN |
Sentry DSN for error tracking | — | ❌ | https://sentry.io/...; must be valid http/https URL if set |
WEB_SERVER_PORT |
HTTP (web) server port | 8880 |
❌ | 1–65535 |
| Trial & Referral | ||||
SITE_URL |
Base URL for landing pages | https://vpn.site |
❌ | Must be valid http/https URL; used in Telegram links |
TRIAL_DURATION_HOURS |
Trial subscription duration | 3 |
❌ | 1–168 hours (7 days max) |
TRIAL_RATE_LIMIT |
Max trial requests per IP per hour | 3 |
❌ | 1–100 |
| Payments | ||||
PAYMENT_ENABLED |
Enable Platega payment buttons and webhook | false |
❌ | Requires merchant ID and secret when true |
PAYMENT_PROVIDER |
Payment provider | platega |
❌ | Only platega is supported |
PLATEGA_MERCHANT_ID |
Platega merchant ID | (empty) | Required when enabled | Sent as X-MerchantId |
PLATEGA_SECRET |
Platega webhook/API secret | (empty) | Required when enabled | Sent as X-Secret; keep private |
| Donation | ||||
DONATE_CARD_NUMBER |
Donation card (T-Bank) | (empty) | ❌ | Shown in donate menu |
DONATE_URL |
Donation collection link | (empty) | ❌ | T-Bank or other |
DONATE_ENABLED |
Show "☕ Донат" button in main menu | true |
❌ | true / false; when false the "💎 Premium" button is lifted directly under "📋 Подписка" |
For free clients, the bot sends and preserves the following panel fields:
{"reset":30,"resetDay":0,"resetMax":0,"trafficReset":"monthly","trafficResetDay":1}This keeps renewal rolling every 30 days (resetDay=0), allows unlimited renewals (resetMax=0), and resets traffic on the first day of each month. Trials are sent with reset=0 and are not renewed. Existing clients do not need to be recreated; update their settings once in the panel. The bot retains a fallback that repairs an expired free-client expiry during synchronization.
Nodes are managed through the nodes table. To add a new node, insert a row and link it to a plan:
-- Add a 3x-ui node
INSERT INTO nodes (name, is_active, host, api_token, inbound_ids, subscription_url, type)
VALUES ('main', 1, 'http://panel:2053', 'your-token', '[1]', 'http://panel:2053/sub/', '3x-ui');
-- Add a proxman node
INSERT INTO nodes (name, is_active, host, api_token, inbound_ids, subscription_url, type)
VALUES ('proxman1', 1, 'http://proxman:8080', 'your-token', '[]', 'http://proxman:8080/sub/', 'proxman');
-- Add a fetch node (read-only HTTP source, no API token needed)
INSERT INTO nodes (name, is_active, host, api_token, inbound_ids, subscription_url, type)
VALUES ('external', 1, '', '', '[]', 'https://external-source.com/raw-proxy', 'fetch');
-- Link node to a plan (replace IDs as needed)
INSERT INTO plan_nodes (plan_id, node_id) VALUES (1, <node_id>);Node types:
| Type | Host | API Token | Subscription URL | Description |
|---|---|---|---|---|
3x-ui |
Required | Required | http://panel/sub/ |
Full CRUD via 3x-ui API |
proxman |
Required | Required | http://proxman/sub/ |
Webhook create/delete |
fetch |
Empty | Empty | https://source/raw |
Read-only HTTP fetch, URL used as-is |
GLOBAL_SUB_URLmust use HTTPS in production — subscription links are distributed to users and must not leak over plain HTTP. The URL is validated to be a well-formedhttporhttpsURL..envfile should have permissions600(readable only by owner)- Never commit
.envto version control
# Health check
curl http://localhost:8880/healthz
# Expected: JSON with status "ok" and a components.database object with status "ok"
# Bot logs
docker logs rs8kvn_bot | tail -20- Open Telegram
- Start bot:
/start - Click "📥 Получить подписку"
- Should receive subscription link + QR code
Set TELEGRAM_ADMIN_ID in .env to your Telegram user ID.
Admin-only commands:
/lastreg— last 10 subscribers/del <id>— delete subscription by DB ID/setplan <subscription_id> <plan_id> [days]— change subscription plan (reconciles VPN nodes; ifdaysomitted, keeps a future expiry or applies a 30-day default)/broadcast <msg>— message all users/send <id|@username> <msg>— private message/refstats— referral statistics
- Backup database:
cp data/rs8kvn.db data/rs8kvn.db.backup - Pull new image or rebuild from source — embedded migrations run automatically
- Update
.env:- New required:
GLOBAL_SUB_URL(base URL for subscription links) - New optional:
SUBSERVER_ACCESS_LOG(subscription access log) - Removed:
SUB_EXTRA_SERVERS_ENABLED,SUB_EXTRA_SERVERS_FILE(no longer used)
- New required:
- Restart bot. Node configuration lives in the
nodestable — add nodes via SQL (see Adding Nodes via SQL).
Breaking changes:
GLOBAL_SUB_URLis now required. Without it the bot will not start.- Extra servers config file (
SUB_EXTRA_SERVERS_FILE) feature has been removed.
- Backup database:
cp data/rs8kvn.db data/rs8kvn.db.backup - Pull new image — migrations run automatically
- Update
.env:- New optional:
TRIAL_DURATION_HOURS
- New optional:
- Restart bot
# Stop and remove container
docker stop rs8kvn_bot && docker rm rs8kvn_bot
# Remove image
docker rmi ghcr.io/kereal/rs8kvn_bot:latest
# Remove data (⚠️ THIS DELETES ALL SUBSCRIPTIONS)
rm -rf ./data
# Remove .env
rm .envIssues: https://github.com/kereal/rs8kvn_bot/issues
Documentation: doc/handover.md, doc/operations.md
Before reporting:
- Check logs:
docker logs rs8kvn_bot - Verify
.envsettings (especiallyGLOBAL_SUB_URL) - Test 3x-ui connectivity:
curl -H "Authorization: Bearer <api_token>" http://your-panel-ip:2053/panel/api/server/status - Include the bot version shown in the startup logs
For architecture details, see handover.md.