Развертывание LM Studio как сервиса в Ubuntu 25.04

Статья обновлена 2025.11.10, см. комментарий.

Введение

В этой статье расскажу о том, как развернуть LM Studio как сервис в среде Ubuntu 25.04 (может подойдет и для других версий).

LM Studio в качестве сервера позволяет не только загружать языковые модели и работать с ними единолично, но и организовывать API для подключения внешних сервисов. При этом вернуться к локальной работе можно, остановив сервис и включив приложение как обычно.

Вы сможете:

  • работать с разными языковыми моделями (по запросу с выгрузкой при неиспользовании)
  • подключать к API свои приложения или плагины

Скачать

Скачайте AppImage (в статье мы говорим про приложение для Linux), сайт:

Разместите файл в папке ~/llm и сделайте его исполняемым:

chmod +x ~/llm/LM-Studio-0.3.27-4-x64.AppImage

На момент написания статьи эта версия была актуальной.

headless

Развертывание в виде сервиса требуется тогда, когда вы работаете с ним удаленно. Сразу определитесь:

а) на сервере (ПК/ноутбуке и т.д.) графика есть и пользователь уже логинился хотя бы раз в оболочку UI. Либо если вы работаете в UI - то пропустите этот шаг.

б) на сервере графики нет либо пользователь никогда не логинился в UI. В этом случае понадобится пара дополнительных команд, выполняемых разово:

sudo loginctl enable-linger $USER

эта команда делает возможным работать с окружением, не входя в графический интерфейс

loginctl show-user ivan | grep Linger

эта команда показывает статус настройки (активно или недоступно):

Linger=yes

Команды, проверяющие, что ничего не мешает (в выводе должно быть какой-нибудь понятный статус типа degraded, но недопустимы ошибки):

systemctl --user status
systemctl --user is-system-running

systemd

Если вы все еще не решились на headless-режим (без графики), то пропустите этот шаг.

А если автозапуск это ваше всё, то будьте добры создать скрипт:

~/.config/systemd/user/lm-studio.service

И допустим, что ваш исполняемый файл LM Studio находится по адресу

%HOME/llm/LM-Studio-0.3.27-4-x64.AppImage

содержимое:

[Unit]
Description=LM Studio Service
After=network.target

[Service]
Type=simple

ExecStart=/usr/bin/xvfb-run -a --server-args="-screen 0 1920x1080x24" %h/llm/LM-Studio-0.3.27-4-x64.AppImage --run-as-service
ExecStartPost=/bin/bash -c 'sleep 10 && exec lms server start'

Restart=always
RestartSec=10
Environment=PATH=%h/.local/bin:/usr/local/bin:/usr/bin:/bin:%h/.lmstudio/bin
Environment=DISPLAY=:99
WorkingDirectory=%h/llm

[Install]
WantedBy=default.target

Скрипт состоит из двух частей:

  • запуск приложения
  • старт серверной части

По своей сути такой скрипт является хрупким, так как состоит из двух частей, которые не знают о состоянии друг друга. Не используйте подобные решения в продакшене. И вообще LM Studio не используйте, так как VLLM значительно быстрее.

Заметьте, что здесь эмулируется экран, поэтому вам необходимо доустановить:

sudo apt update && sudo apt install xvfb

Обратите внимание, что systemd в данном примере будет установлена от пользователя, а не root.

Проделайте обычные операции для стартового скрипта (применить изменения и сделать автозапуск):

systemctl --user daemon-reload
systemctl --user enable --now lm-studio.service

Запуск

systemctl --user status lm-studio.service

Сервис сейчас должен молчать, поскольку его еще ни разу не запускали.

systemctl --user start lm-studio.service

и теперь проверьте статус сервера:

lms server status

он должен прослушивать порт 1234.

Строго говоря, вам следует сначала поиграть с UI LM Studio, чтобы установить нужные параметры и загрузить модели. А также перевести прослушивание с адреса 127.0.0.1 на 0.0.0.0, если нужно организовать внешнее подключение к вашему API (потенциально опасно, так как сначала установите шифрование и авторизацию)

CURL

Проверьте endpoint вашего сервера:

curl -v http://127.0.0.1:1234/v1/models

вам должны вернуться доступные модели:

*   Trying 127.0.0.1:1234...
* Connected to 127.0.0.1 (127.0.0.1) port 1234
* using HTTP/1.x
> GET /v1/models HTTP/1.1
> Host: 127.0.0.1:1234
> User-Agent: curl/8.12.1
> Accept: */*
> 
* Request completely sent off
< HTTP/1.1 200 OK
< 
{
  "data": [
    {
      "id": "nvidia_nvidia-nemotron-nano-9b-v2",
      "object": "model",
      "owned_by": "organization_owner"
    },
    {
      "id": "text-embedding-qwen3-embedding-0.6b",
      "object": "model",
      "owned_by": "organization_owner"
    },
    {
      "id": "qwen/qwen3-8b",
      "object": "model",
      "owned_by": "organization_owner"
    },
    {
      "id": "google/gemma-3-4b",
      "object": "model",
      "owned_by": "organization_owner"
    }
  ],
  "object": "list"
* Connection #0 to host 127.0.0.1 left intact

REST API

Теперь вы можете подключаться к вашему REST API, используя эндпоинты:

GET  http://127.0.0.1:1234/v1/models
POST http://127.0.0.1:1234/v1/chat/completions
POST http://127.0.0.1:1234/v1/completions
POST http://127.0.0.1:1234/v1/embeddings

Где-то между версиями 0.3.31-2 и 0.3.31-7 произошло изменение, которое принудительно меняет прослушиваемый интерфейс с внешнего на 127.0.0.1 и отключает CORS.

Поскольку LM Studio поддерживает hot-reload конфига HTTP-сервера (по крайней мере, при изменении cors и networkInterface ), то сделать изменение можно без перезапуска (сработает разово):

%h/.lmstudio/.internal/http-server-config.json
jq '.cors = true | .networkInterface = "0.0.0.0"' ~/.lmstudio/.internal/http-server-config.json | sponge ~/.lmstudio/.internal/http-server-config.json

при этом sponge входит в пакет moreutils - не забудьте его установить.

Чтобы изменение происходило автоматически, измените стартовый скрипт:

$HOME/.config/systemd/user/lm-studio.service
[Unit]
Description=LM Studio Service
After=network.target

[Service]
Type=simple

ExecStart=/usr/bin/xvfb-run -a --server-args="-screen 0 1920x1080x24" %h/llm/lmstudio --run-as-service

# 1. Запуск HTTP-сервера
ExecStartPost=/bin/bash -c 'sleep 15 && exec lms server start'

# 2. Применение требуемых настроек (после того, как сервер уже стартовал)
ExecStartPost=/bin/bash -c ' \
  sleep 2 && \
  jq \".cors = true | .networkInterface = \\\"0.0.0.0\\\"\" \
     \"%h/.lmstudio/.internal/http-server-config.json\" \
     > \"%h/.lmstudio/.internal/http-server-config.json.tmp\" && \
  mv \"%h/.lmstudio/.internal/http-server-config.json.tmp\" \
     \"%h/.lmstudio/.internal/http-server-config.json\" \
'

Restart=always
RestartSec=10
Environment=PATH=%h/.local/bin:/usr/local/bin:/usr/bin:/bin:%h/.lmstudio/bin
Environment=DISPLAY=:99
WorkingDirectory=%h/llm

[Install]
WantedBy=default.target

Примените изменения и перезапустите сервис (без sudo!):

systemctl --user daemon-reload
systemctl --user stop lm-studio.service
systemctl --user start lm-studio.service

Обратите внимание на тюнинг параметров ОС для оптимальной работы.

Пример

Вот пример из моей системы (это ноутбук с подключенной видеокартой с помощью Oculink):

free -h
               total        used        free      shared  buff/cache   available
Mem:            37Gi       4.9Gi        29Gi       240Mi       3.5Gi        32Gi
Swap:          8.0Gi          0B       8.0Gi
cat /proc/meminfo | grep -E 'MemTotal|MemAvailable'

MemTotal:       39223064 kB
MemAvailable:   34073484 kB

VRAM выводится отдельно — она не входит в MemTotal

nvidia-smi

Fri Nov 21 12:24:47 2025       
+-----------------------------------------------------------------------------------------+
| NVIDIA-SMI 580.82.09              Driver Version: 580.82.09      CUDA Version: 13.0     |
+-----------------------------------------+------------------------+----------------------+
| GPU  Name                 Persistence-M | Bus-Id          Disp.A | Volatile Uncorr. ECC |
| Fan  Temp   Perf          Pwr:Usage/Cap |           Memory-Usage | GPU-Util  Compute M. |
|                                         |                        |               MIG M. |
|=========================================+========================+======================|
|   0  NVIDIA GeForce RTX 5060 Ti     Off |   00000000:01:00.0 Off |                  N/A |
|  0%   44C    P8              8W /  180W |      13MiB /  16311MiB |      0%      Default |
|                                         |                        |                  N/A |
+-----------------------------------------+------------------------+----------------------+

+-----------------------------------------------------------------------------------------+
| Processes:                                                                              |
|  GPU   GI   CI              PID   Type   Process name                        GPU Memory |
|        ID   ID                                                               Usage      |
|=========================================================================================|
|    0   N/A  N/A            5314      G   /usr/bin/gnome-shell                      2MiB |
+-----------------------------------------------------------------------------------------+

Что делать

Проверь политику overcommit и swappiness

cat /proc/sys/vm/overcommit_memory
cat /proc/sys/vm/overcommit_ratio
cat /proc/sys/vm/swappiness

Рекомендуемые значения:

2 # лимит = (RAM * ratio/100) + Swap
80 # это и есть ratio
10 # не выше 20 - это сколько оставшейся RAM должно быть, чтобы начал работать swap, сам swap может быть 8 GiB, если у тебя не используется гибернация ОС, тогда своп будет равен размеру оперативки

Команды для выполнения:

sudo sysctl vm.overcommit_ratio=80
echo 'vm.overcommit_ratio=80' | sudo tee -a /etc/sysctl.d/99-ml-workstation.conf
sudo sysctl vm.swappiness=10
echo 'vm.swappiness=10' | sudo tee -a /etc/sysctl.d/99-ml-workstation.conf

ВАЖНО: Для LLM (особенно llama.cpp) THP = always может вызывать паузы (задержки в минуту после логина в ОС)

cat /sys/kernel/mm/transparent_hugepage/enabled
# Должно быть: [always] madvise never

При работе с диском (например, когда веса LLM пишутся на SSD), важна настройка writeback:

cat /proc/sys/vm/dirty_ratio
cat /proc/sys/vm/dirty_background_ratio
cat /proc/sys/vm/dirty_expire_centisecs

Показатели, оптимальные для NVMe:

10
5
1000 (это 10 секунд)
echo 'vm.dirty_ratio=10' | sudo tee -a /etc/sysctl.d/99-ml-workstation.conf
echo 'dirty_background_ratio=5' | sudo tee -a /etc/sysctl.d/99-ml-workstation.conf
echo 'vm.dirty_expire_centisecs=1000' | sudo tee -a /etc/sysctl.d/99-ml-workstation.conf

Между запусками lmstudio рекомендуют очищать кеш памяти:

sync && echo 2 | sudo tee /proc/sys/vm/drop_caches