{"slug": "i-updated-my-android-app-from-a-restaurant-my-laptop-was-off", "title": "I Updated My Android App From a Restaurant. My Laptop Was Off.", "summary": "A developer updated an Expo app from a restaurant using Codex on an Android phone while their laptop was off, thanks to a remote development setup where code and services run on an Ubuntu tower at home. The developer built the system to use existing hardware more efficiently, separating client tasks on a MacBook Air from backend processes on the tower connected via Tailscale and VS Code Remote SSH.", "body_md": "Recently, I was sitting in a restaurant with my MacBook Air powered off inside my backpack. I opened Codex on my Android phone and asked it to make a change to an Expo app I was working on. I use Codex as a coding agent connected to a development workspace running on another machine.\n\nThe repository wasn't on the phone. It wasn't on the MacBook either. It lived on an old gaming PC running Ubuntu at home, along with the coding agent, the backend, the database, Metro, and the Android build tools.\n\nWhen the change was ready, I switched from Codex to the app on the same phone and saw the result there.\n\nI had originally built this setup because I wanted to use the hardware I already owned more efficiently. The restaurant was when I realized I had changed something more important.\n\nIf my laptop was off, where was “local development” actually happening?\n\nThe tower was a modest personal PC I had used for gaming. It has 16 GB of RAM, a Ryzen 5 5600G, and roughly 1 TB of storage. It wasn't a workstation I bought for AI agents. It was hardware I already had at home.\n\nMy first attempt was to keep Windows and use WSL, Docker, and remote desktop from my 16 GB MacBook Air. Each part made sense on its own, but the combination felt awkward. Windows needed memory, WSL needed memory, Docker needed memory, and I was also paying the cost of rendering and streaming a remote desktop I didn't\n\nreally want.\n\nI could make it work, but I couldn't work comfortably. The machine spent too much of its limited capacity supporting the environment around development instead of the services I wanted to run.\n\nSo I installed Ubuntu on the tower. The goal was simple: let that machine focus on repositories, Docker containers, databases, development servers, builds, and coding agents. My MacBook could keep doing what it was already good at: running the browser, the editor interface, and everything that required my attention.\n\nI don’t have a clean before-and-after RAM benchmark from the Windows setup, so I won’t pretend this was a scientific comparison. However, I can say that the split felt significantly better in daily use.\n\nThe MacBook and the tower now have different jobs. The MacBook Air is my main client. I use VS Code, Codex, the browser, and the usual communication tools there. The Ubuntu tower owns the codebases and the processes around them: OpenSSH, Docker, PostgreSQL, backends, frontend servers, Metro, Android builds, and the agent tools that need access to all of it.\n\n[Tailscale](https://tailscale.com/docs/features/magicdns) connects the machines through a private network and gives them stable names. I use regular OpenSSH on top of that network. This is not Tailscale SSH, which is a separate feature.\n\nWith [VS Code Remote SSH](https://code.visualstudio.com/docs/remote/ssh), I can open a repository on Ubuntu from the MacBook and use it almost as if the files were local. The integrated terminal runs on Ubuntu.\n\nExtensions that need access to the code run there too. When I start a development server, port forwarding exposes it on the MacBook through `localhost`\n\n.\n\nThat last detail matters more than it sounds. I didn't want to remember private addresses every time I launched a web app or changed its configuration depending on where I was. From the browser on my MacBook, a service running on the tower can still feel like a normal local server.\n\nAt this stage, I thought I had built a cleaner remote development environment. Useful, but not especially surprising.\n\nThe bigger difference appeared when I started letting coding agents handle longer tasks. The repository, development services, and agent tools were all on Ubuntu, when I closed the MacBook, that environment didn't go to sleep with it.\n\nI could launch work, leave the laptop behind, and return later from another Codex client without rebuilding the environment on a temporary cloud VM or keeping my personal laptop awake.\n\nWhen I inspected the tower for this article on July 20, it had been running continuously since July 3. That isn't a reliability benchmark, but it matches how I now treat the machine. It is infrastructure rather than a device I open and close throughout the day.\n\nA more powerful MacBook would compile faster and run more services at once. It would still be a laptop. It sleeps, travels with me, runs my browser, joins video calls, and gets used for unrelated work. Moving the environment gave me a kind of persistence that a hardware upgrade alone wouldn't provide.\n\nNot long after I started experimenting with this setup, I watched Theo Browne's [Why I'm moving to Linux (for real)](https://www.youtube.com/watch?v=9tGrhrVKCrE). His setup is far more ambitious than mine, but one part felt familiar: moving coding-agent workloads away from the MacBook in front of you and onto Linux machines that can keep working remotely.\n\nMy version wasn't a fleet of servers. It was one reused gaming PC. But it was enough to change the lifecycle of my development work.\n\nWeb development already worked well from this environment. I could run a server on Ubuntu, forward the port, and open it from the MacBook. Android made the setup more interesting.\n\nI installed ADB and the Android build toolchain on Ubuntu. The tower could now compile an Expo development build, install the APK, and open it on my physical Android phone. The backend, PostgreSQL, and Metro still ran on Ubuntu.\n\nThe first networking problem was getting Ubuntu to reach a phone that wasn't on the same physical network. Both devices were connected to Tailscale, so each had a private address reachable inside that network. Android's wireless debugging opened a dynamic ADB port on the phone, and Ubuntu could connect to that port through the phone's Tailscale address.\n\nThe second problem went in the opposite direction. The app expected its backend on `localhost:3000`\n\nand Metro on `localhost:8081`\n\n, but on Android, `localhost`\n\nmeans the phone, not the Ubuntu tower.\n\nADB solved that boundary with two reverse mappings:\n\n`localhost:3000`\n\non Android reached the backend on Ubuntu.`localhost:8081`\n\non Android reached Metro on Ubuntu.In plain language, `adb reverse`\n\nmade the phone's localhost point back to the development services on the tower. Tailscale let Ubuntu reach Android. Once ADB was connected, the reverse mappings let the Android app reach Ubuntu.\n\nFor normal JavaScript or TypeScript changes, I didn't need another native build.\n\nMetro served the updated bundle to the development build already installed on the phone. Changes to native dependencies, permissions, or configuration still required rebuilding and reinstalling the app that now with adb connected the agent can install on its own\n\nI later recorded a separate demo with [Synapse](https://dev.to/juandastic/my-wife-sent-297-messages-in-15-days-not-to-me-to-the-ai-i-built-her-the-synapse-story-333o), the personal AI companion I built for my wife. This wasn't the restaurant session or the same feature. I wanted a recording that showed the mechanics from beginning to end.\n\nFrom the Android phone, I paired wireless debugging with Ubuntu through the phone's Tailscale address. Codex established the ADB connection, restored the reverse mapping for Metro, and tried to open the Synapse development client. It then discovered that Synapse wasn't installed on the phone.\n\nFor this demo, the development build came from Expo services instead of being compiled locally on the tower. The build took about 25 minutes. Once it was ready, Codex downloaded the APK to Ubuntu, installed it through remote ADB, restored the Metro tunnel, and launched Synapse on the phone.\n\nThen I used Codex on that same Android device to request a visible change:\n\nthe phone directed the coding agent and received the result.\n\nThe restaurant was the first time all of these pieces felt like one coherent workflow. The Ubuntu tower was at home. My MacBook was off in my backpack. The Android phone in my hand was connected to a hotspot from another phone, and both the development phone and Ubuntu were reachable through Tailscale.\n\nThat hotspot was necessary for a slightly annoying reason. Android wireless debugging is designed around Wi-Fi. Mobile data alone\n\nwasn't enough to keep the wireless debugging mode available on my phone.\n\nTailscale solved the route between devices, but it couldn't make Android treat a cellular connection as Wi-Fi.\n\nConnecting the development phone to another phone's hotspot satisfied Android's Wi-Fi requirement. Tailscale still provided the private path back to Ubuntu, so ADB on the tower could reach the wireless-debugging port even though one device was at home and the other was with me at the restaurant.\n\nI opened Codex on Android and requested a change. The agent had the real repository, its dependencies, the backend, and the tools it needed on Ubuntu.\n\nWhen the change was ready, I switched to the Expo app and verified it on the same phone.\n\nThis wasn't conventional coding on a phone. I wasn't editing TypeScript with a small keyboard or running Gradle on Android. The phone was doing two different jobs: it was the control surface for the coding agent and the physical device receiving the result.\n\nMy MacBook Air is still the best place for focused editing, browser work, and long development sessions. This setup didn't make it obsolete. It changed its role.\n\nThe codebase, services, tools, and agent environment now persist together on Ubuntu. The MacBook is one client for that environment. Android became another client for starting, steering, observing, and testing certain tasks.\n\nThis is why the setup now feels better to me than simply buying a more powerful laptop. A larger laptop could run more work locally, but the environment would still be attached to the device I carry around and close at the end of a session.\n\nI notice the difference at my full-time job. There, I run the codebase and services locally and work with Claude Code on the same laptop. It is a familiar and capable setup, but now I miss being able to open my phone, request a full-stack change, and know that the complete environment will remain available even if the laptop is locked or turned off.\n\nI thought I was giving an old gaming PC a second life. What changed was which machine my development work depended on.\n\nNow, I’m contemplating what could be achieved with a more powerful development box or even a cluster of them.\n\nAre your coding agents still tied to the laptop in front of you, or have you moved the environment somewhere persistent?\n\nIf you are building AI products and want to follow along, I write about the problems and trade-offs that appear when someone actually uses them.\n\nFollow me:", "url": "https://wpnews.pro/news/i-updated-my-android-app-from-a-restaurant-my-laptop-was-off", "canonical_source": "https://dev.to/juandastic/i-updated-my-android-app-from-a-restaurant-my-laptop-was-off-283k", "published_at": "2026-07-27 04:22:12+00:00", "updated_at": "2026-07-27 04:33:17.303699+00:00", "lang": "en", "topics": ["developer-tools", "ai-agents", "ai-infrastructure"], "entities": ["Codex", "Expo", "Tailscale", "VS Code", "Ubuntu", "MacBook Air", "Android"], "alternates": {"html": "https://wpnews.pro/news/i-updated-my-android-app-from-a-restaurant-my-laptop-was-off", "markdown": "https://wpnews.pro/news/i-updated-my-android-app-from-a-restaurant-my-laptop-was-off.md", "text": "https://wpnews.pro/news/i-updated-my-android-app-from-a-restaurant-my-laptop-was-off.txt", "jsonld": "https://wpnews.pro/news/i-updated-my-android-app-from-a-restaurant-my-laptop-was-off.jsonld"}}