<!--
.. title: Part 25: The phone in my pocket runs Linux
.. slug: the-phone-in-my-pocket-runs-linux
.. date: 2026-10-23 19:49:18 UTC
-->

## TL;DR

- My phone runs [SailfishOS](https://sailfishos.org) - Linux, neither Apple nor Android - and the platform gap is real: mainstream software assumes two ecosystems, and mine is neither.
- [Tailscale](https://tailscale.com) ships no SailfishOS app, only binaries. So I packaged them into an RPM and built the app around them: driving tailscaled, reporting status, aiming for feature parity with the Android app plus the extras I actually wanted.
- The stock alarm app was the second offender - no snooze interval settings, and disabling an alarm is easier than snoozing it - so that became my second app.
- Both are built on the [Forgejo](https://forgejo.org) runners from last post, with GLM doing the heavy lifting, and the loop now works end to end: I ask Hermes to plan a feature, a PR eventually appears, I test the built RPM.

## The phone choice, and what it costs

Everyone's phone is one of two operating systems. Mine isn't. It runs SailfishOS, which means it runs Linux - actual GNU userland, a real terminal, packages - on the device I carry everywhere. I chose it for the same reasons I chose everything else in this series: I want the thing to be mine, to be inspectable, and to not phone home to a company whose business model depends on my attention. The privacy argument that runs through this whole estate does not stop at the pocket.

But I won't pretend the choice is free. The cost of leaving the duopoly is that every app you take for granted has to either exist for your platform or get built. Mainstream vendors build for iOS and Android and stop there. Your platform is a rounding error on their user metrics, and the binaries - when they bother to ship them at all - arrive with no UI, no integration, and no app.

SailfishOS does have one card to play here: Android app support. An Android compatibility layer - with Aurora Store and Obtainium sitting on top of it - gives surprisingly good coverage for everyday apps, and that closes a lot of the gap I'd otherwise feel daily. But not all apps work, and the ones that misbehave tend to be the ones I care most about. Which is why I've drawn a line: any future app that's purely an API client, I'll take on and code myself rather than fight the compatibility layer to make someone else's wrapper behave.

This post is about the two things I refused to live without, and what building them myself actually looked like.

## Tailscale, the app that didn't exist

Tailscale is non-negotiable in this setup. The whole estate's remote access story rests on it - the mesh, the doors, the admin surfaces that are "nobody unreachable" - and a phone without the mesh is a phone cut off from all of it. SailfishOS gets no native app. What it does get is binaries: the client and the daemon, compiled for the right architecture, downloadable but uninstalled-shaped. No UI. Nothing that drives tailscaled. Nothing that tells you whether you're actually connected.

So the gap between "binaries exist" and "usable app" was mine to fill. I packaged the binaries into an RPM, and built an app around them that starts and drives the daemon, and reports on status - connected or not, what the mesh looks like from the pocket. The target is parity with the Android app where it matters, plus the extras the official apps don't give me and that I actually want. It's not finished, but it's real: the phone joins the mesh through software I built, packaged the way my platform expects, published on my Forgejo.

## The alarm, or: the bar was on the floor

The second app came from a more mundane place, which is fitting, because mundane software failures are the ones that stick. The stock alarm app had two flaws that made it unusable for me. First, no settings for snooze intervals - the snooze behavior was fixed, take it or leave it, and my mornings are not the developer's mornings. Second, and worse: dismissing an alarm outright was easier than snoozing it. A half-asleep swipe in the wrong place and the alarm is gone for the day, with no way to tell the difference until the consequences arrive.

That combination - inflexible where I needed flexibility, careless where I needed care - meant the existing app was not going to improve. So I wrote my own. It does what the stock one wouldn't: configurable snooze intervals, and a deliberate friction gradient where stopping an alarm takes more intent than postponing it. It's a small app. It solves a small problem. But it's the clearest demonstration of the difference between using software and being able to change it: when the alarm misbehaves at 6am, I don't file a bug and hope. I own the repo it lives in.

## The loop, which last post set up

Here's where the story connects back to the runners. Both of these apps are built on the Forgejo runners from last post - the [KEDA](https://keda.sh)-scaled pool that spins up when the queue has work. The push-to-build pipeline that compiles my cluster's tooling also compiles my phone's apps, on hardware I own, from repos I own. The recursion from the GitOps post just keeps deepening: the cluster now builds software for a device that isn't even part of the cluster.

And the development loop itself is where GLM comes in - first 5.2, now 5.3 - doing the heavy lifting on features, with me steering and reviewing. The workflow that's settled in is the one I find quietly remarkable: I ask Hermes to plan a new feature, and later it tells me a PR is ready. I test the built RPM, decide, merge or iterate. The phone in my pocket gets feature development from an agent on my own infrastructure, building from my own repos, through runners that existed because I got tired of maintaining build servers. None of that was the plan when I started this series. It's just what the estate turned out to be able to do once each layer was in place.

## What the pocket proves

The thesis of this blog is that owning your stack is worth the time it costs, and the pocket is where that thesis gets its hardest test. A server is easy to make sovereign - it sits in your rack, runs what you tell it, and nobody expects it to run other people's apps. A phone is hostile territory: an entire industry built around two platforms, an app economy that assumes them, and a daily-use device where "it doesn't work" is not an acceptable outcome.

What I've learned living on the minority platform is that the gap is real but shallower than it looks. Tailscale's binaries were there; they needed packaging and a face. The alarm app needed writing, but writing a small app in 2026 is a conversation away from a first draft. The duopoly's real lock-in was never technical difficulty - it was the assumption that if your platform isn't served, you lose. The assumption is wrong for anyone with a cluster, runners, and the patience this series keeps talking about. The phone is the newest tenant of the estate, it doesn't even live in the house, and it's already on the payroll of the house that builds.

I should be honest about the last holdout, though: I still carry a [GrapheneOS](https://grapheneos.org) phone alongside the SailfishOS one. The rule is simple - I do as much as I can on the Linux phone first, and reach for the Graphene one only when I can't do otherwise. What's striking is the direction of that list: it keeps shrinking, and not because I'm porting faster than the industry. It's because more and more app developers have decided to be the authority on which devices are allowed to run their software, and the Graphene phone - which lives by the same defiance as the Linux one - keeps failing their checks alongside mine. Add Google's plans to disable sideloading, and the "escape hatch" phone is inheriting the same cage as everything else. That's precisely why the Linux phone earns its place: it gives me work, but the work buys back freedom that the mainstream platforms are actively selling off. The cage is closing from both sides. Mine is the one with a terminal.