[Русский](ИНСТРУКЦИЯ.md) · **English**

# HostinPL 5.6 · Complete guide

From an empty server to a hosting service that can take clients.

**Maintained and developed with [REDL.IO](https://redl.io) — AI-powered hosting.**

Contents:

1. [How it all fits together](#1-how-it-all-fits-together)
2. [Installing the panel](#2-installing-the-panel)
3. [Installing a game node](#3-installing-a-game-node)
4. [Connecting the node to the panel](#4-connecting-the-node-to-the-panel)
5. [Game server files](#5-game-server-files)
6. [Configuring the panel](#6-configuring-the-panel)
7. [Interface language](#7-interface-language)
8. [Captcha](#8-captcha)
9. [HTTPS and domain](#9-https-and-domain)
10. [Maintenance](#10-maintenance)
11. [Troubleshooting](#11-troubleshooting)

---

## 1. How it all fits together

Two roles, usually on separate machines:

**The panel** — the website, the database, client areas, tickets, payments. It does not need Docker
and runs happily on a cheap VPS.

**A game node (location)** — where the game servers themselves run. The panel connects to it
**over SSH** and creates a separate Docker container for every ordered server:

```
docker create --tty --rm --name=gs<ID> --network=host \
  --cpus="<cores>" --memory=<RAM>M \
  --volume="/home/gs<ID>/:/home/container/" \
  --workdir=/home/container debian:stretch
```

Hence two hard requirements for a node: **working Docker** and **a local image tagged
`debian:stretch`** (the name is hard-coded in the panel). Server files live on the node in
`/home/gs<ID>` and appear inside the container as `/home/container`.

You can have several nodes — they are added to the panel as separate locations, and the client
picks one when ordering.

> **Important about nodes.** Docker does not start inside container-based VPSes (LXC, OpenVZ and
> similar) because the kernel forbids nested namespaces. A node needs **a dedicated server or KVM**.
> The installer checks this first and warns you.

---

## 2. Installing the panel

Ubuntu 24.04 / 22.04 or Debian 12 / 13. Minimum: 1 core, 1 GB RAM, 10 GB disk.

```bash
apt-get update && apt-get install -y git
git clone https://github.com/RedlHosting/redl-gamepanel.git
cd redl-gamepanel
sudo bash install-panel.sh
```

The script asks three things:

* **the panel's domain or IP** — it goes into the configuration as the base address, and all links
  and e-mails are built from it;
* **the administrator e-mail** — also the login;
* **the administrator password** — at least 6 characters.

Everything after that is automatic: packages, a database with a random password, the schema
(27 tables), configuration, permissions, PHP, nginx, the scheduler, an autostart watchdog, an
administrator with full access, and a final check that the login page opens.

It takes 2–5 minutes. The panel address and login are printed at the end.

**A non-standard port** (if 80 is taken, or the panel sits behind a reverse proxy):

```bash
sudo PORT=8095 bash install-panel.sh
```

**Where things end up:**

| What | Path |
|------|------|
| Panel code | `/var/www/hostinpl` |
| Configuration | `/var/www/hostinpl/application/config.php` |
| Database password and scheduler token | `/root/.redl-panel-credentials` |
| nginx configuration | `/etc/nginx/sites-available/hostinpl` |
| Scheduler | `/etc/cron.d/hostinpl` |
| Autostart watchdog | `/usr/local/bin/hostinpl-guard` |
| Database | MariaDB, database `hostin` |

**phpMyAdmin** (optional; the panel links to it from the admin area):

```bash
sudo apt-get install -y phpmyadmin        # do NOT let it configure a web server, decline
sudo ln -sfn /usr/share/phpmyadmin /var/www/hostinpl/phpmyadmin
```

Log in with the database user from `/root/.redl-panel-credentials`. Remember that phpMyAdmin will be
reachable by anyone who knows the address: either restrict it by IP in nginx, or do not install it.

---

## 3. Installing a game node

You need **a dedicated server or KVM** (not LXC/OpenVZ). Minimum: 2 cores, 2 GB RAM, 100 GB disk,
counting 1–2 GB per game server.

```bash
apt-get update && apt-get install -y git
git clone https://github.com/RedlHosting/redl-gamepanel.git
cd redl-gamepanel
sudo bash install-node.sh
```

The script asks:

* **this node's IP** — how the panel will reach it;
* **whether to allow root SSH login with a password** — the panel can only connect with a login and
  password, and its commands require root. Answer `yes` and the script generates a strong root
  password and shows it at the end.

What it does: checks that the machine is suitable, installs Docker from the official repository,
builds the `debian:stretch` image (Debian 12 inside, plus 32-bit libraries, Java, Node.js and
screen), creates `/home/cp/gameservers/files` and the `gameservers` group, installs MariaDB for game
server databases, SteamCMD and ProFTPD, configures sshd, and installs a Docker watchdog.

The first run takes 5–15 minutes, mostly building the image.

**Close the ports immediately afterwards.** The script prints ready-made commands; substitute the
panel's address:

```bash
ufw allow from PANEL_IP to any port 22 proto tcp
ufw allow from PANEL_IP to any port 3306 proto tcp
ufw deny 3306
# leave the game ports open: 7777 (SA-MP), 22005 (RAGE:MP), 25565 (Minecraft) and so on
```

If you also connect to the node over SSH yourself, **allow your own IP first** or you will lock
yourself out:

```bash
ufw allow from YOUR_IP to any port 22 proto tcp
```

---

## 4. Connecting the node to the panel

1. Sign in to the panel as an administrator
2. Go to **Admin → Locations → Add location**
3. Fill in:

| Field | What to enter |
|-------|---------------|
| Name | Anything meaningful: "Frankfurt", "Helsinki" |
| IP | The node's IP |
| User | `root` |
| Password | The root password printed by `install-node.sh` |
| Games | Tick the games available on this node |

4. Save and open **Admin → Locations** — if the panel connected, the location shows its real cores,
   RAM and disk (refreshed hourly by the scheduler).

If it does not connect, check from the panel machine:

```bash
ssh root@NODE_IP                      # does the password work?
php -m | grep ssh2                    # is the php-ssh2 extension installed?
```

If the extension is missing: `apt-get install -y php-ssh2`, then restart PHP-FPM.

---

## 5. Game server files

**They are not included.** The original installer downloaded them from third-party sites over plain
HTTP with no signature checking, so we removed that (details in [SECURITY.en.md](SECURITY.en.md)).

Lay the files out on the node like this:

```
/home/cp/gameservers/files/<game_code>/
```

Game codes are listed under **Admin → Games** (for example `samp`, `crmp`, `mta`, `minecraft`,
`cs`, `css`, `ragemp`). When a server is created, the panel copies the contents of that directory
into `/home/gs<ID>/`.

Only take builds from primary sources: SA-MP from `sa-mp.com`, MTA from `mtasa.com`,
Minecraft (Paper) from `papermc.io`, RAGE:MP from `rage.mp`, Steam games via SteamCMD
(already installed in `/root/steamcmd`).

Then test the whole chain: order a server as a test client and confirm that it is created and starts.

---

## 6. Configuring the panel

**Admin → Settings**, four tabs:

* **General settings** — name, description, contacts, logo
* **Payment gateways** — Unitpay, Enot, AnyPay, LiteKassa, RoboKassa, FreeKassa, YooKassa, QIWI.
  You need your own merchant accounts. We never tested a gateway with a real payment — test each one
  with your own money before taking clients.
* **Other settings** — trial period, e-mail confirmation, **captcha**, maintenance mode, VK login
* **Information and bonuses** — top-up bonuses, referral percentages

Plans and games live under **Admin → Games** (versions, RAM and core limits, prices).

> When you save settings the panel rewrites `config.php` entirely. It matches configuration lines
> **by substring**, so do not create parameters whose names are contained in other names.
> After editing the file by hand, restart PHP-FPM **fully** (`restart`, not `reload`) — otherwise a
> cached copy is served.

---

## 7. Interface language

The panel speaks **Russian and English**. The language is chosen in this order:

1. `?lang=en` or `?lang=ru` in the URL — an explicit choice, remembered in a cookie for a year
2. the `lang` cookie — the previous choice
3. **the browser's `Accept-Language` header** — automatic detection
4. the `lang` value in `application/config.php` (default `ru`)

Automatic detection deliberately keeps Russian for Russian-speaking locales (ru, uk, be, kk and
others) and switches to English for everything else, so a visitor from Germany or Brazil gets the
English interface without touching anything.

**The switcher** sits in the top bar of the client area and the admin area (the `RU` / `EN` buttons
next to the balance) and in the footer of the login page.

**How it is built.** The panel was written with Russian text directly in the templates — about
1500 strings across 200 files. Instead of rewriting all of them, translation happens on the finished
response: `index.php` wraps the output in `ob_start()`, and before the page is sent to the browser
the strings are replaced using a dictionary (`engine/main/lang.php`). This covers the whole panel at
once, including the admin area and e-mails, and anything missing from the dictionary simply stays
Russian — nothing can vanish from the screen.

Replacement only happens on word boundaries, so a short dictionary key cannot corrupt a longer word.

**Adding your own language**, for example German:

```bash
cp panel/application/lang/en.php panel/application/lang/de.php
# translate the values in de.php, then add 'de' to Lang::AVAILABLE
# in panel/engine/main/lang.php
```

**Coverage.** The dictionary holds 1304 phrases and covers everything a person sees: the client area,
ordering, server management (console with all RCON commands, FTP, MySQL, firewall, scheduler,
mod auto-install), tickets, the whole FAQ, the entire admin area, and the e-mail templates. Coverage
is measured, not assumed — a script replays the replacement logic against the sources and reports
what would stay Russian; a live crawl of 45 pages in English mode confirms it. Four items remain
Russian on purpose: the "Русский" label of the switcher itself, the two game-server language codes
in the FAQ (`rus => Pyccĸий`, `ukr => Українська`), and a name-validation regular expression.

If you do spot an untranslated phrase, add it to `application/lang/en.php` — the key is the Russian
text exactly as it appears in the output. Two rules worth knowing:

* **never add a standalone function word** (a preposition or conjunction such as `в`). It gets
  substituted inside sentences that are not translated yet and produces mixed-language nonsense
  like "at поле имя пользователя". Translate such words as part of the whole phrase.
* if the phrase contains a PHP variable, use the static fragments around it as separate keys — the
  rendered text has real values in place of the variable and the full string would never match.

See the [screenshot gallery](docs/screenshots/README.md) for every section in both languages.

Translation only runs when the language is not Russian; in Russian mode the dictionary is not even
loaded, so there is no overhead.

---

## 8. Captcha

By default it is **off** — registration and login work without it, and the field is simply absent.

To enable it:

1. Get **reCAPTCHA v2 "I'm not a robot"** keys at
   [google.com/recaptcha/admin](https://www.google.com/recaptcha/admin) and specify the panel's domain
2. Go to **Admin → Settings → Other settings → "Bot protection (reCAPTCHA v2)"**
3. Paste the **Site key** and the **Secret key**
4. Switch to "Enabled" and save

**The order matters.** Enable the captcha without keys and the form will show
"ERROR for site owner: Invalid site key", locking everyone out. If that happens, open
`/var/www/hostinpl/application/config.php`, set `'captcha_enable' => '0'` and restart PHP-FPM:

```bash
sudo sed -i "s/'captcha_enable' => '1'/'captcha_enable' => '0'/" /var/www/hostinpl/application/config.php
sudo service php8.4-fpm restart     # use your PHP version
```

The captcha covers login, registration, password recovery and ticket creation — all four forms at once.

---

## 9. HTTPS and domain

The panel is installed over HTTP. For a certificate:

```bash
sudo apt-get install -y certbot python3-certbot-nginx
sudo certbot --nginx -d panel.example.com
```

After the certificate is issued, update the address in the configuration, otherwise links in
e-mails and redirects will stay on `http://`:

```bash
sudo sed -i "s|'url' => 'http://|'url' => 'https://|" /var/www/hostinpl/application/config.php
sudo service php8.4-fpm restart
```

If the panel sits behind a reverse proxy (Caddy, nginx, Cloudflare), its nginx configuration already
contains `absolute_redirect off; port_in_redirect off;` — without them redirects point at the
internal port and links such as `/phpmyadmin` do not open from outside.

---

## 10. Maintenance

**Backups.** Keep the database and the configuration:

```bash
mysqldump hostin > /root/hostin-$(date +%F).sql
cp /var/www/hostinpl/application/config.php /root/config-$(date +%F).php
```

Game server files live on the node in `/home/gs<ID>` — back those up on the node.

**Logs:**

| What to look at | Where |
|-----------------|-------|
| PHP and nginx errors | `/var/log/nginx/error.log` |
| Scheduler hits | `grep 'main/cron' /var/log/nginx/access.log` |
| Login journal | the `authlog` table |
| Node image build | `/tmp/redl-docker-build.log` |

**Health check:** **Admin → System check** — PHP extensions, versions, scheduler links. Every item
should be green.

**Updating the code** from the repository:

```bash
cd redl-gamepanel && git pull
sudo cp -a panel/application panel/engine /var/www/hostinpl/     # config.php is not overwritten
sudo chown -R www-data:www-data /var/www/hostinpl
sudo service php8.4-fpm restart
```

---

## 11. Troubleshooting

**"Error: could not load the controller …"** — the panel found no controller for that address.
Usually it is a link to something that does not exist, for example `/phpmyadmin` when phpMyAdmin is
not installed (see section 2). If the section really should exist, check that the files are in place
and owned by `www-data`, then restart PHP-FPM **fully**: opcache may have cached a file that was
caught mid-write.

**The login page is blank or has no form** — look at `/var/log/nginx/error.log`. A common cause is
`short_open_tag = Off`. The panel uses short `<? ?>` tags; the installer turns the option on, but you
have to restore it if you reinstall PHP.

**Cannot log in, the form shows "Invalid site key"** — the captcha is on without keys, see section 8.

**The scheduler is not running** (statistics not updating, expired servers not suspended):

```bash
grep -c 'main/cron' /var/log/nginx/access.log     # should grow
pgrep -a cron                                     # is the daemon running?
cat /etc/cron.d/hostinpl                          # every job must be on ONE line
```

If a job is split across two lines, the token got into the file with a newline — rewrite the file,
taking the token from `/root/.redl-panel-credentials`.

**A server is not created on the node** — check on the node:

```bash
docker info                                  # is the daemon up?
docker image inspect debian:stretch          # is the image there?
docker run --rm debian:stretch echo ok       # does a container start?
ls /home/cp/gameservers/files/               # are the game files in place?
```

**The server is created but dies immediately** — look at the container log on the node:
`docker logs gs<ID>`. Most often the 32-bit libraries are missing (rebuild the image from our
`docker/Dockerfile`) or the game build is damaged.

**Everything worked until a reboot** — on machines without systemd, the watchdogs on cron bring
things back. Check `pgrep -a cron` and run `/usr/local/bin/hostinpl-guard` by hand
(`gamenode-guard` on a node).

**A phrase in the interface is still Russian in English mode** — add it to
`panel/application/lang/en.php`; the key is the Russian text exactly as it appears on the page.

---

**Maintained and developed with [REDL.IO](https://redl.io) — AI-powered hosting.**
