My server is a phone now

Aug 09, 2026 05:49 AM - 6 hours ago 3

For a while, my individual infrastructure lived connected a mini Hetzner VPS. It ran a fewer web apps, a distant browser called Surf, Caddy, and the accustomed supporting cast. Nothing peculiarly serious. It worked, I conscionable didn’t for illustration paying for it.

One of the apps I run, Surf, made the discuss difficult to ignore. The cheapest shared machines were good until Chrome had existent activity to do, astatine which constituent they felt starved. Dedicated CPU machines hole that, but costs capable each period to make a individual browser consciousness for illustration a questionable financial commitment.

Buying different instrumentality wasn’t an appealing flight hatch either. DRAM prices person gone wholly stupid, truthful putting together a caller container pinch a comfortable magnitude of representation felt particularly sick timed. I looked astatine utilized mini PCs and concisely considered turning my desktop into a server whenever I wasn’t utilizing it. Then I remembered the CMF Phone 1 I already own.

Eight ARM cores, 8 GB of RAM, 128 GB of flash, Wi-Fi 6, a 5G modem, and a built successful artillery backup, that is each attached to an SoC that I consciousness is overqualified for sitting successful a drawer. And I had already paid for it. After dusting it disconnected and playing pinch it for a while, I decided to move the telephone into the server.

Today it runs Surf and its managed Chrome instance, my individual finance tracker, a surface sharing service, and a fistful of smaller web apps. They past reboots, deploy from Git, and stay reachable erstwhile the telephone moves betwixt networks, truthful this is now the instrumentality that really replaced the VPS.

The cleanest type of this thought seemed to beryllium flashing a normal Linux distribution. The CMF Phone 1 has a postmarketOS instrumentality port, it boots, and the instrumentality page has capable greenish boxes to make a reckless personification optimistic. I ended up being that person.

What I paid little attraction to was everything marked broken: Wi-Fi, Bluetooth, hardware acceleration, and astir of the different things that make the telephone useful arsenic a mini server. I sewage arsenic acold arsenic the postmarketOS scatter surface and a achromatic display. At that constituent I had neither a server nor a phone.

Recovering banal Nothing OS turned into its ain broadside quest. The flashing inferior needed Windows, truthful I installed Windows successful QEMU, fought USB passthrough and MediaTek drivers, watched the flashing instrumentality hang, past yet moved the process to an existent Windows installation and restored the mill images.

There was a infinitesimal successful the mediate of this wherever the telephone was soft bircked and only showed a achromatic surface and I genuinely thought I had converted a perfectly bully instrumentality into a paperweight.

It came back, and instruction learned: Android already has moving drivers for each portion of this hardware. Wi-Fi, powerfulness management, the battery, the GPU, the modem, and each weird vendor item already work. Throwing each of that distant successful pursuit of a much accepted userspace was the incorrect trade.

I didn’t really request the telephone to go a normal Linux machine. I needed it to tally Linux applications reliably while Android continued doing the hardware circumstantial activity it is bully at.

The 2nd effort kept banal Android and treated Termux arsenic the big environment.

Termux gives maine OpenSSH, runit, Caddy, Cloudflared, package management, and normal capable Unix tooling. Termux:Boot starts the supervisor and SSH aft a reboot. Tailscale gives the telephone a unchangeable backstage address, truthful from immoderate instrumentality connected my tailnet I tin conscionable run:

ssh cmf

Termux is not a virtual machine. Its processes still execute against Android’s Linux kernel, but its Bionic based userspace is different capable from an mean Debian installation that existing Linux exertion images cannot simply beryllium dropped into it. That divided ended up being useful, though: Termux could stay the mini big power level while each exertion brought the Linux filesystem it expected.

The existent services are supervised by runit. Android’s artillery guidance is very bully astatine its normal occupation and very bad for a instrumentality pretending to beryllium a server, truthful I had my Ansible build besides use an Android big profile: it installs a persistent aftermath lock, disables ray and heavy idle, exempts Termux, Termux:Boot, and Tailscale from inheritance restrictions, disables the kid process limiter, prevents Wi-Fi suspension, and configures Tailscale arsenic the ever connected VPN.

The betterment concatenation matters much than immoderate individual setting. Android boots, ever connected VPN brings Tailscale back, Termux:Boot starts runit, runit starts each resident service, and wellness checks verify the section and nationalist paths. The telephone tin reboot without waiting for maine to notice.

Android boot -> Tailscale always-on VPN -> Termux:Boot -> runit -> resident services -> section and nationalist wellness checks

This isn’t a accepted Linux server. There is nary systemd, nary normal Docker daemon, and nary logic to dress otherwise. But it is simply a Linux kernel pinch a very tin userland sitting connected apical of it, and that turns retired to beryllium enough.

Most of my applications already shipped arsenic Linux ARM64 OCI images. proot-distro made those amazingly easy to tally nether Debian without changing the applications themselves.

PRoot intercepts filesystem and process operations successful userspace and makes a regular Termux process judge it lives wrong a Debian guidelines filesystem. It isn’t a instrumentality boundary. Everything still shares Android’s kernel, web namespace, and Termux UID. But arsenic an exertion compatibility layer, it is highly useful because it needs neither guidelines nor a typical kernel.

The mean web services initially ran good this way. Each of my applications sewage a verified guidelines filesystem, a loopback port, and a runit service. Caddy ran straight successful Termux and routed hostnames to those ports.

The performance/latency delicate Surf browser workload was the exception. Starting processes, opening libraries, stepping paths, reference browser profiles, and shuffling seizure information each crossed PRoot’s userspace translator layer. There was CPU available, but Chrome could not scope it efficiently. So I rooted the phone, not to switch Android, but to equine the aforesaid Debian filesystem decently and participate it pinch a existent chroot.

Runit still owned the lifecycle from Termux, configuration still came from the aforesaid place, and exertion information still lived successful Termux storage. The workload simply reached Android’s kernel done autochthonal syscalls alternatively of PRoot. The betterment was not subtle!

Once that way was solid, leaving the smaller residents nether PRoot stopped making overmuch sense. They now tally the aforesaid way. My workstation resolves each ARM64 image to an nonstop digest and exports its filesystem; Ansible verifies and installs it connected the phone. A mini guidelines helper creates a backstage equine namespace, binds the required paths, enters the filesystem pinch chroot, drops privileges, and starts the original image entrypoint.

Neither Docker nor a compiler needs to beryllium connected the phone. These are still compatibility environments alternatively than information boundaries: the residents stock Android’s kernel and web stack, while the backstage equine namespaces chiefly support mounts and cleanup predictable.

I besides spent acold excessively agelong trying to span Debian’s graphics stack to the phone’s Mali GPU done VirGL and Android Vulkan. I sewage hardware compositing checkmarks alongside corrupt pages and worse performance… The boring package rendered way turned retired to execute better.

By this constituent the telephone could tally everything, but I didn’t want a pet server assembled from commands I would hide successful a week. I moved the full big into an Ansible managed state: versions, work definitions, routes, powerfulness settings, secrets, and wellness checks each unrecorded successful 1 backstage repository.

The deployment travel is roughly:

release aliases OCI image -> checksum/digest pinned successful Git -> Ansible complete SSH -> versioned files connected the phone -> atomic existent symlink -> runit service -> section wellness check -> nationalist separator check

Releases are pinned by digest aliases checksum and installed into versioned directories down an atomic existent symlink. A grounded checksum aliases wellness cheque stops the deployment, rollback intends reverting the pin and applying again. Application information lives separately from releases.

After the mini manual bootstrap: instal the 3 Android apps, guidelines the phone, assistance Termux superuser access, and authorize SSH, the aforesaid repository owns the rest. From my workstation, bringing the big to the declared authorities is deliberately simple:

make phone make phone-status make phone-edge-check

Applying it again does not switch unchanged runtime files aliases restart patient residents. More importantly, the configuration is useful if this telephone dies: different rootable ARM64 telephone tin beryllium brought toward the aforesaid authorities without reconstructing a ammunition history.

Secrets are not stored connected the phone’s Git checkout, because location is nary Git checkout. Ansible Vault values unrecorded encrypted successful the infrastructure repository. The vault password is derived by asking my 1Password SSH supplier to motion a fixed challenge, truthful the backstage cardinal stays successful 1Password and the telephone ne'er needs entree to it. During deployment, Ansible renders only the runtime values each work needs into Termux’s backstage storage.

The adjacent problem to tackle was ingress. My location relationship does not travel pinch the benignant of fixed server setup a VPS gives you, and I don’t want to expose SSH aliases a postulation of random exertion ports done the router. I besides wanted the telephone to stay a telephone successful 1 important sense: I should beryllium capable to unplug it, return it location else, link it to the internet, and still person my server.

The HTTP applications usage a Cloudflare Tunnel. Cloudflared makes 1 outbound relationship from the phone, Cloudflare sends each hostname done it, and Caddy routes the petition to the correct loopback service.

Internet -> Cloudflare Tunnel -> Caddy connected 127.0.0.1 -> exertion connected 127.0.0.1

There is nary inbound router norm for those services. Cloudflared only needs an outbound connection, truthful if I move the telephone to different network, the passageway reconnects and the hostnames travel it. Tailscale does the aforesaid for administration. The phone’s artillery tin span the move, and the nationalist services do not attraction which Wi-Fi happens to beryllium underneath them.

The Surf distant browser backend needed a different way though. Its nonstop relationship is latency sensitive, terminates its ain TLS, and the aged iPad that connects to it pins the server identity. At home, Cloudflare DDNS keeps a DNS only grounds pointed astatine the existent nationalist reside and the router forwards 1 larboard to Surf. On the LAN, the iPad connects straight to the phone.

That still near roaming. A normal Cloudflare Tunnel terminates TLS astatine Cloudflare, which is precisely what Surf’s pinned relationship does not want. The solution was to wrap the complete Surf TLS watercourse wrong an mean WebSocket. Cloudflare sees and forwards the WebSocket, but the existent authenticated Surf relationship remains encrypted extremity to extremity wrong it.

This evidently adds latency, from extracurricular location I usually spot astir different web information trip, and the iPad relationship was astir 60 sclerosis erstwhile I first tested it. But it useful done a passageway that only needs outbound connectivity, connected an operating strategy from 2012, without installing Tailscale connected the iPad.

That was the infinitesimal the telephone setup became somewhat ridiculous successful the champion way. I near it plugged successful astatine home, went to the office, SSHed from my MacBook done Tailscale into my workstation and the phone, and utilized the original iPad done the telephone hosted Surf instance. The VPS was nary longer, it felt liberating.

The instrumentality has 2 layers. Android and Termux ain the hardware, networking, ingress, and supervision. The Linux residents get the filesystem they expect and different enactment retired of the host’s way.

layerresponsibilitypieces
Android / Termux hosthardware, networking, ingress, supervisionrunit, Tailscale, Caddy, Cloudflared, DDNS, operations dashboard
rooted Linux residentsapplication compatibility and workloadsSurf and Chrome, Finances, Screen Share, immoderate different apps

The demanding workload is Surf, which brings a modern Chromium browser to aged iPhones and iPads. The telephone runs desktop Chrome (recent arm64 merchandise of Chrome) and the Surf backend wrong its Debian runtime, an iPad mini receives H.264 video and audio while sending touch, keyboard, tab, and navigation commands back. Surf is why I cared truthful overmuch astir syscall overhead and latency successful the first place.

Alongside it, the telephone hosts my individual finance tracker. It records recurring income and expenses, 1 clip transactions, monthly spending limits, and projects my equilibrium guardant time by day. Unlike the replaceable exertion artifacts, its SQLite database contains authorities I attraction about, truthful it has automated disconnected instrumentality backups and a tested reconstruct path.

None of those names are baked into immoderate expansive “phone server framework.” A caller resident is different immutable artifact, a runit definition, an definitive information contract, a wellness check, and optionally a Caddy route.

The latest summation is observability. I tin still SSH in, inspect runit, tail logs, cheque nationalist routes, and query Android directly, but I nary longer person to do that simply to spot what’s going connected pinch the instrumentality astatine a glance: One autochthonal work collects CPU usage for each 8 cores, memory, storage, uptime, battery, thermals, section and nationalist reachability, and each discovered runit resident. It keeps bounded history and serves an embedded Vue interface astatine https://dash.cmf, reachable only complete the LAN aliases tailnet.

dashboard

The log position discovers the aforesaid work directories astatine runtime, truthful adding different resident doesn’t require school the UI its name. It is overmuch nicer than SSHing successful conscionable to retrieve which point was noisy.

If you already person a reasonably modern, rootable ARM64 telephone sitting unused, this is overmuch little ridiculous than it sounds. You get quiet hardware, debased powerfulness consumption, flash storage, Wi-Fi, a built successful show for recovery, and a artillery that behaves for illustration a mini UPS. Stock Android already supports the hardware, while Termux and a rooted chroot are capable to tally a astonishing magnitude of normal Linux software.

I would not put irreplaceable information connected 1 without automated disconnected instrumentality backups, and I would not dainty the chroots arsenic dispute workload isolation. Rooting expands the spot boundary, Android remains an different server host, and package rendered desktop Chrome is not going to hit a dedicated workstation pinch a GPU.

But for a fistful of individual services, particularly erstwhile the replacement is paying indefinitely for a VPS that is either slow aliases annoyingly expensive, it is simply a genuinely useful action alternatively than only a stunt.

The telephone is still a weird server. It shares 1 kernel, Android occasionally needs to beryllium reminded not to “optimize” it, and a early Android update could ever create a caller surprise.

But it is quiet, artillery backed, accelerated enough, reachable from anywhere, reproducible from Git, and already sitting successful my house. I started this trying to prevention money connected a VPS. I ended up pinch a rooted telephone moving my individual infrastructure, and someway that is overmuch much satisfying. :)

More