Перейти к содержимому

systemd-run: запуск сервиса без unit-файла

Иногда нужно быстро поднять процесс под systemd, но писать unit-файл и класть его в /etc/systemd/system лень или нельзя — контейнер без systemd, чужая машина, временный запуск. На этот случай есть systemd-run.

Зачем systemd-run

Инструмент создает transient unit — юнит, который существует только в памяти systemd, без файла на диске. Это удобно, когда:

  • нужен контроль ресурсов (cgroup) над разовым процессом;
  • важно, чтобы процесс пережил закрытие терминала;
  • хочется изолировать команду в отдельном слайсе.

По сути, это обертка над systemctl start для юнитов, которые никто не сохраняет.

Базовый синтаксис

systemd-run [OPTIONS] COMMAND [ARGUMENTS...]

Простейший запуск:

systemd-run /bin/bash -c "while true; do :; done"

Процесс попадает в user.slice, работает в фоне, управляется через systemctl. Проверить:

systemctl list-units --type=service

В выводе появится что-то вроде run-u1234.service.

Ключевые флаги

ФлагНазначение
--scopeСоздать scope unit вместо service (родительский процесс остается в scope)
--unit=NAMEЗадать имя юнита вместо автоматического
--uid=USERЗапустить от указанного пользователя
--gid=GROUPЗапустить от указанной группы
--nice=NПриоритет (от -20 до 19)
--property=KEY=VALUEПередать свойство в unit (CPUAccounting, MemoryMax и т.д.)
--chdir=PATHРабочая директория
--setenv=VAR=VALUEПеременная окружения
--tmpfs=PATH:OPTIONSПримонтировать tmpfs в указанную точку

Флаги комбинируются. Пример с ограничениями:

systemd-run \
  --uid=appuser \
  --gid=appgroup \
  --nice=10 \
  --property=MemoryMax=512M \
  --property=CPUAccounting=true \
  --property=CPUWeight=256 \
  -- /opt/myapp/bin/server

Изоляция: CPU, RAM, root через cgroup

Основная ценность — управление ресурсами через cgroup v2.

Ограничение по памяти:

systemd-run \
  --property=MemoryMax=1G \
  --property=MemoryHigh=800M \
  /bin/memory-heavy-task

Если процесс превысит лимит, systemd убьет его (OOM kill через cgroup).

Ограничение по CPU:

systemd-run \
  --property=CPUQuota=50% \
  /bin/calculator

50% от одного ядра. Для многоядерных систем считайте проценты от суммарных единиц.

Изоляция с отдельным root:

systemd-run \
  --property=RootDirectory=/opt/jail \
  --property=RootImage=overlayfs \
  /bin/sh -c "whoami"

RootDirectory требует корректной структуры директорий внутри. Без нее процесс не стартует с ошибкой “Failed to pivot root”.

Примонтировать tmpfs:

systemd-run \
  --tmpfs=/tmp/isolated:rw,noexec,size=256M \
  -- /bin/sh

Пригождается для обработки временных файлов с ограничением места.

Комбинированный пример:

systemd-run \
  --uid=nobody \
  --gid=nogroup \
  --nice=5 \
  --chdir=/var/data \
  --property=MemoryMax=256M \
  --property=CPUQuota=25% \
  --property=IOAccounting=true \
  -- /usr/local/bin/worker --queue=default

Все ресурсы процесса учтены в cgroup, видны через systemd-cgtop и /sys/fs/cgroup.

Сравнение с nohup, setsid, chroot

ИнструментРесурсыИзоляцияЖив после logoutУправление
nohupНетНетДаНет
setsidНетНетДаНет
chrootНетФайловая системаЗависитНет
systemd-runcgroupCPU, RAM, I/O, пользовательДаsystemctl

nohup и setsid хороши для фоновых скриптов. Но если нужен контроль памяти или приоритета — без cgroup не обойтись.

chroot решает только задачу изоляции ФС. Запустить systemd-run --chroot нельзя, но можно скомбинировать:

systemd-run \
  --uid=nobody \
  --property=MemoryMax=100M \
  --chdir=/tmp \
  /usr/sbin/chroot --userspec=nobody /opt/jail /bin/daemon

Здесь chroot изолирует файловую систему, systemd-run ограничивает ресурсы.

Подводные камни: scope vs service

По умолчанию systemd-run создает service unit. Разница:

  • service — полноценный unit, регистрируется в systemd, имеет зависимости, управляется стандартно.
  • scope — привязан к родительскому процессу. Указывается флагом --scope. Родитель умирает — scope умирает.

В повседневной практике service подходит для долгоживущих процессов:

systemd-run --unit=my-daemon /usr/local/bin/daemon

Scope полезен для группировки связанных процессов:

systemd-run --scope --uid=user1 /bin/bash -c 'spawn-worker-1 & spawn-worker-2 & wait'

Если запустить с --scope и закрыть терминал — процессы умрут. Без --scope — останутся.

Ограничения transient units

Transien units не сохраняются после перезагрузки. Это очевидно, но есть и другие нюансы:

  • Нет зависимостей. Юниты не имеют After=, Wants=, Requires=. Процесс стартует сразу. Если нужна последовательность — запускайте отдельные команды в скрипте с sleep.
  • Ограниченный набор свойств. Не все поля unit-файла доступны через --property. Например, Restart=always в transient unit не поддерживается (проверено на systemd 254).
  • Сложнее отладка. Нет файла — нет systemctl edit. Все параметры только в командной строке.

Для долгоживущих сервисов с зависимостями и стратегией рестарта — unit-файл остается правильным выбором. systemd-run — для одноразовых задач, прототипирования и ограничения ресурсов “на лету”.