АйТи Фреш
Главная / Статьи / DevOps и автоматизация
DevOps и автоматизация

Почему служба systemctl --user работает только после входа по SSH и как оставить её жить после выхода

Автор: Семёнов Евгений Сергеевич, директор ООО «АйТи-Фреш» · · ~23 мин чтения
Почему служба systemctl --user работает только после входа по SSH и как оставить её жить после выхода
Иллюстрация к статье «Почему служба systemctl --user работает только после входа по SSH и как оставить её жить после выхода».

Знакомая картина: сервис поднят через systemctl --user enable --now, всё зелёное, вы выходите из SSH — и секунд через десять выгрузка из 1С молчит. А после планового ребута она не поднимается вообще, пока утром кто-нибудь не зайдёт на сервер. Разбираю механику: почему пользовательский менеджер systemd умирает вместе с вашей сессией, что реально делает loginctl enable-linger, почему enable — это не про автозапуск при загрузке, и в каких случаях пользовательскую службу лучше вообще не заводить.

Служба живёт ровно столько, сколько живёт ваш логин

Классика жанра из моей практики: администратор разворачивает небольшой демон под непривилегированным пользователем, потому что «не хочу гонять это от root». Кладёт unit в ~/.config/systemd/user, делает systemctl --user daemon-reload, systemctl --user enable --now exporter.service, видит active (running), радуется и закрывает терминал. Через пару минут звонит бухгалтерия: выгрузка не приходит. Админ заходит обратно по SSH — сервис снова работает. И вот тут начинается мистика на два дня, хотя всё абсолютно детерминировано.

Механика простая. Когда вы логинитесь по SSH, PAM-модуль pam_systemd просит systemd-logind создать вам сессию. Logind заводит две вещи: session-N.scope, в который попадают процессы вашей оболочки, и user@UID.service — тот самый пользовательский менеджер systemd, который и запускает ваши --user юниты. Когда вы выходите, logind закрывает сессию. И, если у пользователя не включён linger, после закрытия последней сессии останавливается и user@UID.service — не мгновенно, а через UserStopDelaySec= из logind.conf, по умолчанию 10 секунд (это страховка на случай быстрого перелогина). Все ваши пользовательские юниты уезжают вместе с ним. Не «падают», не «крешатся» — их корректно останавливают, ровно так, как задумано.

Добивает картину настройка KillUserProcesses= в logind.conf: при значении yes процессы в scope-юните сессии убиваются при logout. Здесь важен нюанс, на котором путаются даже опытные люди. В апстримном systemd и в man logind.conf по умолчанию стоит yes, но дистрибутив может собрать пакет с другим значением — в Debian 13, например, страница руководства прямо говорит «Defaults to no». Поэтому сначала смотрите, что написано в man именно на вашей машине и в /etc/systemd/logind.conf.d/, а не в статье из интернета. Именно из-за этого параметра в своё время начали разбегаться tmux и screen, и на него чаще всего грешат, когда отваливается пользовательская служба. Но в нашем случае виноват не он: даже с KillUserProcesses=no пользовательский менеджер всё равно остановится после выхода, если нет linger. Это два разных механизма, и лечить надо второй.

# кто сейчас залогинен и какие сессии живы
loginctl list-sessions

# состояние пользовательского менеджера (UID подставьте свой)
systemctl status user@1002.service

# журнал именно менеджера — здесь видно Stopped User Manager for UID 1002
journalctl -u user@1002.service -b --no-pager | tail -30
Если сервис «работает, пока я в SSH» — не ищите ошибку в самом сервисе. Смотрите journalctl -u user@UID.service: там будет честная запись об остановке менеджера, а не о падении вашего демона.
Памятка: Служба живёт ровно столько, сколько живёт ваш логин — схема
Памятка: Служба живёт ровно столько, сколько живёт ваш логин. Открыть схему в полном размере

loginctl enable-linger — одна команда, которая закрывает 90 % вопроса

Linger (буквально «задержаться») — это флаг у пользователя, который говорит logind две вещи сразу. Первое: запускать user@UID.service при загрузке системы, не дожидаясь ничьего входа. Второе: не останавливать его после выхода последней сессии. В мануале это сформулировано без лишних слов: «If enabled for a specific user, a user manager is spawned for the user at boot and kept around after logouts». Команда существует в systemd очень давно и есть везде, где вы сегодня можете оказаться — от Ubuntu 24.04 LTS (systemd 255) до Debian 13 trixie (systemd 257) и RHEL 10. Если аргумент не указан, linger включается для пользователя текущей сессии.

Команда принимает как имя пользователя, так и числовой UID. Технически включение linger — это создание пустого файла /var/lib/systemd/linger/<username>. Я специально это упоминаю, потому что при разборе чужой инфраструктуры быстрее один раз посмотреть содержимое каталога, чем опрашивать logind по каждому пользователю: сразу видно, для кого автозапуск предусмотрен, а для кого нет.

Проверять состояние надо машинно-читаемым способом, а не глазами по выводу status. Для этого есть loginctl show-user с ключом --property=: он отдаёт пары ключ=значение и отлично ложится в скрипты мониторинга. Я держу такую проверку в чек-листе приёмки сервера: если у сервисного пользователя Linger=no, а на нём висят --user юниты — это готовый инцидент, который выстрелит на ближайшем ребуте.

Есть и второй эффект, о котором вспоминают редко, а он влияет на поведение приложений. С включённым linger каталог /run/user/UID монтируется при загрузке системы и существует постоянно, а не появляется на время сессии и не сносится при выходе. Всё, что ваш демон складывает в XDG_RUNTIME_DIR — сокеты, pid-файлы, кэш, — перестаёт исчезать за спиной. На практике именно это чаще всего и объясняет плавающие баги вида «после моего захода на сервер первая обработка идёт втрое дольше»: пока linger выключен, рантайм-каталог обнуляется на каждом logout, и каждый новый вход начинается с холодного кэша. Учтите только, что это tmpfs: после перезагрузки содержимое всё равно пропадает. Проверить элементарно: ls -ld /run/user/1002 сразу после перезагрузки, не заходя в систему под этим пользователем.

# включить (для чужого пользователя нужен root или polkit-разрешение)
sudo loginctl enable-linger deploy

# проверить машинно-читаемо
loginctl show-user deploy --property=Linger
# Linger=yes

# то же самое «сырым» способом
ls -l /var/lib/systemd/linger/

# выключить
sudo loginctl disable-linger deploy
enable-linger не запускает ваши службы сам по себе. Он поднимает менеджер. Что именно менеджер запустит — решает enable и цель default.target. Это следующий шаг, и его пропускают чаще всего.
Почему служба systemctl --user работает только после входа по SSH и как оставить её жить после выхода — схема
Схема к статье. Открыть схему в полном размере

systemctl --user enable — это не «стартовать при загрузке»

Вторая половина проблемы — путаница с целями. Люди берут привычный системный шаблон, где в секции [Install] написано WantedBy=multi-user.target, копируют его в ~/.config/systemd/user и удивляются, что юнит «включён», но при старте менеджера не поднимается. Причина в том, что в пользовательском экземпляре systemd никакого multi-user.target нет — это цель системного менеджера. У пользовательского свой набор специальных юнитов (они перечислены в man systemd.special): default.target, sockets.target, timers.target, paths.target, shutdown.target, graphical-session.target и ещё несколько. В документации прямо сказано, что default.target — главная цель пользовательского менеджера и аналог multi-user.target системного, только это настоящий юнит, а не алиас. Так что для --user юнита правильная строка ровно одна: WantedBy=default.target.

Соответственно, systemctl --user enable exporter.service просто создаёт симлинк ~/.config/systemd/user/default.target.wants/exporter.service. Если WantedBy указывает на несуществующую цель, symlink уедет в каталог, который никто не вызывает, и юнит будет числиться enabled, оставаясь мёртвым. Проверяется это за секунду — посмотрите глазами, куда легла ссылка. Та же ловушка с After=network-online.target: этой цели в пользовательском менеджере тоже нет, поэтому такая строка в --user юните ничего не упорядочивает. Если скрипт должен дождаться сети, надёжнее научить его повторять попытки (Restart= с RestartSec= как раз для этого) или сделать службу системной. Кстати, RHEL в своих сгенерированных юнитах для rootless-контейнеров пишет WantedBy=multi-user.target default.target именно для того, чтобы один и тот же файл годился и в системном, и в пользовательском режиме.

Каталоги поиска пользовательских юнитов стоит держать в голове, потому что при разборе чужого сервера файл обнаруживается совсем не там, где вы его ищете. Если отбросить служебные каталоги для transient-юнитов, настроек через D-Bus (user.control) и генераторов, порядок приоритета сверху вниз такой: ~/.config/systemd/user ($XDG_CONFIG_HOME), $XDG_CONFIG_DIRS/systemd/user (по умолчанию /etc/xdg/systemd/user), /etc/systemd/user, $XDG_RUNTIME_DIR/systemd/user, /run/systemd/user, ~/.local/share/systemd/user ($XDG_DATA_HOME), $XDG_DATA_DIRS/systemd/user, /usr/local/lib/systemd/user и /usr/lib/systemd/user. Юниты из каталогов выше перекрывают одноимённые из каталогов ниже. Отдельно отмечу /etc/systemd/user — это способ раздать пользовательский юнит централизованно, из конфигурации, а не из домашнего каталога, что удобно, когда домашние каталоги на NFS или пересоздаются.

# ~/.config/systemd/user/exporter.service
[Unit]
Description=Выгрузка расписания из 1С в CSV
# After=network-online.target здесь бесполезен: в --user менеджере этой цели нет

[Service]
Type=simple
ExecStart=/opt/exporter/venv/bin/python /opt/exporter/run.py
Restart=always
RestartSec=10
Environment=PYTHONUNBUFFERED=1

[Install]
WantedBy=default.target
Формула автозапуска пользовательской службы состоит из двух независимых частей: loginctl enable-linger USER (менеджер поднялся при загрузке) плюс systemctl --user enable SERVICE с WantedBy=default.target (менеджер знает, что запускать). Одно без другого не работает.

Разбор с производства: студия звукозаписи «ЗвукГрад», 16 рабочих мест

Клиент — звукозаписывающая студия «ЗвукГрад»: 16 рабочих мест, три аппаратные, база 1С, в которой администраторы ведут бронирование студийного времени, и небольшой сервер на Debian 13 (systemd 257) в серверном шкафу. Раз в пять минут на нём скрипт забирает брони из 1С по OData и складывает CSV, из которого строится расписание на планшетах у дверей аппаратных. Скрипт написал приходящий подрядчик, запустил под пользователем deploy (UID 1002) как пользовательскую службу — вполне здравое решение, чтобы не давать процессу лишних прав. Работало полтора месяца, потому что подрядчик после каждого визита оставлял открытую SSH-сессию в screen, и менеджер systemd для deploy жил за счёт неё.

Дальше произошло очевидное. Плановое обновление ядра, ребут в 03:40, никто не заходит на сервер до 12:50. Девять часов выгрузка не идёт, планшеты показывают вчерашнее расписание, и обнаружилось это самым неприятным способом: в полдень в одну аппаратную пришли два клиента с бронью на одно и то же время. Диагностика заняла минут пятнадцать: systemctl --user выдавал Failed to connect to bus, потому что я зашёл через sudo -u deploy, а не полноценной сессией; journalctl -u user@1002.service показал, что менеджер вообще ни разу не стартовал после загрузки. Каталог /var/lib/systemd/linger был пуст. В [Install] стояло WantedBy=multi-user.target, то есть даже если бы менеджер поднялся, служба не запустилась бы.

Что я сделал. Включил linger для deploy, переписал [Install] на default.target, пересоздал симлинк через disable/enable (важно: старый симлинк в multi-user.target.wants остался бы висеть мусором), убрал бесполезный After=network-online.target и добавил Restart=always с RestartSec=10 — у скрипта была привычка умирать на таймауте OData, а Restart в исходном юните отсутствовал вовсе. Сверху повесил простую проверку в мониторинг: раз в час от root выполняются loginctl show-user deploy --property=Linger и systemctl --user -M deploy@ is-active exporter.service. Итог за четыре месяца: пять перезагрузок сервера, ноль простоев выгрузки, три автоматических рестарта по таймауту OData, о которых никто даже не узнал.

Отдельная деталь, которая всплыла в процессе и стоила ещё часа. Скрипт складывал промежуточные файлы в /run/user/1002/exporter. Пока linger не был включён, этот каталог создавался при входе и уничтожался при выходе — его обслуживает системный юнит user-runtime-dir@1002.service, который удаляет /run/user/1002 при своей остановке. Кэш обнулялся, и первая после логина выгрузка всегда шла полным объёмом за квартал, около двух минут вместо двадцати секунд. С включённым linger каталог /run/user/1002 существует с момента загрузки системы. Но помните: это tmpfs, после перезагрузки он всё равно пуст, так что кэш, который должен переживать ребут, место в ~/.cache или StateDirectory=, а не в XDG_RUNTIME_DIR.

Если меняете WantedBy у уже включённого юнита — сделайте systemctl --user disable, потом enable. Иначе останется симлинк в старом .wants-каталоге, и вы будете смотреть на «enabled» из двух мест одновременно.
Цифры и версии: Разбор с производства: студия звукозаписи «ЗвукГрад», 16 рабочих мест — схема
Цифры и версии: Разбор с производства: студия звукозаписи «ЗвукГрад», 16 рабочих мест. Открыть схему в полном размере

Грабли доступа: Failed to connect to bus в скриптах и Ansible

Самая частая вторичная проблема: вы всё настроили правильно, но проверить не можете. Заходите под root, делаете sudo -u deploy systemctl --user status exporter — и получаете Failed to connect to bus: $XDG_RUNTIME_DIR not defined либо No medium found / Permission denied. Причина в том, что sudo -u и su не регистрируют для целевого пользователя новую сессию в logind: pam_systemd либо вообще не стоит в их PAM-стеке, либо не создаёт сессию внутри уже существующей. Переменные XDG_RUNTIME_DIR и DBUS_SESSION_BUS_ADDRESS остаются от root или не выставляются вовсе, и systemctl не понимает, к какой шине пользовательского менеджера подключаться. Сам менеджер при этом жив и прекрасно работает — вы просто до него не достучались.

Лечится это тремя способами, и я использую все три в разных ситуациях. Самый переносимый — руками выставить XDG_RUNTIME_DIR и адрес шины. Самый честный — machinectl shell deploy@, который создаёт полноценную сессию через logind со всем положенным окружением (в Debian утилита лежит в отдельном пакете systemd-container). Самый короткий для одиночной проверки — systemctl --user -M deploy@: в man systemctl синтаксис ключа -M описан как имя контейнера с необязательным префиксом «пользователь@», а пустая правая часть означает локальный хост. Обратите внимание: первый способ работает только если /run/user/UID существует, то есть после включения linger или при активной сессии пользователя — иначе подключаться просто некуда.

В Ansible ровно та же история. Модуль ansible.builtin.systemd_service со scope: user требует, чтобы у become_user было валидное окружение сессии. Я в плейбуках задаю переменные окружения задачи явно и первым шагом всегда включаю linger — до того, как пытаться что-либо enable. Порядок здесь принципиален: если сначала делать enable, а linger включать после, то на «чистой» машине без интерактивного входа задача упадёт, и половина ролей об это спотыкается.

```bash # рабочая заготовка для скриптов и cron от root UID_DEPLOY=$(id -u deploy) sudo -u deploy env \ XDG_RUNTIME_DIR=/run/user/$UID_DEPLOY \ DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/$UID_DEPLOY/bus \ systemctl --user is-active exporter.service ```

Rootless-контейнеры: тот же linger, но цена ошибки выше

Отдельный класс ситуаций — rootless-контейнеры: Podman и rootless Docker. Здесь пользовательские юниты не экзотика, а основной рабочий сценарий, и забытый linger означает, что после перезагрузки хоста у вас не поднимется вообще ничего из контейнерной нагрузки. Red Hat в документации к RHEL 10 пишет об этом прямым текстом: чтобы служба стартовала при загрузке системы и переживала logout, нужен loginctl enable-linger. Всё остальное — enable, зависимости, targets — работает по тем же правилам, что и для обычных юнитов.

С rootless Docker картина та же. Установщик dockerd-rootless-setuptool.sh install заводит пользовательскую службу docker.service, управлять ей предлагается через systemctl --user start|stop|restart docker.service, а для старта при загрузке документация Docker прямо велит выполнить sudo loginctl enable-linger с именем пользователя. Клиенту при этом нужно указать сокет из рантайм-каталога — DOCKER_HOST=unix:///run/user/UID/docker.sock, — и это ещё одна причина, по которой без linger и /run/user/UID ничего не работает после ребута.

Что изменилось к 2026 году и о чём стоит знать, если вы переносите старые конфигурации: команда podman generate systemd объявлена устаревшей. Её не удаляют, критические баги чинят, но новых возможностей не будет, и рекомендуемый путь — Quadlet. Это генератор systemd, встроенный в сам Podman начиная с 4.4 (документация RHEL 10 ведёт отсчёт от поставляемой в дистрибутиве 4.6): вы кладёте короткий декларативный файл с расширением .container, .pod, .network, .volume, .kube, .image, .build или .artifact, а Quadlet при каждом daemon-reload превращает его в настоящий .service. Для rootless Quadlet ищет файлы в $XDG_RUNTIME_DIR/containers/systemd/, ~/.config/containers/systemd/, а для централизованной раздачи — в /etc/containers/systemd/users/${UID} и /etc/containers/systemd/users/.

Практический нюанс: юниты, порождённые Quadlet, нельзя enable в привычном смысле: документация Podman называет их transient с точки зрения systemd и прямо говорит, что systemctl enable к ним неприменим. Автозапуск задаётся секцией [Install] внутри самого .container-файла, и после daemon-reload сгенерированный сервис оказывается в default.target.wants уже в рантайме. Поэтому диагностика через ls ~/.config/systemd/user/default.target.wants/ тут не сработает — смотрите systemctl --user list-unit-files и /run/user/UID/systemd/generator/.

```ini # ~/.config/containers/systemd/redis.container [Container] Image=docker.io/library/redis:7 PublishPort=127.0.0.1:6379:6379 [Service] Restart=always [Install] WantedBy=default.target ``` После правки файла сгенерированный юнит нужно перечитать и запустить — enable для него не нужен, автозапуск уже задан в [Install]: ```bash systemctl --user daemon-reload systemctl --user start redis.service ```

Когда пользовательская служба вообще не нужна: мой чек-лист приоритетов

Скажу прямо, потому что здесь единого мнения в сообществе нет и не будет. Пользовательские юниты — отличная штука для рабочих станций, для разработчиков, для rootless-контейнеров и для случаев, когда процессу реально нужны сессионные вещи вроде своего keyring или собственного /run/user. Но на боевом сервере, где нужен один фоновый демон под непривилегированным пользователем, я в девяти случаях из десяти беру обычный системный юнит с User= и Group=. Он стартует до всякого logind, не зависит от linger, не ломается при sudo -u, нормально виден в journalctl -u без плясок с шиной и не удивляет коллегу, который придёт разбираться после вас.

Ещё жёстче: если сервису не нужен домашний каталог, стоит посмотреть в сторону DynamicUser=yes вместе с StateDirectory= и RuntimeDirectory=. Systemd сам заведёт учётную запись на время работы юнита, сам создаст и почистит каталоги с правильными правами, и вам не придётся вообще заводить пользователя в системе. По изоляции это лучше, чем «сделал юзера deploy и положил всё в его домашний каталог». Спорный момент, о котором честно предупреждаю: DynamicUser плохо дружит со сценариями, где данные должны переживать смену UID или лежать в шаре, — там придётся возвращаться к статическому пользователю.

И про риск, который часто преувеличивают. Иногда предлагают «решить» проблему выхода из SSH настройкой KillUserProcesses=no в /etc/systemd/logind.conf. На Debian это и так значение по умолчанию — и служба всё равно умирает, что лучше любых аргументов показывает, что дело не в нём. Это не решение задачи автозапуска вообще: менеджер всё равно не поднимется при загрузке без linger. Плюс вы получаете побочный эффект — процессы пользователей перестают убираться после выхода, и на терминальном сервере это со временем превращается в свалку из брошенных ssh-agent и питоновских скриптов. Точечная альтернатива, если очень надо: KillExcludeUsers= для конкретной учётной записи. Root там исключён по умолчанию.

Мой порядок действий, когда прилетает задача «сделай, чтобы работало после ребута»: сначала выяснить, точно ли нужна именно пользовательская служба; если да — включить linger и проверить его машинно; затем привести [Install] к default.target и пересоздать симлинк; добавить Restart и RestartSec, потому что их забывают всегда; и только потом заводить мониторинг. На что можно забить: на тонкую настройку зависимостей внутри пользовательской иерархии и на попытки поймать порядок старта относительно системных юнитов — пользовательский менеджер всё равно не видит системных целей вроде network-online.target, так что в подавляющем большинстве случаев хватает корректного Restart= с разумным RestartSec= и повторных попыток в самом скрипте.

Проверка перед сдачей сервера: перезагрузите его и НЕ заходите по SSH десять минут. Потом зайдите и посмотрите uptime службы. Если счётчик показывает секунды с момента вашего входа — вы ничего не починили, linger отсутствует.
Порядок действий: Когда пользовательская служба вообще не нужна: мой чек-лист приоритетов — схема
Порядок действий: Когда пользовательская служба вообще не нужна: мой чек-лист приоритетов. Открыть схему в полном размере

Частые вопросы

Чем systemctl --user enable отличается от loginctl enable-linger?

Это две независимые настройки. systemctl --user enable создаёт симлинк в ~/.config/systemd/user/default.target.wants/ и говорит пользовательскому менеджеру, что запускать при своём старте. loginctl enable-linger заставляет сам менеджер user@UID.service запуститься при загрузке системы и не останавливаться после выхода пользователя. Без linger enable отработает только тогда, когда вы залогинитесь, — то есть автозапуска при ребуте не будет.

Как проверить, включён ли linger, из скрипта мониторинга?

Командой loginctl show-user <имя_или_UID> --property=Linger — она отдаёт строку вида Linger=yes и предназначена именно для машинного чтения. Быстрая альтернатива при разборе чужого сервера: посмотреть содержимое каталога /var/lib/systemd/linger, где для каждого пользователя с включённым linger лежит пустой файл с его именем.

Почему systemctl --user выдаёт Failed to connect to bus, хотя служба работает?

Потому что вы попали в оболочку через sudo -u или su, а они не регистрируют новую logind-сессию целевого пользователя и не выставляют XDG_RUNTIME_DIR и адрес D-Bus. Сам менеджер при этом жив. Решения: выставить XDG_RUNTIME_DIR=/run/user/UID и DBUS_SESSION_BUS_ADDRESS вручную, зайти через machinectl shell user@ (пакет systemd-container) либо от root обратиться напрямую — systemctl --user -M user@ status unit.

Какой WantedBy писать в пользовательском юните?

WantedBy=default.target. Цели multi-user.target в пользовательском экземпляре systemd нет — это цель системного менеджера, и симлинк уедет в каталог, который никто не активирует. Если правите WantedBy у уже включённого юнита, обязательно сделайте systemctl --user disable, а затем enable, иначе останется висеть старый симлинк.

Поможет ли KillUserProcesses=no вместо linger?

Нет. Этот параметр влияет только на убийство процессов в scope-юните сессии, но не отменяет остановку пользовательского менеджера после последнего logout и уж точно не запускает его при загрузке. В Debian он и так по умолчанию no. На терминальных серверах выключение оставляет мусор из брошенных процессов. Если очень нужно исключение при KillUserProcesses=yes — точечно используйте KillExcludeUsers= для конкретной учётной записи.

Когда лучше вообще не использовать пользовательскую службу?

Когда вам нужен обычный фоновый демон под непривилегированной учётной записью. В этом случае системный юнит с User= и Group= проще: он стартует до logind, не зависит от linger, нормально читается в journalctl и не ломается при заходе через sudo -u. Пользовательские юниты оправданы для rootless-контейнеров, собственного keyring и сценариев, где процессу нужен свой /run/user.

Нужен ли linger для rootless Docker и Podman?

Да. И rootless Docker (docker.service в пользовательском менеджере), и Podman с Quadlet работают как --user юниты. Документация Docker и Red Hat прямо требует loginctl enable-linger для старта при загрузке. Для Quadlet автозапуск задаётся секцией [Install] с WantedBy=default.target в самом .container-файле, systemctl enable к сгенерированным юнитам неприменим.

Столкнулись с похожей задачей? Обращайтесь — решим

Если у вас происходит что-то из описанного в этой статье — или любая другая проблема с ИТ-инфраструктурой, — обращайтесь в любое время. Мои специалисты и я лично разберём ситуацию, найдём настоящую причину и доведём до решения.

Возьмёмся и за разовую задачу, и за постоянное обслуживание. Первичная консультация — бесплатно и без обязательств.

📞 +7 903 729-62-41 💬 MAX: +7 903 729-62-41 ✈ Telegram @ITfresh_Boss

С уважением, Семёнов Евгений Сергеевич, директор «АйТи Фреш» — IT-аутсорсинг для компаний до 50 рабочих мест, 15+ лет практики

Источники

© ООО «АйТи-Фреш» · Москва · Все статьи