A Guide to Hardware
The complete operating manual for the AlwayzPlayzZ VPS bot: a Discord-controlled fleet of resource-limited Linux users, a web dashboard, and a browser-based terminal gateway — all surviving on a single Railway container that was never meant to run a hosting business. This is the real documentation, rebuilt from the actual incident log, not a template.
Complete Deployment Guide
Every real chapter, in the real order things had to happen. No filler chapters, no "Stage N" duplicates — this is the actual path from an empty GitHub repo to a working VPS bot on a platform that was never designed to host one.
01 GitHub — Fork, Clone, Push
Railway deploys from a repository, not from files pasted into a chat. Start by getting the project into your own GitHub account:
# Fork the repo on github.com first, then:
git clone https://github.com/<you>/redesigned-telegram.git
cd redesigned-telegram
# make your changes...
git add .
git commit -m "configure bot for my server"
git push origin main
Once pushed, go to Railway → New → GitHub Repo and pick this repository — not "Deploy a Template."
Templates spin up unrelated pre-built services (we lost an afternoon to a stray ttyd
template service that had nothing to do with our bot).
02 Railway — Project, Service, Builder
- Create a new Railway project.
- Add a service from your GitHub repo.
- Go to Settings → Build and explicitly set Builder: Dockerfile — Railway's auto-detected
Nixpacks builder won't include
useradd,su,socat, orttyd, and the bot will crash the moment it tries to create a VPS user. - Go to Settings → Networking → Generate Domain to get a public URL and have Railway inject the
PORTvariable automatically — this is what ttyd binds to.
03 Persistent Volume — the step everyone forgets
Railway wipes the container filesystem on every redeploy. Without a volume, every VPS user, every home directory, and the entire SQLite database vanish the moment you push a new commit.
- Open the service → Settings → Volumes.
- Click + New Volume.
- Mount path:
/data. - Point
VPS_DB_PATHandVPS_HOME_ROOT(below) at paths under/dataso both the database and the Linux home directories actually live on it.
Important nuance: even with the volume, the Linux user accounts themselves
(/etc/passwd entries) are NOT on the volume — they reset on every redeploy. The
bot's startup recovery pass re-creates missing users automatically, but their home directory contents survive
only because the home directories themselves live on /data.
04 Environment Variables — every single one, documented
| Variable | Required | Default | What it does / common mistake |
|---|---|---|---|
DISCORD_TOKEN | required | — | Your bot's token from the Developer Portal. Regenerate it immediately if it's ever pasted anywhere public. |
MAIN_ADMIN_ID | required | — | Your Discord user ID (numbers only). Leaving this blank crashes the bot at import time with ValueError: invalid literal for int(). |
VPS_USER_ROLE_ID | optional | — | A Discord role ID. Set to 0 if you don't have one — it's also parsed with int() and will crash if blank. |
BOT_NAME | optional | UnixNodes | Cosmetic — shows in embeds and generated Linux usernames. |
PREFIX | optional | ! | Discord command prefix. |
VPS_DB_PATH | required for persistence | vps.db | Set to /data/vps.db or the whole VPS list is forgotten on every redeploy. |
VPS_HOME_ROOT | required for persistence | /home | Set to /data/home for the same reason. |
PORT | auto-set by Railway | 8080 | ttyd binds here. Set automatically once you generate a public domain — don't hardcode it. |
DASHBOARD_PORT | optional | 2026 | Only relevant if you run the separate dashboard process — needs its own exposed port/domain, distinct from ttyd's. |
DASHBOARD_USERNAME / DASHBOARD_PASSWORD | required if dashboard enabled | — | Seeds the first dashboard admin account on first boot. |
VPS_DEFAULT_PASSWORD | optional | root | Initial Linux password set for new VPS users before they change it with !changepwd. |
05 Dockerfile — every line explained
FROM python:3.11-slim
RUN apt-get update && apt-get install -y --no-install-recommends \
passwd procps socat sqlite3 sudo rsync tar coreutils curl \
&& rm -rf /var/lib/apt/lists/*
passwd is what actually provides useradd,
userdel, and su — Debian slim doesn't include
it by default. socat powers port forwarding. sqlite3
is needed by the ttyd gateway script to validate one-time access codes.
RUN curl -fsSL -o /usr/local/bin/ttyd \
https://github.com/tsl0922/ttyd/releases/latest/download/ttyd.x86_64 \
&& chmod +x /usr/local/bin/ttyd
ttyd is pulled as a static binary instead of via apt, since it isn't reliably
packaged for Debian slim and an apt-get install ttyd will just fail the build.
06 start.sh — process supervision
#!/bin/sh
set -eu
echo "[START] Starting bot.py..."
exec python -u /app/bot.py
The exec matters: it replaces the shell process with Python instead of
running it as a child, so Railway's signals (stop/restart) reach the bot directly. If you re-enable the
dashboard, start exactly one instance of each process — the dashboard must never spawn its own copy of
bot.py via subprocess, or you get two live
Discord Gateway connections answering every command twice.
07 dashboard.py — routing and authentication
The dashboard is a plain http.server.ThreadingHTTPServer, cookie-session
authenticated, running as its own OS process. It imports bot.py as a module
purely to reuse its database helpers (get_db(), vps_data)
— importing a module never starts the Discord connection, since bot.run() is
guarded by if __name__ == "__main__":. Because it's a separate process, it does
not share memory with the live bot — it only shares the SQLite file, so it must re-read state from disk
on every request rather than trusting an in-memory copy from whenever it started.
08 bot.py — the actual VPS lifecycle
Each "VPS" is a real Linux user (useradd -m -s /bin/bash) with an anchor
process (su - user -c "sleep infinity") keeping a live session open. Creation,
port forwarding, and stats collection are all plain shell commands run through one central
execute_host() function, which works identically whether the target is the
local machine or a remote node-agent.
09 ttyd — why not SSH
tmate needs an outbound connection to its public relay server (ssh.tmate.io)
— Railway blocks that. ttyd flips the direction: it serves one shared web terminal on Railway's own public
inbound port, and a tiny gateway script asks for a one-time access code generated per SSH request in
Discord before dropping the user into their own Linux shell. No outbound connections required anywhere.
10 Linux Users — restrictions and reality
Usernames must stay under 32 characters and avoid characters Linux's useradd
rejects — a raw Discord snowflake ID plus a counter can blow past that limit, so container names are built from
a shortened suffix of the user ID. Every VPS user shares one kernel, one process table, and one filesystem
root — this is not a security boundary, it's resource-tracked shell hosting.
11 SQLite — the whole database schema
Tables: vps (one row per VPS, resources, status, owner), nodes
(local + remote hosts), port_forwards (host↔vps port pairs plus the socat PIDs
forwarding them), access_codes (one-time ttyd login codes with an expiry), and
settings (thresholds, config). WAL mode is enabled so the dashboard process and
the bot process can read/write concurrently without locking each other out.
12 VPS Lifecycle
Create → useradd + optional cgroup limits + anchor process spawned.
Start → anchor process relaunched if not already running, port forwards verified.
Stop → pkill -u <user>, no data loss.
Suspend → same as stop, plus a flag blocking the owner from restarting it themselves.
Resume → suspend flag cleared, anchor restarted.
Delete → processes killed, userdel -r removes the account and home
directory, DB rows purged.
Developer Journal
A real, chronological account of what actually broke, in the order it actually broke.
Error Database
Every real error hit during this deployment. Search by keyword, error text, or vibe.
Practical Lab
Paste a real log line and the built-in troubleshooting engine will diagnose it against every pattern encountered during this project.
Waiting for input.
Paste a log on the left, or click one of the example chips to see it work.
Command Reference
Every real bot command, what it needs, and what happens under the hood.
System Architecture
Hover any node for details.
Repository
Canonical source for this project.
ClassyFX-Pro / redesigned-telegram
github.com/ClassyFX-Pro/redesigned-telegram
Clone
git clone https://github.com/ClassyFX-Pro/redesigned-telegram.git
cd redesigned-telegram
Branch workflow
git checkout -b feature/my-change
# make changes
git add .
git commit -m "describe the change"
git push origin feature/my-change
# open a Pull Request on GitHub
Connect to Railway
Railway → New → GitHub Repo → select redesigned-telegram → Settings → Build →
set Builder to Dockerfile. Pushing to the connected branch triggers an automatic redeploy.
Secrets
This page is intentionally weird. Things are hidden. Some of them are real.
Known frequencies
↑ ↑ ↓ ↓ ← → ← → B A does something.
Typing sudo make ubuntu work anywhere on this page is a bad idea.
Click the memorial stone below. It remembers.
Typing vm2 lives anywhere brings it back. Briefly. Against its will.
Click the logo in the header seven times fast. Telemetry was not meant to be seen.
Hold the letter U for three full seconds. Ubuntu remembers everything.
Triple-click the word "Railway" in the footer. It will not go well.
Reveal all 100 fragments below to unlock the ability to leave one of your own.
🪦 In Memory of VM 2
"It never got a persistent volume. We hardly knew it."
Achievements Unlocked
None yet. Try things.
Fragment Archive 0 / 100 revealed
100 sealed fragments recovered from the deployment. Click one to break the seal. No two are the same — that was the whole point of rebuilding this page.