Статья о том, как я получил root‑права на CMF Phone 1, чтобы развернуть на нём домашнюю инфраструктуру.
Долгое время моя личная инфраструктура работала на небольшом VPS от Hetzner. Там крутились несколько веб‑приложений, удалённый браузер Surf, Caddy и прочие вспомогательные сервисы. Ничего особо серьёзного. Всё работало, просто мне не нравилось за это платить.
Особенно остро проблема ощущалась с Surf, одним из моих приложений. Самые дешёвые виртуалки с общими ресурсами справлялись с нагрузкой, пока Chrome не приходилось заниматься чем‑то посерьёзнее. Тут сразу становилось заметно, насколько ему не хватает ресурсов. Виртуалки с выделенными ядрами CPU решали проблему, но стоили уже столько, что ежемесячные расходы на браузер для личных нужд начинали казаться сомнительной затеей.
Покупка ещё одного компьютера тоже не выглядела привлекательным выходом. Цены на DRAM окончательно сошли с ума, так что момент для сборки новой машины с приличным запасом оперативной памяти был крайне неудачным. Я присматривался к подержанным мини‑ПК и даже подумывал использовать свой основной компьютер как сервер, пока сам за ним не работаю. А потом вспомнил про CMF Phone 1, который у меня уже был.
Восемь ARM‑ядер, 8 ГБ оперативной памяти, 128 ГБ флеш‑памяти, Wi‑Fi 6, 5G‑модем и встроенный аккумулятор, который заодно служит резервным источником питания. И всё это на базе SoC, слишком мощной, на мой взгляд, для того, чтобы просто пылиться в ящике. К тому же телефон уже был куплен. Я достал его, смахнул пыль, немного поэкспериментировал и решил превратить в сервер.
Теперь на нём работают Surf вместе с управляемым экземпляром Chrome, мой трекер личных финансов, сервис для демонстрации экрана и ещё несколько небольших веб‑приложений. Все они автоматически восстанавливают работу после перезагрузки, разворачиваются из Git и остаются доступными, даже когда телефон переключается между сетями. Так что теперь именно этот смартфон полноценно заменяет мне VPS.
Первая неудачная идея: заменить Android
Казалось, что самый простой и правильный путь — установить на телефон обычный дистрибутив Linux. Для CMF Phone 1 существует порт postmarketOS, система загружается, а на странице устройства столько зелёных отметок, что легко проникнуться неоправданным оптимизмом. Я как раз проникся.
А вот на всё, что было помечено как неработающее, я обратил куда меньше внимания: Wi‑Fi, Bluetooth, аппаратное ускорение и большинство других компонентов, без которых телефон сложно использовать как небольшой сервер. В итоге я увидел заставку postmarketOS, а следом — чёрный экран. Теперь у меня не было ни сервера, ни телефона.
Восстановление штатной Nothing OS превратилось в отдельный квест. Утилита для перепрошивки требовала Windows, поэтому я установил её в QEMU, повозился с пробросом USB и драйверами MediaTek, посмотрел, как намертво зависает прошивальщик, а затем всё‑таки перешёл на настоящую Windows и восстановил заводские образы прошивки.
В какой‑то момент телефон превратился в программный «кирпич» и показывал только чёрный экран. Я всерьёз подумал, что превратил совершенно исправное устройство в дорогое пресс‑папье.
К счастью, телефон удалось оживить. Урок я усвоил: в Android уже есть рабочие драйверы для всего этого железа. Wi‑Fi, управление питанием, аккумулятор, GPU, модем и всевозможные специфические особенности производителя — всё уже работает. Отказываться от этого ради более привычного пользовательского пространства было плохой идеей.
На самом деле мне вовсе не требовалось превращать смартфон в полноценную Linux‑машину. Нужно было лишь обеспечить стабильную работу Linux‑приложений, оставив Android заниматься тем, с чем он и так отлично справляется: взаимодействием с железом.
Termux как хостовая ОС
Во второй попытке я сохранил штатный Android, а в качестве основного окружения выбрал Termux.
Termux предоставляет мне OpenSSH, runit, Caddy, Cloudflared, пакетный менеджер и вполне привычный набор Unix‑инструментов. После перезагрузки Termux:Boot запускает супервизор процессов и SSH. Благодаря Tailscale у телефона есть постоянный приватный IP‑адрес, поэтому с любого устройства в моей частной сети Tailscale (tailnet) я могу просто выполнить:
ssh cmf
Termux — не виртуальная машина. Его процессы работают с тем же ядром Linux, что и Android, но пользовательское пространство на базе Bionic достаточно сильно отличается от обычной Debian, поэтому готовые образы Linux‑приложений нельзя просто взять и запустить в Termux. Впрочем, это разделение оказалось даже полезным: Termux остался небольшим управляющим слоем хоста, а каждое приложение получило привычную ему файловую систему Linux.
За работой самих сервисов следит runit. Система управления питанием Android отлично справляется со своей обычной задачей, но совершенно не подходит устройству, которое пытается работать как сервер. Поэтому я добавил в свою конфигурацию Ansible отдельный профиль настроек Android‑хоста. Он активирует постоянную блокировку перехода в спящий режим (wake lock), отключает лёгкий и глубокий режимы простоя (light/deep idle), снимает ограничения на фоновую работу для Termux, Termux:Boot и Tailscale, отключает ограничитель дочерних процессов, не даёт Wi‑Fi переходить в спящий режим и настраивает Tailscale как постоянно активный VPN.
При этом важнее любой отдельной настройки то, как вся система восстанавливается после перезагрузки. Сначала загружается Android, затем постоянно активный VPN восстанавливает подключение Tailscale, Termux:Boot запускает runit, а тот поднимает все постоянно работающие сервисы. После этого проверки работоспособности (health checks) убеждаются, что сервисы доступны как локально, так и извне. Теперь телефон может перезагрузиться без моего участия — не нужно даже следить за тем, когда это произойдёт.
Android boot -> Tailscale always-on VPN -> Termux:Boot -> runit -> resident services -> local and public health checks
Это не совсем обычный Linux‑сервер. Здесь нет ни systemd, ни привычного демона Docker, и делать вид, будто это не так, бессмысленно. Зато есть ядро Linux и весьма функциональное пользовательское пространство. Как выяснилось, этого вполне достаточно.
Насколько хорошо вы разбираетесь в устройстве Linux и администрировании серверов? Проверить себя можно с помощью вступительного теста курса «Администратор Linux. Продвинутый уровень».
Вторая неудачная идея: PRoot
Большинство моих приложений уже распространялись в виде OCI‑образов для Linux ARM64. С помощью proot‑distro их оказалось на удивление легко запускать в окружении Debian, причём без каких‑либо изменений самих приложений.
PRoot перехватывает операции с файловой системой и процессами в пользовательском пространстве, создавая для обычного процесса Termux иллюзию, что он работает внутри корневой файловой системы Debian. Но полноценной контейнерной изоляции здесь нет. Все процессы по‑прежнему используют общее ядро Android, одно сетевое пространство имён и UID Termux. Зато как слой совместимости для приложений PRoot очень удобен: ему не нужны ни root‑права, ни специальное ядро.
Поначалу обычные веб‑сервисы работали таким способом без проблем. Каждое приложение получило проверенную корневую файловую систему, порт на loopback‑интерфейсе и отдельный сервис runit. Caddy работал непосредственно в Termux и перенаправлял запросы к нужным портам в зависимости от имени хоста.
Исключением оказался Surf с его браузерной нагрузкой, чувствительной к производительности и задержкам. Запуск процессов, загрузка библиотек, обход каталогов, чтение профилей браузера и передача данных захвата — всё проходило через слой трансляции PRoot в пользовательском пространстве. Вычислительных ресурсов хватало, но Chrome не мог эффективно их использовать. Поэтому я получил root‑права на телефоне. Не для того, чтобы заменить Android, а чтобы нормально смонтировать ту же файловую систему Debian и запускать приложения внутри неё через настоящий chroot.
Жизненным циклом сервисов по‑прежнему управлял runit из Termux, конфигурация загружалась из прежнего источника, а данные приложений оставались в хранилище Termux. Просто теперь приложения обращались к ядру Android через нативные системные вызовы (syscall), минуя PRoot. Разница в производительности оказалась огромной!
Когда этот способ заработал стабильно, оставлять небольшие сервисы под PRoot уже не было особого смысла. Теперь они запускаются точно так же. На моём рабочем компьютере для каждого ARM64-образа определяется конкретный дайджест и экспортируется его файловая система. Затем Ansible проверяет её и устанавливает на телефон. Небольшая вспомогательная утилита с root‑правами создаёт отдельное пространство имён монтирования (mount namespace), подключает необходимые пути через bind‑монтирование (bind mount), переходит в файловую систему с помощью chroot, сбрасывает привилегии и запускает исходную точку входа (entrypoint) образа.
При этом ни Docker, ни компилятор на самом телефоне не нужны. Всё это по‑прежнему окружения для обеспечения совместимости, а не полноценная изоляция с точки зрения безопасности. Сервисы используют общее ядро Android и сетевой стек, а отдельные пространства имён монтирования нужны прежде всего для того, чтобы управлять монтированиями и освобождать ресурсы предсказуемым образом.
Ещё я потратил неприлично много времени, пытаясь связать графический стек Debian с GPU Mali в телефоне через VirGL и Android Vulkan. В итоге добился заветных галочек напротив аппаратного композитинга, но вместе с ними получил артефакты при отображении страниц и ухудшение производительности… В конце концов скучный вариант с программным рендерингом оказался быстрее.
Инфраструктура, а не куча команд из истории терминала
К этому моменту телефон уже мог запускать все нужные приложения, но мне не хотелось получить сервер, который приходится обслуживать вручную (pet server), собранный из команд, о которых я забуду через неделю. Поэтому я перевёл всю конфигурацию хоста под управление Ansible. Версии, определения сервисов, маршруты, настройки энергопотребления, секреты и проверки работоспособности теперь хранятся в одном приватном репозитории.
Процесс развёртывания выглядит примерно так:
release or OCI image -> checksum/digest pinned in Git -> Ansible over SSH -> versioned files on the phone -> atomic current symlink -> runit service -> local health check -> public edge check
Каждый релиз привязан к конкретному дайджесту или контрольной сумме и устанавливается в отдельный каталог с указанием версии. Переключение между версиями происходит через атомарно обновляемую символическую ссылку current. Если проверка контрольной суммы или работоспособности не проходит, развёртывание останавливается. Для отката достаточно вернуть прежний дайджест или контрольную сумму и повторно применить конфигурацию. Данные приложений хранятся отдельно от релизов.
После небольшой первоначальной настройки вручную — установки трёх Android‑приложений, получения root‑прав на телефоне, предоставления Termux прав суперпользователя и настройки SSH‑доступа — всё остальное управляется из того же репозитория. Я специально сделал приведение хоста к описанному в конфигурации состоянию максимально простым. На рабочем компьютере достаточно выполнить:
make phone make phone-status make phone-edge-check
При повторном применении конфигурации файлы, которые не изменились, остаются нетронутыми, а исправно работающие сервисы не перезапускаются. Но главное, эта конфигурация пригодится, даже если телефон выйдет из строя. Другой ARM64-смартфон с возможностью получения root‑прав можно будет привести к тому же состоянию, не пытаясь восстановить последовательность команд по истории терминала.
Секреты не хранятся в Git‑репозитории на телефоне, потому что самого репозитория там нет. Значения Ansible Vault хранятся в зашифрованном виде в репозитории инфраструктуры. Пароль для Vault я получаю на основе криптографической подписи фиксированного проверочного сообщения, которую создаёт SSH‑агент 1Password. Благодаря этому закрытый ключ остаётся в 1Password, а телефону доступ к нему вообще не нужен. Во время развёртывания Ansible формирует только те значения, которые необходимы каждому сервису для работы, и сохраняет их в приватном хранилище Termux.
Как обеспечить доступ к телефону из интернета
Следующей задачей стала организация доступа извне (ingress). Домашний интернет не даёт такой же удобной конфигурации с постоянным адресом, как VPS, а открывать на роутере доступ к SSH и куче случайных портов приложений мне совсем не хотелось. К тому же я хотел, чтобы телефон оставался телефоном хотя бы в одном отношении: его можно отключить от зарядки, взять с собой куда угодно, подключить к интернету, и сервер продолжит работать.
Для HTTP‑приложений я использую Cloudflare Tunnel. Cloudflared устанавливает исходящее соединение с телефона, Cloudflare направляет через него запросы к соответствующим доменным именам, а Caddy перенаправляет их нужным локальным сервисам.
Internet -> Cloudflare Tunnel -> Caddy on 127.0.0.1 -> application on 127.0.0.1
Открывать входящие порты на роутере для этих сервисов не нужно. Cloudflared достаточно исходящего соединения, поэтому, когда я переключаю телефон на другую сеть, туннель переподключается, а сервисы остаются доступны по прежним доменным именам. Для администрирования аналогичную задачу решает Tailscale. Аккумулятор позволяет телефону пережить смену сети без отключения, а публичным сервисам совершенно не важно, через какой именно Wi‑Fi они работают.
Но для бэкенда удалённого браузера Surf понадобилась другая схема. Его прямое соединение чувствительно к задержкам, TLS завершается на самом сервере, а старый iPad, с которого я подключаюсь, проверяет заранее закреплённую идентичность сервера (pinning). Дома Cloudflare DDNS поддерживает DNS‑запись в режиме DNS only, обновляя её при смене публичного IP‑адреса, а роутер пробрасывает один порт на Surf. В локальной сети iPad подключается напрямую к телефону.
Оставалось решить проблему доступа из других сетей. Обычный Cloudflare Tunnel завершает TLS‑соединение на стороне Cloudflare, а для Surf с его проверкой идентичности сервера такой вариант не подходит. Решение нашлось: я завернул весь TLS‑трафик Surf внутрь обычного WebSocket‑соединения. Cloudflare принимает и проксирует WebSocket, но само аутентифицированное соединение Surf внутри него остаётся зашифрованным от клиента до сервера.
Разумеется, это добавляет задержку. При подключении извне обычно появляется примерно один дополнительный цикл передачи запроса и ответа по сети (round trip). При первом тестировании задержка соединения с iPad составляла около 60 мс. Зато всё работает через туннель, которому достаточно исходящего подключения к интернету, причём на операционной системе 2012 года и без установки Tailscale на iPad.
Именно тогда вся эта затея с телефоном стала выглядеть слегка абсурдной, но в хорошем смысле. Я оставил смартфон подключённым к зарядке дома, поехал в офис, с MacBook подключился по SSH через Tailscale к своему рабочему компьютеру и телефону, а затем воспользовался своим старым iPad через запущенный на телефоне Surf. VPS больше не было. И это было невероятно приятное чувство свободы.
Что в итоге работает на телефоне
Вся система состоит из двух уровней. Android и Termux отвечают за работу с железом, сеть, доступ извне и управление сервисами. Linux‑приложения получают привычную файловую систему и в остальном не вмешиваются в работу хоста.
Уровень |
За что отвечает |
Компоненты |
Хост на Android / Termux |
Работа с железом, сеть, доступ извне, управление сервисами |
runit, Tailscale, Caddy, Cloudflared, DDNS, панель мониторинга |
Linux‑приложения в окружении с root‑правами |
Совместимость и выполнение прикладных задач |
Surf и Chrome, Finances, Screen Share и другие приложения |
Самую серьёзную нагрузку создаёт Surf, который позволяет запускать современный Chromium на старых iPhone и iPad. На телефоне внутри окружения Debian работают настольный Chrome (свежая версия для ARM64) и бэкенд Surf. iPad mini получает видеопоток H.264 и звук, а в обратную сторону отправляет данные о касаниях, вводе с клавиатуры, переключении вкладок и навигации. Именно из‑за Surf я с самого начала так внимательно относился к накладным расходам на системные вызовы и задержкам.
Помимо него, на телефоне работает мой трекер личных финансов. Он учитывает регулярные доходы и расходы, разовые операции, месячные лимиты трат и прогнозирует остаток средств на каждый следующий день. В отличие от артефактов приложений, которые всегда можно развернуть заново, его база SQLite содержит важные для меня данные. Поэтому я настроил автоматическое резервное копирование на другое устройство и проверил, что из этих копий действительно можно восстановиться.
При этом названия приложений не зашиты в какой‑то универсальный «фреймворк для сервера на телефоне». Чтобы добавить ещё один сервис, достаточно подготовить неизменяемый артефакт, описание сервиса для runit, явно определить правила работы с данными, добавить проверку работоспособности и, если нужно, маршрут в Caddy.
Последним дополнением стала система мониторинга. Я по‑прежнему могу подключиться по SSH, проверить состояние runit, посмотреть логи, проверить доступность публичных маршрутов и напрямую запросить данные у Android. Но теперь, чтобы быстро понять, что происходит с сервером, делать всё это необязательно. Один нативный сервис собирает данные о загрузке всех восьми ядер CPU, оперативной памяти, накопителе, времени работы системы, аккумуляторе, температурах, доступности сервисов из локальной сети и интернета, а также обо всех автоматически обнаруженных сервисах runit. Он хранит историю за ограниченный период и предоставляет встроенный веб‑интерфейс на Vue по адресу https://dash.cmf, доступный только из локальной сети или через Tailscale.

Интерфейс просмотра логов тоже автоматически находит каталоги сервисов во время работы. Поэтому при добавлении нового приложения не нужно отдельно прописывать его название в интерфейсе. Это гораздо удобнее, чем каждый раз подключаться по SSH, просто чтобы выяснить, какой сервис опять засыпает логи сообщениями.
Стоит ли вам повторять этот эксперимент?
Если у вас без дела лежит относительно современный ARM64-смартфон, на котором можно получить root‑права, идея не настолько безумная, как кажется. Вы получаете бесшумное устройство с низким энергопотреблением, флеш‑памятью, Wi‑Fi, встроенным экраном на случай восстановления системы и аккумулятором, который работает как небольшой ИБП. Штатный Android уже поддерживает всё железо, а Termux и chroot с root‑правами позволяют запускать на удивление много обычных Linux‑приложений.
Я бы не стал хранить на таком устройстве незаменимые данные без автоматического резервного копирования на другой носитель. И не рассчитывал бы, что chroot обеспечит безопасную изоляцию потенциально вредоносных приложений. Получение root‑прав расширяет границу доверия, Android остаётся довольно необычной платформой для сервера, а настольный Chrome с программным рендерингом не сравнится по производительности с полноценной рабочей станцией с GPU.
Но для нескольких личных сервисов это действительно полезный вариант, а не просто эксперимент ради эксперимента. Особенно если альтернатива — бесконечно платить за VPS, который либо слишком медленный, либо раздражающе дорогой.
Смартфон всё ещё остаётся довольно странным сервером. Все приложения используют одно ядро, Android периодически приходится напоминать, чтобы он не пытался всё «оптимизировать», а очередное обновление системы в любой момент может преподнести новый сюрприз.
Зато он тихий, работает от аккумулятора, достаточно быстрый, доступен из любой точки, полностью восстанавливается из Git и уже стоит у меня дома. Всё началось с желания сэкономить на VPS. А закончилось смартфоном с root‑правами, на котором работает вся моя личная инфраструктура. И почему‑то это приносит гораздо больше удовольствия.:)
P. S. Эту статью обсуждали на Hacker News. В лучших традициях HN неожиданно большая часть дискуссии развернулась вокруг заголовка. Случайно возникшее обсуждение лингвистики меня немало позабавило.

Перенести личные сервисы с VPS на собственное устройство — лишь часть задачи. Дальше нужно разобраться с сетевым взаимодействием и запуском приложений. Углубиться в эти темы можно на бесплатных вебинарах:
5 ноября, 20:00. «Стек сетевых протоколов и с чем его едят. На примере TCP/IP». Записаться
10 ноября, 20:00. «CRI — обзор контейнерных runtime». Записаться
Больше обучающих материалов по инфраструктуре можно посмотреть в дайджесте.
Комментарии (12)

zompin
22.10.2026 15:18<s>Продам сервер OnePlus 9 Pro (12/256). Состояние муха не любилась)</s>
Дать вторую жизнь железяке (причем вполне живой) дело хорошее
P. S. Не продам)

AnatoliVeresk
22.10.2026 15:18Такое устройство хорошо для обучения Linux. Я развернул сервер на плеере DIgma c 3.5` накопителем. По итогу освоил консоль Linux, выучил команды, демоны, потом с облегчением продал плеер.

retab
22.10.2026 15:18В свое время тоже думал про такую концепцию. Для хранилища лучше всего использовать NAS решения. В итоге можно даже рейд развернуть.
А сам телефон только как мозги использовать.

DS2
22.10.2026 15:18Как давно я использовал старый смартфон как часы и он постоянно висел на питании. В итоге аккум распух, выдавил заднюю крышку и выгнул дисплей.
А как в новых смартфонах с этим обстоит дела?
VADemon
22.10.2026 15:18Можно выставить верхний порог ограничения зарядки из коробки в интервале 75-95% (зависит от прошивки). Костыль для старых устройств с рутом: программно дёргать вкл/откл. зарядки у порта.

becefi
22.10.2026 15:18MacBook подключился по SSH через Tailscale к своему рабочему компьютеру и телефону, а затем воспользовался своим старым iPad через запущенный на телефоне Surf.
омг... лучше уж VPS

VADemon
22.10.2026 15:18С голой жопой в Турции, но с макбуком и айфоном ;) А еще домен с продлением за 30 баксов.
JBFW
В линуксе все сервисы работают с одним ядром, если там не куча виртуалок
Андроид ядро в общем тоже Линукс, и в некоторых случаях поднять обычный Дебиан можно прямо на нем. Но без доступа к RxTx и Ethernet порта - рискованно