Установка в /opt и доступ к панели по сети

Установщик по умолчанию раскладывает всё в /opt/pzmanager вместо домашнего
каталога пользователя: серверные файлы, миры, бэкапы и конфиг лежат рядом
одной понятной структурой, а системному пользователю больше не нужен
домашний каталог и shell.

Адрес панели задаётся при установке (PZ_LISTEN), каталог и пользователь —
через PZ_HOME и PZ_USER. Конфиг создаётся только при первой установке,
поэтому повторный запуск установщика обновляет бинарник, не трогая
настройки, миры и учётки.

Адрес теперь занимается до автозапуска игрового сервера: если IP ещё не
поднят или порт занят, менеджер падает сразу с внятной ошибкой, а не
оставляет работающий сервер PZ без панели управления.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-04 19:18:13 +03:00
co-authored by Claude Opus 5
parent fe517cfb71
commit 29782dcce9
4 changed files with 147 additions and 46 deletions
+48 -19
View File
@@ -47,7 +47,7 @@ systemd ──> pzmanager ──> start-server.sh ──> java (GameServer)
## Быстрый старт ## Быстрый старт
Собрать бинарник (нужен Go 1.24+): Собрать бинарник (нужен Go 1.24+; собирать можно на любой ОС, включая Windows):
```bash ```bash
make build-linux make build-linux
@@ -59,8 +59,30 @@ make build-linux
sudo ./deploy/install.sh sudo ./deploy/install.sh
``` ```
Установщик заведёт пользователя `pzserver`, поставит зависимости (SteamCMD, Установщик заведёт системного пользователя `pzserver`, поставит зависимости
JRE, SDL2), положит бинарник в `/usr/local/bin` и включит systemd-юнит. (SteamCMD, JRE, SDL2), положит бинарник в `/usr/local/bin`, создаст структуру
в `/opt/pzmanager` и включит systemd-юнит:
```
/opt/pzmanager/
├── config.yaml настройки менеджера
├── data/ учётки панели и скачанный SteamCMD
├── server/ серверные файлы Project Zomboid
├── Zomboid/ миры, конфиги и логи игры
└── backups/ архивы
```
Куда ставить и на каком адресе слушать, задаётся переменными окружения:
```bash
# панель доступна в локальной сети по адресу машины
sudo PZ_LISTEN=10.10.1.142:8080 ./deploy/install.sh
# другой каталог и пользователь
sudo PZ_HOME=/srv/pz PZ_USER=zomboid ./deploy/install.sh
```
По умолчанию панель слушает `127.0.0.1:8080` — только с самой машины.
Дальше: Дальше:
@@ -70,29 +92,30 @@ JRE, SDL2), положит бинарник в `/usr/local/bin` и включи
journalctl -u pzmanager -n 30 --no-pager journalctl -u pzmanager -n 30 --no-pager
``` ```
2. Откройте панель. По умолчанию она слушает только `127.0.0.1:8080`, так что 2. Откройте панель в браузере и создайте администратора, введя этот код.
зайти на неё можно через SSH-туннель: Если панель слушает localhost, пробросьте порт:
```bash ```bash
ssh -L 8080:127.0.0.1:8080 пользователь@сервер ssh -L 8080:127.0.0.1:8080 пользователь@сервер
``` ```
Затем откройте <http://127.0.0.1:8080> и создайте администратора, введя код
из журнала.
3. Во вкладке «Обзор» нажмите **Установить / обновить через SteamCMD** и 3. Во вкладке «Обзор» нажмите **Установить / обновить через SteamCMD** и
дождитесь окончания — прогресс виден во вкладке «Консоль». дождитесь окончания — прогресс виден во вкладке «Консоль».
4. Запустите сервер. При первом запуске PZ создаст `~/Zomboid/Server/*.ini` — 4. Запустите сервер. При первом запуске PZ создаст `Zomboid/Server/*.ini` —
после этого станут доступны вкладки «Конфиг сервера», «Песочница» и «Моды». после этого станут доступны вкладки «Конфиг сервера», «Песочница» и «Моды».
Не забудьте открыть игровые порты: Порты, которые нужно открыть:
```bash ```bash
sudo ufw allow 16261/udp sudo ufw allow 8080/tcp # веб-панель, если она слушает не localhost
sudo ufw allow 16262/udp sudo ufw allow 16261/udp # игровой порт
sudo ufw allow 16262/udp # второй игровой порт
``` ```
Обновление ставится поверх: соберите новый бинарник и повторите
`sudo ./deploy/install.sh` — конфиг, миры и учётки останутся на месте.
## Моды ## Моды
Project Zomboid хранит моды в двух строках конфига: `WorkshopItems` — что Project Zomboid хранит моды в двух строках конфига: `WorkshopItems` — что
@@ -160,6 +183,11 @@ Mod ID, который нужен серверу, лежит внутри сам
**SSH-туннель (по умолчанию)** — ничего настраивать не нужно, панель слушает **SSH-туннель (по умолчанию)** — ничего настраивать не нужно, панель слушает
только localhost. только localhost.
**Доверенная локальная сеть** — `PZ_LISTEN=<ip>:8080` при установке или
`listen` в конфиге. Учтите, что HTTP не шифруется: пароль от панели идёт по
сети открытым текстом, так что для доступа через интернет этот вариант не
годится.
**HTTPS через reverse-proxy** — если панель нужна с телефона. Настройте nginx с **HTTPS через reverse-proxy** — если панель нужна с телефона. Настройте nginx с
сертификатом (например, от Let's Encrypt): сертификатом (например, от Let's Encrypt):
@@ -200,14 +228,15 @@ pzmanager users # список пользователей
## Конфигурация ## Конфигурация
Файл `~/.config/pzmanager/config.yaml` создаётся при первом запуске. Всё, что в Файл `/opt/pzmanager/config.yaml` создаёт установщик (при запуске без него
нём есть, правится и через вкладку «Настройки». менеджер возьмёт `~/.config/pzmanager/config.yaml`). Всё, что в нём есть,
правится и через вкладку «Настройки».
```yaml ```yaml
listen: 127.0.0.1:8080 # адрес веб-панели listen: 127.0.0.1:8080 # адрес веб-панели; 0.0.0.0 или IP — доступ по сети
data_dir: /home/pzserver/.local/share/pzmanager data_dir: /opt/pzmanager/data
server_dir: /home/pzserver/pzserver # куда SteamCMD ставит сервер server_dir: /opt/pzmanager/server # куда SteamCMD ставит сервер
zomboid_dir: /home/pzserver/Zomboid # миры, конфиги и логи игры zomboid_dir: /opt/pzmanager/Zomboid # миры, конфиги и логи игры
steamcmd_path: "" # пусто — найти в PATH или скачать steamcmd_path: "" # пусто — найти в PATH или скачать
profiles: # серверы: у каждого свой мир и настройки profiles: # серверы: у каждого свой мир и настройки
- id: vanilla # vanilla -> vanilla.ini, Saves/Multiplayer/vanilla - id: vanilla # vanilla -> vanilla.ini, Saves/Multiplayer/vanilla
@@ -224,7 +253,7 @@ autorestart: true # поднимать сервер после п
stop_timeout: 1m30s # сколько ждать после quit до SIGTERM stop_timeout: 1m30s # сколько ждать после quit до SIGTERM
log_buffer_lines: 5000 # строк консоли в памяти log_buffer_lines: 5000 # строк консоли в памяти
backup: backup:
dir: /home/pzserver/pzbackups dir: /opt/pzmanager/backups
schedule: 6h # 0 — отключить автобэкапы schedule: 6h # 0 — отключить автобэкапы
keep: 20 # архивов на профиль keep: 20 # архивов на профиль
stop_server: false # останавливать сервер на время бэкапа stop_server: false # останавливать сервер на время бэкапа
+11 -2
View File
@@ -8,6 +8,7 @@ import (
"errors" "errors"
"flag" "flag"
"fmt" "fmt"
"net"
"net/http" "net/http"
"os" "os"
"os/signal" "os/signal"
@@ -147,6 +148,15 @@ func serve(cfg *config.Config, users *auth.Store, secureCookies bool) error {
IdleTimeout: 120 * time.Second, IdleTimeout: 120 * time.Second,
} }
// Адрес занимаем до автозапуска игрового сервера: если его не поднять
// (IP ещё не назначен, порт занят), лучше упасть сразу, чем оставить
// работающий сервер PZ без панели управления.
listener, err := net.Listen("tcp", cfg.Listen)
if err != nil {
return fmt.Errorf("не удалось занять адрес %s: %w", cfg.Listen, err)
}
fmt.Printf("pzmanager слушает http://%s\n", cfg.Listen)
if cfg.AutoStart { if cfg.AutoStart {
if err := supervisor.Start(); err != nil { if err := supervisor.Start(); err != nil {
supervisor.AppendLog("manager", "Автозапуск не удался: "+err.Error()) supervisor.AppendLog("manager", "Автозапуск не удался: "+err.Error())
@@ -155,8 +165,7 @@ func serve(cfg *config.Config, users *auth.Store, secureCookies bool) error {
errCh := make(chan error, 1) errCh := make(chan error, 1)
go func() { go func() {
fmt.Printf("pzmanager слушает http://%s\n", cfg.Listen) if err := httpServer.Serve(listener); err != nil && !errors.Is(err, http.ErrServerClosed) {
if err := httpServer.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) {
errCh <- err errCh <- err
} }
}() }()
+82 -19
View File
@@ -1,13 +1,21 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# Установка pzmanager на Ubuntu. Запускать от root: # Установка pzmanager на чистый Ubuntu/Debian. Запускать от root:
#
# sudo ./deploy/install.sh # sudo ./deploy/install.sh
# #
# Скрипт: заводит системного пользователя, кладёт бинарник в /usr/local/bin, # Переменные окружения:
# ставит systemd-юнит и зависимости, нужные серверу Project Zomboid. # PZ_HOME куда ставить всё хозяйство (по умолчанию /opt/pzmanager)
# PZ_USER системный пользователь (по умолчанию pzserver)
# PZ_LISTEN адрес веб-панели (по умолчанию 127.0.0.1:8080)
# BINARY путь к собранному бинарнику (по умолчанию ./pzmanager)
#
# Пример — панель доступна в локальной сети по адресу машины:
# sudo PZ_LISTEN=10.10.1.142:8080 ./deploy/install.sh
set -euo pipefail set -euo pipefail
PZ_USER="${PZ_USER:-pzserver}" PZ_USER="${PZ_USER:-pzserver}"
PZ_HOME="${PZ_HOME:-/home/${PZ_USER}}" PZ_HOME="${PZ_HOME:-/opt/pzmanager}"
PZ_LISTEN="${PZ_LISTEN:-127.0.0.1:8080}"
BINARY="${BINARY:-./pzmanager}" BINARY="${BINARY:-./pzmanager}"
UNIT_SRC="$(dirname "$0")/pzmanager.service" UNIT_SRC="$(dirname "$0")/pzmanager.service"
@@ -23,27 +31,69 @@ fi
echo "==> Устанавливаю системные зависимости" echo "==> Устанавливаю системные зависимости"
apt-get update -qq apt-get update -qq
# lib32gcc-s1 нужен SteamCMD, остальное — самому серверу PZ. # lib32gcc-s1 нужен SteamCMD, остальное — самому серверу PZ. Свою JRE сервер
# приносит с собой, системная нужна как запасной вариант.
apt-get install -y --no-install-recommends \ apt-get install -y --no-install-recommends \
ca-certificates curl lib32gcc-s1 libsdl2-2.0-0 openjdk-17-jre-headless ca-certificates curl lib32gcc-s1 libsdl2-2.0-0 openjdk-17-jre-headless
if ! id "$PZ_USER" &>/dev/null; then if ! id "$PZ_USER" &>/dev/null; then
echo "==> Создаю пользователя $PZ_USER" echo "==> Создаю пользователя $PZ_USER"
adduser --system --group --home "$PZ_HOME" --shell /bin/bash "$PZ_USER" adduser --system --group --home "$PZ_HOME" --shell /usr/sbin/nologin "$PZ_USER"
fi fi
echo "==> Копирую бинарник в /usr/local/bin/pzmanager" echo "==> Копирую бинарник в /usr/local/bin/pzmanager"
install -m 0755 "$BINARY" /usr/local/bin/pzmanager install -m 0755 "$BINARY" /usr/local/bin/pzmanager
echo "==> Готовлю директории" echo "==> Готовлю директории в $PZ_HOME"
install -d -o "$PZ_USER" -g "$PZ_USER" -m 0755 \ install -d -o "$PZ_USER" -g "$PZ_USER" -m 0755 \
"$PZ_HOME/.config/pzmanager" \ "$PZ_HOME" \
"$PZ_HOME/.local/share/pzmanager" \ "$PZ_HOME/data" \
"$PZ_HOME/pzserver" \ "$PZ_HOME/server" \
"$PZ_HOME/pzbackups" "$PZ_HOME/Zomboid" \
"$PZ_HOME/backups"
# Конфиг создаётся только при первой установке: обновление не должно
# затирать настройки, которые уже поправили руками или через панель.
CONFIG="$PZ_HOME/config.yaml"
if [[ -f "$CONFIG" ]]; then
echo "==> Конфиг $CONFIG уже есть — оставляю как есть"
else
echo "==> Создаю конфиг $CONFIG (панель будет слушать $PZ_LISTEN)"
cat > "$CONFIG" <<EOF
# Адрес веб-панели. 127.0.0.1 — только через SSH-туннель,
# 0.0.0.0 — на всех интерфейсах, конкретный IP — только на нём.
listen: ${PZ_LISTEN}
data_dir: ${PZ_HOME}/data # учётки панели и скачанный SteamCMD
server_dir: ${PZ_HOME}/server # серверные файлы Project Zomboid
zomboid_dir: ${PZ_HOME}/Zomboid # миры, конфиги и логи игры
profiles:
- id: servertest
title: Основной сервер
admin_password: "" # пароль админа игры, можно задать в панели
java_memory: 4g # -Xmx для JVM
extra_args: []
active_profile: servertest
autostart: false # поднимать игровой сервер вместе с панелью
autorestart: true # поднимать сервер после падения
stop_timeout: 1m30s # ждать после quit до SIGTERM
log_buffer_lines: 5000
backup:
dir: ${PZ_HOME}/backups
schedule: 6h # 0 — отключить автобэкапы
keep: 20 # архивов на профиль
stop_server: false # останавливать сервер на время бэкапа
EOF
chown "$PZ_USER:$PZ_USER" "$CONFIG"
# В конфиге лежит пароль админа игрового сервера.
chmod 0640 "$CONFIG"
fi
echo "==> Ставлю systemd-юнит" echo "==> Ставлю systemd-юнит"
sed -e "s|/home/pzserver|${PZ_HOME}|g" \ sed -e "s|/opt/pzmanager|${PZ_HOME}|g" \
-e "s|User=pzserver|User=${PZ_USER}|" \ -e "s|User=pzserver|User=${PZ_USER}|" \
-e "s|Group=pzserver|Group=${PZ_USER}|" \ -e "s|Group=pzserver|Group=${PZ_USER}|" \
"$UNIT_SRC" > /etc/systemd/system/pzmanager.service "$UNIT_SRC" > /etc/systemd/system/pzmanager.service
@@ -51,15 +101,28 @@ sed -e "s|/home/pzserver|${PZ_HOME}|g" \
systemctl daemon-reload systemctl daemon-reload
systemctl enable --now pzmanager systemctl enable --now pzmanager
# Дадим сервису секунду подняться, чтобы код настройки успел попасть в журнал.
sleep 2
echo echo
echo "Готово. Панель запущена." echo "Готово. Панель запущена и слушает ${PZ_LISTEN}."
echo "Код первичной настройки смотрите в журнале:"
echo echo
echo " journalctl -u pzmanager -n 30 --no-pager" if [[ "$PZ_LISTEN" == 127.0.0.1:* || "$PZ_LISTEN" == localhost:* ]]; then
echo "Панель доступна только с самой машины. Пробросьте порт:"
echo echo
echo "По умолчанию панель слушает 127.0.0.1:8080." echo " ssh -L 8080:${PZ_LISTEN} <пользователь>@<сервер>"
echo "Откройте её через SSH-туннель:"
echo echo
echo " ssh -L 8080:127.0.0.1:8080 <пользователь>@<сервер>" else
PORT="${PZ_LISTEN##*:}"
echo "Откройте в браузере: http://${PZ_LISTEN}"
echo echo
echo "Не забудьте открыть игровые порты: udp/16261 и udp/16262." echo "Если включён firewall, разрешите порт панели:"
echo
echo " sudo ufw allow ${PORT}/tcp"
echo
fi
echo "Код первичной настройки — в журнале:"
echo
echo " journalctl -u pzmanager -n 30 --no-pager | grep -A2 'код первичной'"
echo
echo "Игровые порты сервера: udp/16261 и udp/16262."
+5 -5
View File
@@ -7,8 +7,8 @@ Wants=network-online.target
Type=simple Type=simple
User=pzserver User=pzserver
Group=pzserver Group=pzserver
WorkingDirectory=/home/pzserver WorkingDirectory=/opt/pzmanager
ExecStart=/usr/local/bin/pzmanager serve --config /home/pzserver/.config/pzmanager/config.yaml ExecStart=/usr/local/bin/pzmanager serve --config /opt/pzmanager/config.yaml
Restart=on-failure Restart=on-failure
RestartSec=5 RestartSec=5
@@ -18,12 +18,12 @@ KillSignal=SIGTERM
KillMode=mixed KillMode=mixed
TimeoutStopSec=300 TimeoutStopSec=300
# Панель пишет только в свои директории. # Всё своё панель держит в /opt/pzmanager, домашние каталоги ей не нужны.
NoNewPrivileges=true NoNewPrivileges=true
PrivateTmp=true PrivateTmp=true
ProtectSystem=full ProtectSystem=full
ProtectHome=read-only ProtectHome=true
ReadWritePaths=/home/pzserver ReadWritePaths=/opt/pzmanager
StandardOutput=journal StandardOutput=journal
StandardError=journal StandardError=journal