Я давно хотел захостить свой небольшой проект — бота, которым пользуюсь сам. Останавливал меня не код, а хостинг. Не хотелось арендовать VPS и настраивать всё удалённо — а потом обновлять и следить, чтобы не упало. Переплачивать за готовый хостинг, лишь бы этим не заниматься, тоже не хотелось. Так это и оставалось идеей.

Зато был старый OnePlus 3T в ящике: флагман 2016 года, Snapdragon 821, 6 ГБ оперативной памяти — рабочий, но не включавшийся годами. Выбрасывать рабочий компьютер не хотелось, а для одного небольшого always-on сервиса это более чем достаточно: пара ватт в простое, своя батарея, никаких ежемесячных платежей.

Загвоздка в том, что это телефон. Загрузчик с завода заблокирован, стоит Android, батарея не рассчитана на постоянную зарядку, и нет вентилятора. Дальше — как я разбирался с каждым из этих пунктов.

Одно условие: всё это возможно только потому, что загрузчик OnePlus 3T в принципе можно разблокировать. На новых телефонах это всё чаще невозможно, так что переиспользование телефона во многом упирается в выбор модели, которую вообще дают разблокировать.

Ставим Linux на телефон

Я ставил postmarketOS: mainline-Linux, systemd, пакеты Alpine — обычная Linux-система, а не Android.

Если нужно просто Linux-окружение, чтобы поэкспериментировать, ничего заменять не обязательно. Termux на Android вместе с proot-distro даёт окружение Debian или Alpine поверх штатной системы, без разблокировки. Но это всё равно Android со всеми его минусами.

Разблокировка и бэкап

OnePlus 3T разблокируется командой fastboot oem unlock — без запроса кода у вендора и без периода ожидания. Разблокировка стирает устройство, поэтому первым делом — полный бэкап штатной системы: все разделы, 56 из 56, 3.4 ГБ, с манифестом имён и размеров. Он пригодился: позже я восстанавливался из него, когда первая сборка не загрузилась.

lk2nd

На msm8996 практичный способ загрузить mainline-ядро — lk2nd, небольшой открытый вторичный загрузчик. Первичный загрузчик подписан Qualcomm и остаётся на месте; lk2nd ставится рядом. lk2nd-msm8996.img прошивается в раздел boot; он загружается при старте, даёт чистый интерфейс fastboot и загружает mainline boot-образ со смещением 512 КБ (первые 512 КБ раздела занимает сам). pmbootstrap про него знает и пишет настоящий boot-образ по нужному смещению автоматически.

Зависает при загрузке

Первая собранная мной postmarketOS зависала на сплэше lk2nd при каждой загрузке. На маке не появлялось USB-сетевого устройства, экран оставался на сплэше, и понять — рано упало ядро или lk2nd не передал управление — было нельзя. Без консоли остаётся только гадать, а msm8996 — это 2016 год: в нём нет того аппаратного USB-отладчика, что есть в более новых Snapdragon, так что единственный отладочный serial — UART на 1.8 В, выведенный на USB-разъём, до которого нужен специальный джиг; его у меня не было, и возиться с ним ради этого не хотелось. Я восстановил бэкап, вернулся на Android и стал искать другой способ увидеть, что происходит.

Получить логи загрузки без UART всё-таки можно: fastboot oem log выводит лог работы lk2nd — какой DTB он нашёл и куда передал управление, — а ядро можно настроить так, чтобы оно сохраняло последний лог в pstore, и зависшую загрузку можно было прочитать потом. Пробовал и обычные для Qualcomm параметры ядра против таких зависаний — clk_ignore_unused, arm-smmu.disable_bypass, console=ttyMSM0 — ничего не дало.

Самый свежий релиз postmarketOS разом обновил многое, в том числе ядро — с 6.3.1, которое не меняли годами, до 6.19.5, и этот релиз вешает OnePlus 3T до initramfs. Я не выяснял, дело в самом ядре или в чём-то ещё новом в релизе; откатился на предыдущую версию, где ядро всё ещё 6.3.1, пересобрал — и оно загрузилось. USB-сеть поднялась на 172.16.42.1, фреймбуфер ожил.

Не монтируется файловая система

Загрузка доходила до initramfs и останавливалась:

Trying to mount subpartitions ..
ERROR: failed to mount subpartitions

и сваливалась в отладочную консоль, доступную по telnet на 172.16.42.1:23. Дело в размере сектора накопителя. Раздел userdata на телефоне — устройство с 4-КБ сектором UFS, а таблица разделов в образе была записана из расчёта 512-байтных секторов, так что ядро не могло прочитать вложенный GPT и не создавало подразделы. Лечится одним флагом — pmbootstrap install --sector-size 4096, — которого в профиле OnePlus 3T просто нет, хотя у его собратьев по msm8996 он выставлен.

fastboot не пишет userdata

Ещё одно: fastboot flash userdata у меня просто не работает — пишет «успех», но ничего не записывает, старая файловая система остаётся на месте. Прошивать приходится через TWRP recovery, записывая образ прямо в блочные устройства by-name через dd (или simg2img для sparse-образа). dd TWRP урезанный(как минимум в той версии, которую я использую): размеры — только в байтах, а conv=fsync он не понимает.

После этого телефон загрузился как обычный Linux: Linux op3t 6.3.1-msm8996 aarch64, postmarketOS, 6 ГБ памяти, корневой раздел растянут на весь диск, есть доступ по SSH.

Делаем сборку воспроизводимой

Собрать образ один раз руками — нормально. Повторять все после каждого изменения — нет, и одно из моих правил было: каждый ручной шаг должен оказаться в скрипте. Так что весь процесс я свёл в один скрипт, который запускает pmbootstrap в контейнере и прошивает с мака.

pmbootstrap не работает на macOS — ему нужны Linux-chroot и loop-устройства — поэтому он работает в привилегированном Linux-контейнере. Docker Desktop на Apple Silicon поднимает arm64 Linux виртуальную машину, так что pmbootstrap собирает aarch64-образ нативно, без QEMU в цепочке. Каталог проекта примонтирован внутрь, собранные образы попадают обратно на мак, а прошивка идёт с мака по USB.

Чтобы скрипт заработал надёжно, пришлось исправить несколько багов.

Пустой образ

Одна сборка сообщила об успехе, прошивка побитово сошлась — и телефон сразу оказался в отладочной консоли initramfs. Образ был валидным GPT поверх 1.3 ГБ нулей — около 271 ненулевого байта на весь файл.

Оказалось, что внутри контейнера pmbootstrap не мог создать узлы loop-разделов:

ERROR: Unable to find the first partition of /dev/loop0, expected it to be at /dev/loop0p1!

Docker монтирует /dev как tmpfs, поэтому когда losetup -P просит ядро создать /dev/loop0p1, узел не появляется в /dev контейнера. pmbootstrap пишет таблицу разделов, но не может создать и заполнить файловые системы внутри. Монтирование настоящего devtmpfs поверх /dev в контейнере сборки заставляет узлы появиться.

А «успехом» это обернулось из-за второго бага. Установка шла как pmbootstrap … install | tail внутри docker exec sh -lc '…'. У внешнего скрипта стоял set -o pipefail, но он не действует во внутренней оболочке, которую запускает docker exec, так что конвейер вернул код выхода tail — ноль — и реальный сбой остался невидимым. Решение: pipefail во внутренней оболочке плюс проверка после экспорта, которая ищет в образе содержимое файловой системы и прерывается, если его нет.

Backup-GPT не на месте

Запись образа, размером по содержимому (около 1.3 ГБ), на 53-ГБ раздел userdata оставляет backup-заголовок GPT там, где кончается образ, а не в конце раздела:

GPT: Alternate GPT header not at the end of the disk.

Ядро отвергает GPT, у которого backup-заголовок не там, где положено, и подразделы не отображаются. Шаг прошивки теперь перемещает backup-GPT в конец устройства после записи, а потом проверяет, что подразделы появились.

Прошивка

У прошивки нашлось ещё несколько способов сломаться:

  • Образ raw, а не Android-sparse, так что simg2img его отвергает. Скрипт определяет формат по magic-числу и для raw использует dd.

  • adbd отваливается посреди многогигабайтной записи — failed to read copy response: EOF, и устройство пропадает из adb devices. Поэтому скрипт запускается на телефоне отдельно (nohup), а хост опрашивает файл с результатом и переподключает adb, если тот отвалился.

  • Однажды push прошёл проверку по размеру и всё равно дал незагружаемую систему, так что скрипт проверяет, считывая записанную область обратно с устройства и сравнивая SHA-256 с локальным образом, а не только длину.

Серверные проблемы, свойственные телефону

Батарею нельзя просто вынуть

Сервер постоянно подключён к питанию, а литиевый аккумулятор от того, что его держат заряженным и в тепле, стареет быстрее. Напрашивается решение — вынуть аккумулятор и работать от зарядного устройства. Так не получится: контроллер питания рассчитывает на установленную батарею, и без неё кратковременные скачки тока SoC вызывают такую просадку напряжения на шине питания, что устройство перезагружается. (Можно поставить плату-заглушку с мощным стабилизированным источником, но добавлять отдельное железо ради этого не хочется.)

Так что батарея остаётся — как буфер для скачков тока и коротких просадок, не для автономности; переживать длительное отключение питания мне не нужно.

Для буфера заряд лучше держать низким, около 3.7–3.8 В. Как буфер батарея работает одинаково хорошо и при половинном заряде, и при полном, а чем ниже напряжение, тем медленнее она стареет и меньше вздувается, — так что держать её полной незачем. Заряд удерживает небольшой systemd-сервис: каждые 20 секунд он чуть подстраивает лимит входного тока зарядки, чтобы заряд держался у нужного процента. Ток через батарею при этом почти нулевой — она не заряжается и не разряжается, просто держит уровень.

WiFi

WiFi — это чип QCA6174, подключённый по PCIe, и на mainline-ядре PCIe-линк не поднимался:

qcom-pcie 600000.pcie: Phy link never came up

Хост-контроллер PCIe определялся, а устройство за ним — нет; Bluetooth (тот же чип) отдавал -110, таймаут. Я проверил питание и тактирование: регулятор включения WiFi был на 1.8 В, сброс PERST снят, опорная частота — 19.2 МГц. Вся последовательность инициализации была правильной, а линк всё равно мёртв — и я решил, что чип неисправен.

Оказалось, нет. Я загрузил сток-ядро Android через TWRP — и оно сразу увидело чип, [168c:003e]. Железо было в порядке; проблема целиком в инициализации PCIe в mainline-ядре. Я перебрал несколько ядер, чтобы локализовать: 6.3.1 и 6.12.10 одинаково не поднимают линк, 6.19.5 вообще не грузится. Значит, это не регрессия между версиями, а давняя недоработка именно для этого устройства.

Исправление — одна строка в командной строке ядра, на 6.12.10:

pcie_aspm=off pci=nomsi

ASPM отвечает за энергосбережение PCIe; стоит его выключить — и линк поднимается. pci=nomsi форсирует legacy-прерывания, обходя капризный MSI на этом SoC. С обоими:

ath10k_pci 0000:01:00.0: enabling device

wlan0 поднялся, отсканировал, подключился. Bluetooth тоже снова заработал — он на UART, не на PCIe, так что флаги командной строки на него напрямую не влияли; он вернулся, когда чип стал нормально инициализироваться. Та версия pmbootstrap, которуя я использовал, игнорирует командную строку ядра из профиля устройства, так что рабочие флаги пришлось вписать прямо в заголовок Android-boot-образа, по фиксированному смещению, а не штатным конфигом.

Сетевой доступ

Иногда боту нужно показать мне веб-страницу — одноразовую ссылку, которую он присылает в Telegram, — а значит, нужен HTTPS-адрес, который я смогу открыть, и доступ к нему должен быть только у меня. Я перепробовал три варианта, прежде чем остановиться на самом простом.

Tailscale — это mesh-VPN. Его функция Funnel выставляет сервис в публичный интернет по стабильному адресу https://<имя>.ts.net, без домена и без статического IP. (Cloudflare Tunnel делает похожее, но требует домен, поэтому я его не рассматривал.)

Потом я попробовал свести публичную часть к минимуму: включать funnel только пока жива хотя бы одна свежая короткоживущая ссылка и выключать, когда истекает последняя. Бот сам поднимает и гасит тоннель через Tailscale, а небольшой вотчер следит за активными ссылками. Работает, но это слишком громоздко ради того, что вообще не должно быть публичным.

Но публичным ему быть и не нужно. Сервис только мой, и ссылки нужны лишь мне — поэтому хватает tailscale serve: он выставляет локальный порт по адресу https://op3t.<tailnet>.ts.net, и открыть его можно только с моих устройств в tailnet. Валидный HTTPS, без домена, без проброса портов, ничего в публичном интернете — и никакой логики тоннелей в боте: serve настраивается на хосте один раз и остаётся включённым.

Компромисс, на который я пошёл: mesh-only означает, что устройство, с которого я открываю ссылку, тоже должно быть в tailnet, так что Tailscale стоит и на телефоне, включаю по необходимости. Прежние варианты как раз и придумывались, чтобы этого избежать, — но эта сложность себя не оправдывала.

Деплой сервиса

Сервис — небольшой Telegram-бот: Node и TypeScript, хранилище на SQLite. Обычно такой сервис собирают в Docker-образ и запускают в контейнере. Собирать образ на телефоне не хотелось, потому что это могло быть медленно, плюс сильно нагрузило бы само устройство.

Дефицитный ресурс здесь не оперативка — 6 ГБ боту с огромным запасом, — а износ накопителя, нагрузка на CPU и нагрев; телефон охлаждается пассивно, вентилятора в нём нет. Установка зависимостей, компиляция нативного SQLite-биндинга из исходников и прогон TypeScript-компилятора — это длительная нагрузка на CPU пассивно охлаждаемого телефона, ровно то, что он переносит хуже всего, и деплой по принципу «забрать и пересобрать на устройстве» повторяет это каждый раз.

Поэтому ничего из этого на телефоне не происходит. GitHub Actions собирает образ на arm64-раннере — npm ci, нативная компиляция, tsc, всё на настоящей Linux-машине в CI — и отправляет его в реестр. Устройство только забирает готовый образ и запускает. Парадоксально, но контейнеры здесь бережнее к железу, чем запуск бота напрямую: единственный дорогой шаг уходит в CI, а не на телефон.

Для управления контейнерами я поставил Portainer — веб-интерфейс к Docker с логами, состоянием контейнеров и просмотром реестра. Опасался, что он будет постоянно потреблять ресурсы, замерил: в простое он занимает около 80 МБ оперативной памяти и примерно 0% CPU — на устройстве с 6 ГБ это незаметно. Удобно, и ресурсов почти не требует.

В итоге весь деплой выглядит так: CI собрал свежий образ, а Portainer забирает его и пересоздаёт контейнер из небольшого compose-файла.

Сеть в контейнерах

Образ собирается в CI, Portainer готов — казалось, деплой бота окажется простой частью. Но и здесь обнаружились две проблемы.

containerd не стартует

Установка Docker на этой systemd-сборке postmarketOS тянет всё ожидаемое — движок, CLI, containerd, compose-плагин, даже подходящие правила nftables. Но сервис не поднимался. dockerd висел в activating (start) и не доходил до конца, а виноват был containerd:

systemctl status containerd
ExecStart=/usr/local/bin/containerd (code=exited, status=203/EXEC)

203/EXEC означает, что systemd не смог выполнить файл по этому пути — и не мог, потому что там ничего нет. Пакет ставит бинарь в /usr/bin/containerd, а юнит, который он же кладёт, указывает на /usr/local/bin/containerd, апстримный путь по умолчанию. Так как Docker лишь хочет containerd, а не требует его, dockerd не падал сразу — он ждал сокет, который никогда не появится. Решается это drop-in-файлом для systemd, который исправляет путь в ExecStart.

Поднялся, но unhealthy

Docker заработал, контейнер бота поднялся — и завис со статусом unhealthy. В логе одна стартовая строка и тишина, а wget http://127.0.0.1:8000/health отдавал Connection reset by peer. Порт был проброшен наружу, но приложение внутри контейнера ещё не начало его слушать, поэтому docker-proxy принимал соединение и тут же закрывал его: процесс не доходил до запуска своего HTTP-сервера. Первым делом при запуске бот отправляет исходящий запрос — поэтому я стал разбираться с сетью.

Из одноразового контейнера в той же compose-сети шлюз пинговался, а интернет — нет:

ping 172.18.0.1   → ok
ping 1.1.1.1      → 100% packet loss

Я заподозрил DNS. nft показал обратное:

nft list ruleset | grep masquerade
ip saddr 172.18.0.0/16 ... masquerade ... counter packets 0 bytes 0

Ноль пакетов через правило masquerade означает, что пакеты до него не доходят, так что разрешение имён ни при чём — из контейнера вообще ничего не выходило. Виноват был host-файрвол. Его цепочка forward по умолчанию drop и пропускает форвард для интерфейсов с именами docker*, но бот работает в compose-сети, чей бридж называется br-<hash>, а его не пропускало ни одно правило. В nftables пакет, отброшенный в любой базовой цепочке на его пути, отбрасывается окончательно — поэтому до правила masquerade ниже по цепочке дело уже не доходило.

За этим скрывалась вторая проблема — на этот раз с DNS: в /etc/resolv.conf контейнеру прописывался 100.100.100.100, MagicDNS от Tailscale, доступный с хоста, но не изнутри контейнера. Так что даже после починки форвардинга разрешение имён всё равно не заработало бы. Понадобились две правки: правило nftables, пропускающее compose-бриджи, и публичный DNS в конфигурации демона Docker, чтобы контейнеры не наследовали недостижимый адрес. После этого бот подключился к Telegram, начал слушать свой порт и перешёл в статус healthy.

Производительность и масштабирование

Когда всё заработало, остаётся вопрос: справится ли телефон 2016 года с такой нагрузкой. Portainer строит график загрузки CPU по контейнерам, и у бота это ровный 0% в простое с короткими всплесками до 6–8%.

Линейно это не масштабируется. Большая часть всплесков — фиксированная работа, не растущая с числом пользователей: плановый опрос раз в несколько минут, цикл long-poll Telegram, базовая цена пробуждения Node-процесса. Остальное — работа на конкретный запрос, и она ограничена вводом-выводом: эти миллисекунды бот проводит в ожидании ответов по сети, а не в вычислениях, поэтому один event loop успевает чередовать множество таких ожиданий, не загружая ядро. С ростом числа пользователей увеличивается только эта переменная часть, а фиксированная не копируется заново.

Для Node-сервиса важна не «доля от четырёх ядер», а загруженность единственного потока. Node исполняет бота в одном потоке, и остальные три ядра этому процессу ничем не помогают. Поток почти не загружен, и раньше всего дело упрётся не в CPU этого SoC, а во внешние ограничения — лимиты сторонних API и единственного писателя в SQLite.

Поэтому и привычный рецепт против «однопоточности» — запустить по копии процесса на каждое ядро — здесь не подходит и сразу сломал бы бота. Telegram-бот на long polling допускает только одного поллера на токен: второй получит 409 Conflict. А четыре процесса вступили бы в конфликт за один файл SQLite. Настоящее горизонтальное масштабирование выглядело бы иначе: отдельный входной слой, который только принимает сообщения и кладёт их в очередь; очередь с разбиением по пользователю, чтобы действия одного шли строго по порядку; пул stateless-воркеров; Postgres вместо файлового SQLite; и один планировщик с выбором лидера, чтобы каждая задача выполнялась ровно один раз. Одному пользователю ничего из этого не нужно — под такую нагрузку у телефона огромный запас.

Полная картина

Вот общая схема:

            GitHub Actions (arm64)
                   │ build + push
                   ▼
                 GHCR ──pull──►  ┌───────────────────────────┐
                                 │  OnePlus 3T               │
  me ──Tailscale (mesh)──────────┤  postmarketOS / systemd   │
   https://op3t.<tailnet>.ts.net │   └─ Docker               │
                                 │        ├─ Portainer (UI)  │
                                 │        └─ service (bot)   │
                                 │  battery-guard(systemd)   │
                                 └───────────────────────────┘

Телефон работает на postmarketOS — mainline-Linux с systemd. Docker запускает два контейнера: Portainer для управления и самого бота. Образ бота собирается в CI и забирается из реестра; телефон ничего не собирает. Всё доступно только внутри моего Tailscale, ничего в публичном интернете. systemd-сервис держит батарею около половины заряда, а DNS для контейнеров, правило файрвола и остальное встроены в сборку.

Как повторить

Весь проект — это два репозитория. Хостовая часть — пайплайн сборки и прошивки, вспомогательные сервисы и setup-скрипт — находится в github.com/arttttt/oneplus3t-pmos-server.

Настроить устройство с нуля можно быстро — все исправления уже заложены в скрипты:

  1. Прошить образ — install.sh собирает его в контейнере и прошивает, с уже встроенными исправлениями для sector-size, GPT, ядра и WiFi.

  2. tailscale up и авторизоваться.

  3. Запустить setup-host.sh — он ставит Docker, исправляет юнит containerd, настраивает DNS для контейнеров и правило файрвола, поднимает Portainer и публикует Portainer и бота через Tailscale.

  4. Создать админ-аккаунт Portainer.

  5. Развернуть стек бота и заполнить его секреты.

Скриптами не охвачены только логин в Tailscale, пароль администратора и секреты сервиса — это намеренно оставлено на ручную настройку.

Итог

Старый телефон — вполне пригодный маленький сервер: ARM SoC, пара ватт потребления, собственная батарея. Главное препятствие между ним и этой ролью — программное обеспечение, которое всё ещё считает его телефоном. Вся работа и состояла в том, чтобы снять это допущение. Для одного устройства это немало, но проделать это нужно лишь однажды — и в результате рабочий компьютер снова приносит пользу, а не лежит без дела в ящике.

Если любопытно, что именно на нём работает: это Telegram-бот, который я написал, — он вручную реализует стратегию усреднения цены покупки(DCA) нескольких активов; проект я пока ещё дорабатываю, так что подробности оставлю на другой раз. Код — github.com/arttttt/CMIDCABot. Но самым интересным здесь был сам телефон.

Комментарии (28)


  1. Ryav
    01.07.2026 01:42

    Спасибо за статью!

    Я почему-то думал, что если устройства в списке поддерживаемых нет, то поднять систему будет нереально. У самого OnePlus 5T лежит и только форвадит СМС, тоже хочу сделать что-нибудь из него на помощь своему домашнему серверу.

    Вопрос по питанию — разве околонулевая зарядка лучше, чем хождение в диапазоне 30-60%, например?

    И второй момент — можно развернуть ещё Caddy, тогда можно будет свои локальные домены использовать без пути от Tailscale.


    1. Gutt
      01.07.2026 01:42

      Вопрос по питанию — разве околонулевая зарядка лучше, чем хождение в диапазоне 30-60%, например?

      Лучше всего постоянное нахождение в районе 40 %. Любое циклирование вызывает старение.


    1. arttttt Автор
      01.07.2026 01:42

      Вопрос по питанию — разве околонулевая зарядка лучше, чем хождение в диапазоне 30-60%, например?

      Уже ответили выше, но да, в целом лучше держать заряд на одном уровне. Литий так меньше интеркалируется в анод, таким образом удается держать объем анода на одном уровне. Меньше дельта изменения объема = меньше износ

      И второй момент — можно развернуть ещё Caddy, тогда можно будет свои локальные домены использовать без пути от Tailscale.

      Если честно, то не изучал вопрос тк в моем случае я не хотел выставлять бота в публичный доступ извне моей mesh-сети. Как я понимаю, то можно запустить и настроить все, что поддерживает pmOS тк это обычный линукс

      У самого OnePlus 5T лежит и только форвадит СМС, тоже хочу сделать что-нибудь из него на помощь своему домашнему серверу

      Есть несколько моментов, на которые стоит обратить внимание:

      • Если хочется именно чистый Linux, как в моем случае, то сначала нужно проверить, можно ли разблокировать загрузчик на телефоне. Насколько я помню, Op5t разблокируется не сложнее, чем Op3t

      • Насколько хорошо SoC и платформа в целом поддержаны в mainline Linux или наличие форков под конкретные устройства. Можно и самому доработать, но затратно по ресурсам

      Еще есть вариант попробовать сделать в Termux, но того уровня гибкости, который дает полноценная система не получить


  1. AleksUb
    01.07.2026 01:42

    Есть мысль: заменить акум на ионистор. В таком случае вроде как "батарея есть" и контроллер не будет с ума сходить, в то же время в случае скачков напряжения он поможет.

    У меня старенький смарт обнулён для Enstein@Home (всё равно не нужен) и питается от "солнца". Если акум со временем станет проблемой, думаю как раз в этом направлении. Уличная подсветка, уже перепаянная на это дело в прошлом году, уже показала, что оно того стоит.


    1. Tuesok
      01.07.2026 01:42

      У емких ионисторов, чтобы 100 фарад и больше - рабочее напряжение маленькое, придется городить из них батарею (2 штуки минимум) + BMS для их балансировки.

      И емкости хватит только на защиту от скачков - там десятки мАч получаются при пересчете, из-за того, что можно использовать только часть напряжения - от 4,2 до 2.9, дальше устройство отключится.


      1. AleksUb
        01.07.2026 01:42

        Это да, но в данном случае никто на полноценную замену не претендует. Просто пусть питается от внешней сети, а этот вариант только для обмана контроллера. И чтобы не возникла ситуация, когда через полгода-год аппарат вдруг начинает полнеть и ломать всё внутри, как у меня когда-то с читалкой было из-за вспухшего аккума. :)


    1. arttttt Автор
      01.07.2026 01:42

      Есть мысль: заменить акум на ионистор. В таком случае вроде как "батарея есть" и контроллер не будет с ума сходить, в то же время в случае скачков напряжения он поможет.

      Можно собрать любую подходящую по характеристикам батарею, лично мне не хочется вандалить телефон и заниматься сборкой подобного

      Я бы с удовольствием почитал про такую доработку, если решите сделать и рассказать об этом


    1. SunRiseX64
      01.07.2026 01:42

      Можно просто не париться и просто удалить батарею. Hо не “в лоб”, так как тогда девайс скорее всего не стартанёт, а:

      • отрезать батарею (сами ячейки от контроллера)

      • замерить на них напряжение

      • купить на али/озоне понижающий преобразователь DC-DC с регулировкой напряжения (с пяти вольт на пониже)

      • на вход преобразователя подать стандартное usb-питание (отдельным проводом), а выход подключить к контроллеру

      Из плюсов

      • максимально пожаробезопасно, так как в схеме отсутствует литий в принципе

      • освобождается usb-разъём, так как питание теперь будет по отдельному проводу идти, в него можно проводной ethernet-адаптер воткнуть вместо wifi

      Минусы тоже есть

      • придётся паять

      • придётся сверлить корпус, чтобы вывести провод питания

      Проделывал такой трюк на Google Nexus 7, уже полгода полёт нормальный


  1. vpman
    01.07.2026 01:42

    Тоже решил заморочиться с POCO X3 NFC (6/64Gb , 8 ядер) в качестве сервера. В поддерживаемых PMOS устройствах он есть. Можно поставить только консольку, без UI.
    Ставится с pmbootstrap без особых проблем. Но есть нюансы :
    - спец версии под устройство нет, используется generic qcom-sm7150 сборка.
    - без включенного bluetooth ребутится после загрузки ОС, поэтому оставил, отключив все функции для экономии.
    - ограниченное управление и мониторинг питания. Не удалось настроить отключение по достижению процента зарядки. Поэтому решил использовать розетку с таймером, чтобы регулярно отключать питание и экономить ресурс батареи.
    - не видит доп. SD карту через SIM разъем. Хотя, например, TWRP с ней может работать.
    - огорчило то, что не работает dual режим с зарядкой по OTG . Пока что либо зарядка, либо работа с устройствами в USB HUB (хочется подключить ethernet, и всякие USB девайсы, ). Поэтому сейчас коммуникация по WiFi + зарядка.

    Докеры и прочие серверные штуки работают как положено. Энергопотребление - 1-2Вт/ч. только на батарее может дней 5-7 жить, если не сильно нагружать проц и WiFi.
    В целом, можно использовать в качестве сервера для каких то не сильно нагруженных вещей. У меня там Hermes агент, например.


    1. arttttt Автор
      01.07.2026 01:42

      Можно поставить только консольку, без UI

      Я это не упомянул в статье, но у меня именно вариант без UI

      Не удалось настроить отключение по достижению процента зарядки

      Возможно, что мой сервис поддержания зарядка подойдет

      не видит доп. SD карту через SIM разъем. Хотя, например, TWRP с ней может работать

      TWRP работает на ядре под Android систему, поэтому там есть все нужные патчи под железо телефона. Можно посмотреть, как обвязка sd карты сделана в android ядре и пропатчить ядро в pmos, но задачка не такая простая

      огорчило то, что не работает dual режим с зарядкой по OTG . Пока что либо зарядка, либо работа с устройствами в USB HUB

      Тоже проблема поддержки mainline ядро, можно попробовать поизучать android ядро с агентами и сделать патчи для себя

      В целом, можно использовать в качестве сервера для каких то не сильно нагруженных вещей. У меня там Hermes агент, например

      Тоже есть идея хостить OpenClaw/Hermes, заодно посмотреть на то, как железо телефона справится с двумя сервисами


  1. HOMPAIN
    01.07.2026 01:42

    Почему вы не стали использовать штатный Android в качестве системы для сервера? Кроме проблем с зарядкой батареи были другие серьёзные ограничения?


    1. arttttt Автор
      01.07.2026 01:42

      У Android есть много ограничений

      • всю подобную инфраструктуру нужно разворачивать через Termux те минус гибкость

      • Без глубоких доработок системы/пересборки, нельзя убрать гугл сервисы и прочие потребляющие ресурсы вещи

      • Ограничения/оптимизации андроид по питанию нужно обходить отдельно

      • pmOS без ui после запуска потребляет ~200-300MB RAM и 0% CPU, с андроид даже сравнивать не стоит


      1. SunRiseX64
        01.07.2026 01:42

        всю подобную инфраструктуру нужно разворачивать через Termux те минус гибкость

        И про докер можно в принципе на termux забыть


        1. umbral
          01.07.2026 01:42

          Ядро собрать с поддержкой изоляции namespases и всё всегда работало без проблем.

          А сейчас вообще есть просто magisk модуль с докером, установил и работает.


          1. arttttt Автор
            01.07.2026 01:42

            Ядро собрать с поддержкой изоляции namespases и всё всегда работало без проблем.

            Лично в моем понимании, если идти в сторону сборки ядра или подобных изменений, то проще уже поставить pmOS как минимум для экономии ресурсов устройства(pmos потребляет гораздо меньше) и большей гибкости


            1. umbral
              01.07.2026 01:42

              Pmos выигрывает только в одном – headless сервер. Андроид так просто не умеет. Для некоторых задач подходит только обычный Линукс.

              А во всем остальном андроид работает лучше и проще, нужен только root.


              1. arttttt Автор
                01.07.2026 01:42

                pmOS с огромным отрывом выигрывает по расходу ресурсов, что не менее важно


      1. HOMPAIN
        01.07.2026 01:42

        А вариант сборки телеграмм бота как обычное приложения Андройд не рассматривали? Termux мне тоже не зашёл, но с pmOS тоже очень много нюансов


        1. arttttt Автор
          01.07.2026 01:42

          А вариант сборки телеграмм бота как обычное приложения Андройд не рассматривали?

          Не представляю, как полноценный сервис, который нужно хостить, упаковать в android приложение, чтобы оно нормально работало

          Это даже не учитывая всех тех проблем, что андроид потребляет больше ресурсов, нужно настраивать вейклоки и прочие вещи по отключению энергосбережения


        1. umbral
          01.07.2026 01:42

          А что не так с termux?


          1. HOMPAIN
            01.07.2026 01:42

            Сколько раз не пробовал на него что-то ставить, всё время какие-то нюансы. То паки не так расположены и программа ругается при запуске, то из под рута нельзя запустить процесс, а там рут по умолчанию.

            У меня не было цели прям что-то запустить, я экспериментировал в образовательных целях и ничего с ходу не заработало из того что мне было интересно.


            1. umbral
              01.07.2026 01:42

              Можно и с винды пересесть на линь, и ругать его, что пути другие и команды отличаются :)

              Просто termux и терминал на компе – разные вещи, termux – linux-подобная среда пакетов, которая крутится на рантайме андроида и не изолируется от него. Этим различия и обусловлены.

              И рут там не по-умолчанию, из коробки даже не установлены пакеты, работающие с su


    1. vpman
      01.07.2026 01:42

      Я сначала тоже попроще выриант с Termux пробовал, но без рута ничего там продвинутого запустить не получилось. Докер, фаервол и прочее не работали, упирались в защиту Андроида. С рутом уже не стал экспериментировать. Затем Droidian поставил и тоже упёрся в какие то лимиты. Далее - PMOS. Он уже заработал как натуральный Линукс сервер, правда не вся аппаратура. Но пока мне достаточно.


      1. HOMPAIN
        01.07.2026 01:42

        У меня аналогично с Termux ничего не завелось нормально…


  1. arttttt Автор
    01.07.2026 01:42

    Удалено


  1. Limping
    01.07.2026 01:42

    Интересно, ставил ли кто-нибудь postmarketOS на старичка Xiaomi Redmi Note 3 Pro (Kenzo).

    Загрузчик у меня уже разблокирован.


    1. arttttt Автор
      01.07.2026 01:42

      Поддерку можно проверить в официальной wiki pmOS: https://wiki.postmarketos.org/wiki/Xiaomi_Redmi_Note_3_(xiaomi-kenzo)

      У Kenzo все выглядит довольно неплохо


  1. dbLank
    01.07.2026 01:42

    Зато был старый OnePlus 3T в ящике: флагман 2016 года, Snapdragon 821, 6 ГБ оперативной памяти — рабочий, но не включавшийся годами.

    Спасибо за статью. Есть парочку старых телефонов. Но я ставил на них серверные сборки mysql php прям на Android