Адаптивный пересказ поста my server is a phone now (seg6.space). Текст и диаграммы здесь мои; за первоисточником и деталями — по ссылке. Это чужой личный проект — я лишь пересказываю его опыт.
Идея звучит как шутка: взять старый смартфон из ящика и сделать из него домашний сервер вместо оплачиваемого VPS. Но если присмотреться, телефон — это неплохой мини-сервер: несколько ARM-ядер, гигабайты памяти, флеш-накопитель, Wi-Fi, а главное — встроенная батарея, то есть бесплатный UPS. Ниже — как это довели до рабочего состояния, включая пару тупиковых поворотов (они полезнее, чем гладкая инструкция).
Зачем
Личная инфраструктура автора жила на дешёвом VPS: remote-браузер, финтрекер, screen-share, пара мелких web-приложений. Работало, но платить не хотелось — особенно за выделенный CPU, который нужен браузеру под нагрузкой. Покупать отдельную машину тоже не тянуло (память сейчас дорогая). А в ящике лежал уже оплаченный телефон с 8 ARM-ядрами, 8 ГБ RAM, 128 ГБ флеша, Wi-Fi 6 и 5G. Железо явно простаивало.
Плохая идея №1: снести Android
Первый, «чистый» вариант — прошить нормальный Linux (у телефона есть порт postmarketOS). На бумаге всё зелёное, на деле — сломаны Wi-Fi, Bluetooth, аппаратное ускорение и половина того, что делает телефон полезным как сервер. Итог: сплэш-экран, потом чёрный экран — ни сервера, ни телефона. Восстановление стоковой прошивки превратилось в отдельный квест (утилита требовала Windows, USB-passthrough, драйверы MediaTek, soft-brick и почти-кирпич).
Вывод, который стоит запомнить до того, как повторять: у Android уже есть рабочие драйверы на всё это железо — Wi-Fi, питание, батарею, GPU, модем. Выбрасывать их ради «привычного» Linux — плохая сделка. Телефону не нужно становиться обычной Linux-машиной. Нужно, чтобы он надёжно запускал Linux-приложения, пока Android продолжает делать свою низкоуровневую работу.
Termux как хост-система
Второй заход: оставить сток-Android, а хостом сделать Termux
. Termux даёт OpenSSH, runit, Caddy, Cloudflared, пакетный менеджер и достаточно юниксового окружения. Termux:Boot поднимает супервизор и SSH после ребута, а Tailscale
даёт телефону стабильный приватный адрес — и с любой машины в своей сети можно просто:
ssh cmfВажный нюанс: Termux — не виртуальная машина. Его процессы работают прямо на ядре Android, но userspace на Bionic достаточно отличается от Debian, чтобы обычные Linux-образы просто так в нём не завелись. Это ограничение обернулось плюсом: Termux остаётся маленьким «control plane» хоста, а каждое приложение приносит свою Linux-файловую систему.
Отдельная боль — управление питанием. Для телефона оно отличное, для «сервера» — вредное: Android агрессивно усыпляет фон. Поэтому в host-профиль (применяется через Ansible) входят: persistent wake lock, отключение idle-режимов, вывод Termux / Termux:Boot / Tailscale из-под фоновых ограничений, запрет засыпания Wi-Fi и Tailscale как always-on VPN.
Важнее любой отдельной настройки — цепочка восстановления, чтобы телефон переживал ребут без участия человека:
Порядок такой: Android загрузился → always-on VPN поднял Tailscale → Termux:Boot стартовал runit → runit поднял все сервисы → health-check проверил локальный и публичный путь. Ни systemd, ни обычного Docker-демона здесь нет — и не нужно притворяться, что они есть. Это Linux-ядро с дееспособным userland, и этого достаточно.
Плохая идея №2 (наполовину): proot, потом chroot
Приложения уже собраны как OCI-образы под ARM64. Запустить их под Debian помог proot-distro: PRoot в userspace перехватывает файловые и процессные вызовы и заставляет обычный Termux-процесс думать, будто он внутри Debian-rootfs. Это не граница контейнера (общие ядро, сеть и UID), но как слой совместимости — прекрасно: не нужен ни root, ни специальное ядро.
Обычные web-сервисы так и поехали: у каждого — своя проверенная rootfs, loopback-порт и runit-сервис, а Caddy в Termux роутит hostname на нужный порт. Исключением стал latency-чувствительный браузерный workload: старт процессов, открытие библиотек, обход путей, чтение профилей — всё это шло через userspace-трансляцию PRoot. CPU был, но браузер не мог до него эффективно дотянуться.
Решение — не заменить Android, а получить нативные syscalls: телефон рутанули, чтобы смонтировать ту же Debian-ФС по-нормальному и войти в неё настоящим chroot. Жизненным циклом по-прежнему владеет runit из Termux, конфиг — из того же места, данные — в хранилище Termux; workload просто дошёл до ядра Android напрямую. Разница в отзывчивости оказалась заметной, и после этого мелкие сервисы тоже перевели на chroot.
Схема установки: рабочая станция резолвит ARM64-образ в точный digest и экспортирует его ФС; Ansible проверяет и ставит на телефон; маленький root-helper создаёт приватный mount namespace, биндит нужные пути, входит через chroot, сбрасывает привилегии и запускает штатный entrypoint образа. Ни Docker, ни компилятор на телефоне не нужны.
Отдельная поучительная неудача: попытка пробросить графику Debian на GPU Mali через VirGL/Vulkan дала «галочки» аппаратного композитинга — вместе с битыми страницами и худшей производительностью. Скучный софтварный рендер оказался быстрее. Иногда правильный ответ — не гнаться за ускорением.
Инфраструктура, а не свалка из истории команд
Чтобы это не был «питомец», собранный из команд, которые забудешь через неделю, весь хост загнали в Ansible-managed state: версии, определения сервисов, роуты, настройки питания, секреты и health-check — в одном приватном репозитории.
Поток деплоя примерно такой:
релиз / OCI-образ
→ digest/checksum, зафиксированный в Git
→ Ansible по SSH
→ версионированные файлы на телефоне
→ атомарный симлинк current
→ runit-сервис
→ локальный health-check
→ публичный edge-checkРелизы прибиты по digest/checksum и ставятся в версионированные каталоги за атомарным симлинком current. Провал контрольной суммы или health-check останавливает деплой; откат — вернуть pin и применить заново. Данные приложений лежат отдельно от релизов. После маленького ручного bootstrap (поставить три Android-приложения, рутануть, дать Termux root, авторизовать SSH) остальным владеет репозиторий:
make phone
make phone-status
make phone-edge-checkПовторный прогон не трогает неизменившиеся файлы и не перезапускает здоровые сервисы. И — что ценно — если этот телефон умрёт, другой rootable ARM64-телефон приводится к тому же состоянию без реконструкции истории команд.
Секреты на телефоне не хранятся вовсе: они зашифрованы Ansible Vault в репозитории, а пароль от Vault добывается подписью фиксированного challenge через 1Password SSH-agent — приватный ключ остаётся в 1Password, телефон к нему доступа не имеет. При деплое на телефон попадают только нужные runtime-значения.
Как трафик доходит до телефона
Домашний интернет не даёт статики «как на VPS», выставлять SSH и случайные порты через роутер не хочется, а ещё телефон должен оставаться телефоном: отключил, унёс, подключил к другому Wi-Fi — сервер работает.
- HTTP-приложения — через Cloudflare Tunnel. Cloudflared делает один исходящий коннект, Cloudflare заводит каждый hostname в него, Caddy роутит на нужный loopback-сервис. Никаких inbound-правил на роутере: переносишь телефон в другую сеть — туннель переподключается, и hostname едут за ним. Батарея телефона мостит переезд.
- Администрирование — через Tailscale (тот же принцип: приватный адрес следует за телефоном).
- Latency-чувствительный браузерный бэкенд — особый случай: соединение пиннит идентичность сервера и терминирует свой TLS, а обычный Cloudflare Tunnel завершает TLS на своей стороне. Выход — завернуть весь TLS-поток в обычный WebSocket: Cloudflare видит и форвардит WebSocket, но аутентифицированное соединение остаётся зашифрованным end-to-end внутри. Платишь лишним round-trip латентности, зато работает через outbound-only туннель — даже со старого клиента и без установки VPN на нём.
Стоит ли так делать?
Честно — это скорее увлекательный хак, чем «правильный» сервер, и относиться к нему стоит соответственно:
За:
- железо уже оплачено и простаивало; ARM-SoC телефона реально тянет нагрузку;
- встроенная батарея = бесплатный UPS, переживает переезды между сетями;
- только исходящие соединения — не нужно выставлять порты наружу;
- всё в Ansible: воспроизводимо, переносимо на другой телефон.
Против:
- нужен root и возня с Android-питанием — это не «поставил и забыл»;
proot/chroot— слои совместимости, а не безопасности: общие ядро и сеть, изоляции контейнера здесь нет;- нет systemd и обычного Docker — часть привычного тулинга не переносится;
- путь к рабочему состоянию вымощен soft-brick’ами и тупиками (postmarketOS, VirGL).
Если нужен предсказуемый прод — берите VPS или мини-ПК. Но как способ не выбрасывать хорошее железо, разобраться в Android/Termux/Linux-userspace и получить портативный сервер с батарейкой — затея на удивление рабочая. Главное — не выкидывать то, что и так уже работает (драйверы Android), в погоне за «более правильным» устройством.