<< На главную

Быстрый запуск Qwen3 на VPS без GPU. 1 CPU, 2GB RAM

Нужна полноценная настройка API с токеном, proxy и внешним доступом? Есть расширенная инструкция: Qwen3 на VPS с API и токеном .

В ней показано, как закрыть llama-server через proxy, добавить токен авторизации и использовать модель через API.

Технические детали

Модель поднималась на VPS от Бегет:
Процессор: 1 виртуальное ядро Intel Xeon
Оперативная память: 2 ГБ
Swap: 2 ГБ
Диск: 15 ГБ NVMe
OS: Ubuntu
Модель: Qwen3-1.7B, квантование Q4_K_M
Запуск: llama.cpp
    

Команды для запуска модели

mkdir -p /root/llama
cd /root/llama

sudo fallocate -l 2G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile

grep -q '^/swapfile ' /etc/fstab || echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab

sudo apt update
sudo apt install build-essential git cmake python3-pip python3-venv wget curl -y

git clone https://github.com/ggml-org/llama.cpp
cd llama.cpp

mkdir build
cd build

cmake ..
cmake --build . --config Release -j1

python3 -m venv /root/hfvenv
/root/hfvenv/bin/pip install -U "huggingface_hub[cli]"

mkdir -p /root/qwen3

/root/hfvenv/bin/hf download \
  unsloth/Qwen3-1.7B-GGUF \
  Qwen3-1.7B-Q4_K_M.gguf \
  --local-dir /root/qwen3

/root/llama/llama.cpp/build/bin/llama-cli \
  -m /root/qwen3/Qwen3-1.7B-Q4_K_M.gguf \
  -t 1 \
  -c 1024 \
  -n 120 \
  --temp 0.7 \
  --no-conversation \
  -p "Ты мастер текстовой RPG. Отвечай кратко.
/no_think

Игрок: осматриваю комнату.
Мастер:"
    
После ввода команды '/root/llama/llama.cpp/build/bin/llama-cli \ -m /root/qwen3/Qwen3-1.7B-Q4_K_M.gguf \ -t 1 \ -c 1024 \ -n 120 \ --temp 0.7 \ --no-conversation \ -p "Ты мастер текстовой RPG. Отвечай кратко. /no_think Игрок: осматриваю комнату. Мастер:"' нужно не забыть нажать Enter )))) потому что сначала я подумал что модель не взлетела и зависла...

Запуск API для генерации текста

Ручной запуск через llama-cli подходит только для тестов. Для работы с ботом или сайтом удобнее поднять HTTP API. В llama.cpp уже есть встроенный сервер llama-server. Он умеет принимать POST-запросы и возвращать JSON с ответом модели.

В отличие от варианта с Flask и subprocess, тут модель постоянно загружена в память. За счёт этого запросы обрабатываются быстрее и стабильнее. Для VPS с 2 ГБ RAM модель Qwen3-1.7B в квантовании Q4_K_M работает нормально.

Ручной запуск API

/root/llama/llama.cpp/build/bin/llama-server \
  -m /root/qwen3/Qwen3-1.7B-Q4_K_M.gguf \
  -t 1 \
  -c 1024 \
  --host 0.0.0.0 \
  --port 8092

После запуска API начинает слушать порт 8092. Проверить можно через health endpoint:

curl http://127.0.0.1:8092/health

Ожидаемый ответ:

{"status":"ok"}

Проверка генерации текста через curl

В Linux запрос выглядит так:

curl -X POST http://127.0.0.1:8092/completion \
  -H "Content-Type: application/json" \
  -d '{"prompt":"/no_think Ты мастер RPG. Игрок осматривает комнату.","n_predict":80}'

В Windows cmd лучше писать одной строкой:

curl -X POST http://VPS_IP:8092/completion -H "Content-Type: application/json" -d "{\"prompt\":\"/no_think Ты мастер RPG. Игрок осматривает комнату.\",\"n_predict\":80}"

Ответ приходит в JSON:

{
  "content":" Внутри комнаты есть небольшой сундук...",
  "tokens_predicted":80,
  "timings":{
    "predicted_per_second":9.63
  }
}

Автозапуск через systemd

Ручной запуск удобен для теста. Но после перезагрузки VPS сервер остановится. Поэтому лучше сразу поднять systemd-сервис.

nano /etc/systemd/system/llm-server.service

Содержимое файла:

[Unit]
Description=Qwen3 LLM Server
After=network.target

[Service]
Type=simple
ExecStart=/root/llama/llama.cpp/build/bin/llama-server \
  -m /root/qwen3/Qwen3-1.7B-Q4_K_M.gguf \
  -t 1 \
  -c 1024 \
  --host 0.0.0.0 \
  --port 8092

WorkingDirectory=/root/llama/llama.cpp/build
Restart=always
RestartSec=3

[Install]
WantedBy=multi-user.target

Запуск сервиса:

systemctl daemon-reload
systemctl enable llm-server
systemctl start llm-server
systemctl status llm-server

Проверка после запуска:

curl http://127.0.0.1:8092/health

Просмотр логов:

journalctl -u llm-server -f

PHP-запрос к локальной LLM

<?php

function generateLocalLlmText($prompt, $max_tokens = 80)
{
    $url = 'http://VPS_IP:8092/completion';

    $data = array(
        'prompt' => '/no_think ' . $prompt,
        'n_predict' => $max_tokens
    );

    $ch = curl_init();

    curl_setopt($ch, CURLOPT_URL, $url);
    curl_setopt($ch, CURLOPT_POST, true);
    curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
    curl_setopt($ch, CURLOPT_HTTPHEADER, array(
        'Content-Type: application/json'
    ));
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_TIMEOUT, 300);
    curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10);

    $response = curl_exec($ch);

    if ($response === false) {
        $error = curl_error($ch);
        curl_close($ch);

        return array(
            'ok' => false,
            'error' => $error
        );
    }

    curl_close($ch);

    $json = json_decode($response, true);

    if (!is_array($json)) {
        return array(
            'ok' => false,
            'error' => 'bad json',
            'raw' => $response
        );
    }

    return array(
        'ok' => true,
        'text' => trim($json['content'])
    );
}

$res = generateLocalLlmText('Ты мастер RPG. Игрок осматривает комнату.');

print_r($res);

?>