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

Индекс LLMS: [llms.txt](/llms.txt)

---

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

## Зачем systemd-run

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

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

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

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

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

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

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

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

```bash
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 в указанную точку |

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

```bash
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.

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

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

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

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

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

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

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

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

> [!WARNING]  
> `RootDirectory` требует корректной структуры директорий внутри. Без нее процесс не стартует с ошибкой "Failed to pivot root".

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

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

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

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

```bash
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-run` | cgroup | CPU, RAM, I/O, пользователь | Да | systemctl |

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

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

```bash
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 подходит для долгоживущих процессов:

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

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

```bash
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` — для одноразовых задач, прототипирования и ограничения ресурсов "на лету".
