Сервер на смартфоне полгода спустя: OnePlus 6, Mobian, 26 контейнеров и одна проблема

В апреле 2026 года я решил ради эксперимента установить на старый смартфон OnePlus 6 Linux и использовать его в качестве сервера. Через некоторое время пришлось немного повозиться, чтобы правильно настроить режим зарядки, чтобы аккумулятор жил в наиболее безопасном и комфортном режиме.
Полгода спустя хочу рассказать, как это всё работает, были ли какие-то весомые проблемы, что у меня там крутится и к каким выводам я пришёл.

Для начала отвечу на самый главный, как мне кажется, вопрос. Доволен ли я, что решил это попробовать? Однозначно да!
У меня теперь в доступе полноценный сервер, на который я в любой момент могу заливать любые свои поделки и удобно ими управлять. За это время доступность сервера в основном зависела от моего интернета. При желании я мог бы установить в него сим-карту, чтобы доступ был ещё надёжнее. Но мне такой уровень безотказности был пока не нужен.
Что крутится на сервере
Что я на нём держу? Сразу скажу, стараюсь никаких продакшен-кодов на нём не держать. В основном это какие-то мои (и не только мои) эксперименты или инструменты для моей собственной работы, а не что-то, от чего зависит ещё чей-то успех.
Однако были и успешные продакшен-кейсы. Летом я размещал там тестовую среду для одного рабочего проекта, потому как это было быстрее и проще, чем настраивать какой-то сервер через вечно занятого DevOps’а, которому и без меня хватает проблем. А также проводил небольшой пешеходный квест (до двух десятков одновременных подключений). Для всех этих нужд было более чем достаточно текущих мощностей, и система не испытывала каких-либо проблем с нагрузкой.
Монитор
Для контроля состояния мобильного сервера извне я навайбкодил простейший монитор (репозиторий для интересующихся), который позволяет контролировать основные состояния устройства: состояние батареи, нагрузку на CPU, объём занятой оперативной памяти и состояние докер-контейнеров.
Была ещё вкладка, относящаяся к процессам на устройстве, но со временем я от неё отказался, так как она во многом дублировала информацию других вкладок и при этом была неудобна в навигации.

Контейнеры
Сейчас на сервере активно 26 докер-контейнеров.
Не то чтобы всеми из них я пользуюсь, но, учитывая, что запасов ресурсов устройства (8 ГБ оперативной памяти, 8 ядер и 128 ГБ накопителя) у меня более чем достаточно, я могу себе позволить не проводить частую ревизию и не гасить всё ненужное сразу, когда оно перестаёт быть нужным.
Где-то десяток из этих контейнеров заняты такими же «экспериментами для личной продуктивности» моей хорошей знакомой, с которой я поделился ресурсами сервера.
Остальные контейнеры – это, собственно, монитор ресурсов, Caddy, пара сайтов с API и базами данных к ним (один из них – упомянутый выше пешеходный квест, другой – мой «стартап», который я так и не довёл до какого-то продуктового состояния, просто потому что сменил работу и не хватает времени и мотивации этим заниматься), система для настолок, пара личных телеграм-ботов и один из рабочих прототипов.
В пике на устройстве крутилось до 40 контейнеров. Мощностей устройства хватало, но, во-первых, я начал упираться в оперативную память, а во-вторых, в зарядку.
Проблемы
Не могу сказать, что в целом были какие-то ещё проблемы, но нежелание отказываться от аккумулятора всё же действительно принесло больше всех проблем. И не в том плане, что он там грелся или что-то в таком духе. Вовсе нет.
За всё время использования не было ни одного аварийного срабатывания на тему перегрева аккумулятора выше 45 градусов и даже 40 градусов температура, насколько я помню, не достигала. Обычно батарея живёт в пределах 30-35 градусов, что на зарядке, что на разрядке.
Но когда я закинул на устройство слишком много всего, именно система управления батареей оказалась слабым местом. У меня было настроено, что для более лёгкого режима батареи ток зарядки ограничен 0,5 А. И в какой-то момент этого стало не хватать. Из-за чего начались «дрожания». Батарея думала, что она то заряжается, то разряжается, что, во-первых, начинало напрягать какие-то внутренние системы устройства, а во-вторых, привело к тому, что у телефона начал постоянно загораться экран, который только ухудшал ситуацию, так как тоже потреблял энергию и немного отвлекал.
Поправил я это довольно легко. Во-первых, перенастроил режим заряда так, чтобы он адаптировал мощность зарядки, чтобы не допускать таких «дрожаний» (но и не уводить аккумулятор в режим постоянной мощной зарядки). Подробнее можно изучить в репозитории. Во-вторых, всё же впервые за 4 месяца пришлось «убраться» - избавиться от лишних контейнеров.
Вот, в общем-то, и всё, больше никаких проблем не возникало. Что для полугода использования считаю довольно хорошим показателем.
SSH и деплой
Хочу немного остановиться на вопросе доставки обновлений на сервер и том, как я с ним взаимодействую.
Для начала проговорю, что до недавнего времени я вообще не имел дела с серверами на Linux, и пришлось всё осваивать на лету и во многом полагаться на ИИ-агентов, которые, конечно, очень сильно помогали с настройками всего и вся.
Настройка была выстроена так, что доступ по SSH есть только с конкретного IP из внутренней сети, в которой живёт устройство. Учитывая, что на самом устройстве есть полноценный терминал, проблема утраты доступа к устройству при проблеме с устройством не стоит. К тому же я всегда могу этот IP перенастроить на другое устройство. Не то чтобы это обеспечивает какую-то запредельную безопасность, но мне показалось, что для моих целей этого достаточно.
При этом, естественно, доступ по SSH возможен только по ключу, вход по паролю отключён.
Как устроен CI/CD
Соответственно, нужно было как-то обеспечить доставку всех бесконечных проектов, которые я пихаю в докер. CI/CD у большинства проектов настроен вполне обычно: GitHub Actions, GHCR и отдельный раннер на телефоне. Основная сборка и тесты проходят на стороне GitHub, а на самом устройстве происходит деплой.
Устроено всё довольно просто. Я пушу код в основную ветку, GitHub Actions запускает проверки и собирает Docker-образ под процессор телефона (ARM64). Для большинства проектов готовый образ отправляется в хранилище контейнерных образов GitHub (GHCR). В некоторых проектах образ помечается хешем коммита, поэтому при обновлении понятно, какую именно версию я ставлю. Конкретные шаги отличаются от проекта к проекту, но обычно телефон получает уже готовую сборку.
На самом телефоне работает self-hosted runner — небольшой агент, который получает задания от GitHub и выполняет их локально. Он сам подключается наружу, так что открывать SSH в интернет ради деплоя мне не пришлось. После успешной сборки раннер скачивает нужный образ, а Docker Compose обновляет контейнеры проекта. В типовом деплое ещё проверяется, что приложение запустилось и отвечает. Настройки с секретами и данные в томах остаются на сервере, поэтому обновление контейнера не означает переустановку всего с нуля.
Caddy и Nginx
За доступ к веб-сервисам из интернета у меня отвечает общий Caddy, а для одного из сайтов за ним работает Nginx. Есть публичный IP, чтобы ко всему этому можно было добраться извне.
Caddy работает отдельным контейнером и принимает запросы на портах 80 и 443. По домену он понимает, в какое приложение отправить запрос, а заодно сам получает и продлевает HTTPS-сертификаты. Приложения подключены к общей Docker-сети caddy_net, поэтому в конфиге можно указать имя контейнера и его внутренний порт.
Для каждого сайта есть небольшой отдельный конфиг, который подключается в общий Caddyfile. Меняю маршрут, проверяю конфигурацию и перезагружаю её в Caddy, а соседние приложения продолжают работать.
Nginx у меня нужен для конкретной логики одного сайта. Главную страницу он проксирует с другого сайта, а короткие ссылки перенаправляет по заданным правилам. Caddy принимает внешний запрос и обслуживает HTTPS, а затем передаёт запрос в Nginx по внутреннему порту 80. Сам Nginx наружу отдельным портом не выставлен. Так что общий вход и сертификаты живут в Caddy, а правила этого сайта — в Nginx. Для остальных приложений Caddy обычно отправляет запрос сразу в нужный контейнер.
Это обеспечивает простоту установки новых контейнеров (так как настройки CI/CD одного проекта можно скопировать в другой и минимально перенастроить, с чем нейронки отлично справляются) и удобство настройки сети.
В среднем, чтобы наладить ещё один проект на уже настроенном сервере, уходит 1 запрос к Codex на среднем размышлении и не более получаса времени. Инструкции по настройке без кредов хранятся в md-файлах. SSH-ключи, естественно, там не лежат, а доступны просто из определённой папки. Изредка надо обновлять авторизацию GHCR, но это меньшее из зол.
Зато за счёт этого я могу делиться своими мощностями с другими. Как и писал выше, не только мои проекты крутятся на этом сервере. Я настроил GitHub Actions в репозитории своей хорошей знакомой. Для проектов настроены отдельные секреты GitHub. Но сервер всё равно общий, поэтому полной изоляции тут нет: чужой проект может затронуть мои, если сожрёт ресурсы или в нём окажется откровенно вредоносный код, чего, я надеюсь, не будет.
Итоги
В общем, я доволен. Эксперимент кажется мне успешным. 3500 рублей за б/у телефон и пару выходных на настройку стоили того, чтобы получить такой функциональный и мощный сервер.
С доработками по батарее я снизил риски разрушения батареи. Да, сто процентов найдутся люди, которым это всё равно покажется небезопасным, но окей, никто вам не мешает выпаять батарею с обманкой или поменять, в конце концов, на новую (и менять, например, каждые три года). Моим критериям безопасности текущая конфигурация соответствует, не хочу никому ничего навязывать.
Проблем особо не было, а сервер в функционале для моих нужд мало отличается от полноценного компьютера на Linux, но меньше и тише. Ну и для моих задач дешевле аналогичного по мощности и объёмам памяти и диска VPS.
Ещё несколько скриншотов монитора
Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.
KioskNews shows a cleaned-up reading view extracted from the publisher’s page — the original always lives on their site, not ours.