Turn a cheap [Motorola Moto G 2025](https://www.walmart.com/ip/ST-MOTOROLA-XT2513V-CDMA-LTE-GRAY-HANDSET-64GB-WALMART-POSA/14552506783) into a portable Linux dev environment.
## What Can You Do With This?
- **Host a website** — See [walmartphone.stetsonblake.com](https://walmartphone.stetsonblake.com) for a live example running on a Moto G 2025
- **Vibe code with Claude** — Install Claude Code and let it help you set up and configure the system
- **Run a Minecraft server** — [Marshall Richards did exactly this](https://x.com/marshallrichrds/status/2001533197365162111)
- **Run monitoring tools** — Prometheus node exporter, Grafana, whatever fits
- **Self-host services** — Nginx, cloudflared tunnels, APIs, databases
All from a $25 phone in your pocket.
## Initial Setup
### Enable RAM Boost
The Moto G 2025 has only 4GB of physical RAM, which can cause performance issues out of the box. **Turn on RAM Boost immediately**—it makes a significant difference.
**How it works:** RAM Boost uses part of your internal storage as virtual memory, caching infrequently accessed data so more physical RAM stays available for active processes. With 8GB of virtual RAM enabled, your phone effectively has 12GB total (4GB fast physical RAM + 8GB slower virtual RAM).
**To enable:**
1. Swipe down from the top right → Settings
2. Scroll down and tap **System**
3. Tap **Performance**
4. Toggle **RAM Boost** on
5. Set it to **8GB** (the maximum)
Per [CNET's hands-on review](https://www.cnet.com/tech/mobile/moto-g-2025-hands-on-a-decent-200-phone-once-you-change-this-setting/): after enabling RAM Boost, "the more extreme performance issues largely disappeared"—apps load quickly, and the phone becomes much more usable.
After enabling this on my own device, the phone became much more stable—fewer app crashes, smoother multitasking, and Termux processes stopped getting killed unexpectedly.
### Why F-Droid?
The Play Store version of Termux is abandoned and won't receive updates. Install from [F-Droid](https://f-droid.org/) instead—it's an open-source app store for Android. All Termux add-on apps (Boot, API, etc.) must come from the same source to work together, so stick with F-Droid for everything.
### Apps to Install
- **[Termux](https://termux.dev/en/)** - Full Linux terminal emulator for Android. Gives you a package manager (`pkg`), shell access, and command-line tools without rooting.
- **[Termux:Boot](https://wiki.termux.com/wiki/Termux:Boot)** - Runs scripts automatically when your device boots. Essential for starting SSH servers and services without manual intervention. After installing, open the app once to enable it, then place executable scripts in `~/.termux/boot/`.
- **[Andronix](https://andronix.app/)** (optional) - Installs full Linux distributions (Ubuntu, Debian, Arch, etc.) inside Termux using proot. Provides a nice UI for selecting distros and configurations. Alternatively, you can skip this and use [proot-distro](https://github.com/termux/proot-distro) directly from Termux—it's built-in and lighter weight.
## Termux Basics
```bash
pkg update && pkg upgrade
```
Accept defaults when prompted.
## SSH Access
Getting SSH set up early makes everything easier—you can work from your laptop instead of typing on a phone screen.
SSH runs on port **8022** by default in Termux.
### Initial Setup
Set a password for your Termux user:
```bash
passwd
```
Find your username:
```bash
whoami
```
Find your phone's IP address:
```bash
ifconfig
```
Install and start the SSH server:
```bash
pkg install openssh
sshd
```
### Connecting from Another Device
From your computer or another device:
```bash
ssh <username>@<phone-ip> -p 8022
```
On iOS, [Terminus](https://apps.apple.com/app/terminus-terminal-ssh-client/id1512873443) is a good SSH client.
### Auto-start SSH on Boot
Create `~/.termux/boot/start-sshd`:
```bash
#!/data/data/com.termux/files/usr/bin/sh
echo "$(date): boot script started" >> ~/boot.log
termux-wake-lock
echo "$(date): wake lock acquired" >> ~/boot.log
sshd
echo "$(date): sshd started" >> ~/boot.log
```
Make it executable:
```bash
chmod +x ~/.termux/boot/start-sshd
```
## Installing a Linux Distro
### How proot Works
Both Andronix and proot-distro use [proot](https://proot-me.github.io/)—a userspace implementation of `chroot`, `mount --bind`, and `binfmt_misc`. It intercepts system calls and translates them, letting you run a full Linux root filesystem without actual root access. The kernel is still Android's, but everything else (packages, filesystem layout, tools) is your chosen distro.
### Option 1: Andronix
1. Open Andronix
2. Choose an OS (Ubuntu, Debian, etc.)
3. Choose mode: GUI, Windowed, or CLI only
4. Andronix copies an install command to clipboard
5. Switch to Termux, long-press to paste, and run
### Option 2: proot-distro
[proot-distro](https://github.com/termux/proot-distro) is Termux's built-in alternative—lighter weight and no app required:
```bash
pkg install proot-distro
proot-distro install ubuntu
proot-distro login ubuntu
```
### Launching Your Distro
After installation, Andronix creates a startup script in your Termux home directory. To enter the Linux environment:
```bash
./start-ubuntu22.sh # or whatever distro you installed
```
A typical workflow: SSH into the phone, then run the start script to drop into your Linux shell.
## Understanding the Init System (or Lack Thereof)
### What's Running Under the Hood
The Android phone itself uses **Android init**—a custom init system specific to Android that uses `init.rc` files. It's not systemd, SysVinit, or OpenRC.
Inside your Andronix/proot container, there's **no real init system at all**. Proot doesn't actually boot a full Linux system—it just provides a filesystem overlay with process translation. The "init" process (PID 1) inside the container is usually just a shell or whatever process you launched when entering the environment.
### Why systemd Won't Work
systemd requires real kernel-level privileges (cgroups, namespaces) that proot can't provide. You're running in userspace with syscall translation, not actual root.
### Alternatives for Service Management
Since you can't use systemd, your options are:
- **Manual background processes** — Start things with `nohup` and `&`
- **Supervisor** — Lightweight process manager
- **runit or s6** — Simpler init-like systems that can run unprivileged
- **`.bashrc` scripts** — Start services on login (see below)
### The .bashrc Workaround
A practical approach: add a startup script to your `.bashrc` that checks if services are running and starts them if not. Logging in automatically brings everything up:
```bash
#!/bin/bash
# Add to .bashrc - starts services if not already running
# Start nginx (silently skip if already running)
nginx 2>/dev/null
# Start cloudflared tunnel if not connected
if ! curl -s http://127.0.0.1:20241/metrics >/dev/null 2>&1; then
nohup cloudflared tunnel run phone-stats > /var/log/cloudflared.log 2>&1 &
fi
# Start node_exporter if not running
if ! curl -s http://127.0.0.1:9100/metrics >/dev/null 2>&1; then
nohup node_exporter --web.listen-address=":9100" > /var/log/node_exporter.log 2>&1 &
fi
```
## Keeping Processes Alive (Android 12+)
Android aggressively kills background processes. The Moto G 2025 runs Android 16—it's unclear if all these steps are still necessary, but if you notice crashes or processes dying unexpectedly, try these fixes.
### Disable Child Process Restrictions (Android 14+)
The simplest fix—no ADB required:
1. Enable Developer Options (tap Build Number 7 times in Settings → About Phone)
2. Go to Developer Options → "Disable child process restrictions"
3. Turn this setting on
### ADB Method (if the above isn't enough)
Use ADB over wireless debugging for more aggressive fixes.
#### Enable Wireless Debugging
1. Enable Developer Options (tap Build Number 7 times)
2. Go to Developer Options → Wireless Debugging → Enable
3. Tap "Pair device with pairing code"
#### Install ADB in Termux
```bash
pkg install android-tools
```
#### Pair and Connect
```bash
adb pair <ip>:<pairing-port> <pairing-code>
adb connect <ip>:<connection-port>
```
*Note: The pairing port and connection port are different. Check the Wireless Debugging screen for each.*
#### Disable Phantom Process Killer
Run these commands to prevent Android from killing background processes:
```bash
adb shell "/system/bin/device_config set_sync_disabled_for_tests persistent"
adb shell "/system/bin/device_config put activity_manager max_phantom_processes 2147483647"
adb shell settings put global settings_enable_monitor_phantom_procs false
```
Reference: [Andronix Android 12+ Guide](https://docs.andronix.app/android-12/andronix-on-android-12-and-beyond#run-the-commands)
### Keep Termux Awake with Wake Lock
Android aggressively suspends apps to save battery. When the screen turns off, the CPU can enter a low-power state that pauses your processes.
`termux-wake-lock` acquires a partial wake lock—it tells Android to keep the CPU running even when the screen is off. You'll see a persistent notification indicating the wake lock is active.
```bash
termux-wake-lock # acquire wake lock
termux-wake-unlock # release it
```
**Trade-off:** This keeps your phone awake and drains battery faster, but it's necessary if you're running servers or long-running processes. Include it in your boot script to ensure processes survive screen-off.
## Useful Tools
### tmux
Essential for keeping sessions alive and managing multiple terminals:
```bash
pkg install tmux
```
### Claude Code
Inside your Andronix Linux environment, you can install Claude Code and have it help set up the system:
```bash
curl -fsSL https://claude.ai/install.sh | bash
```
### Prometheus Node Exporter
Monitor your phone's system metrics:
```bash
pkg install prometheus-node-exporter
node_exporter
```
## Hosting Your Own Website
Here's how to set up a publicly accessible website running on your phone. This setup uses nginx as the web server and Cloudflare Tunnel to expose it to the internet without opening ports.
### The Stack
- **nginx** — Web server, listens on localhost
- **Cloudflare Tunnel** — Exposes local server to the internet securely
- **Tailscale** — Private network access (use the Android app, not CLI)
- **Supervisor** — Process manager (optional, for more robust service management)
### Install nginx
Inside your Linux environment:
```bash
apt update && apt install nginx
```
### Configure nginx
Create a site config at `/etc/nginx/sites-available/your-site`:
```nginx
server {
listen 127.0.0.1:8001;
server_name localhost;
root /root/projects/your-site;
index index.html;
access_log /var/log/nginx/your-site.access.log combined;
error_log /var/log/nginx/your-site.error.log;
location / {
try_files $uri $uri/ =404;
}
add_header X-Served-By "Walmart Phone" always;
}
```
**Important:** Bind to `127.0.0.1`, not `0.0.0.0`. Termux/proot has limitations with binding to all interfaces.
Enable the site:
```bash
ln -s /etc/nginx/sites-available/your-site /etc/nginx/sites-enabled/
nginx -t # test config
nginx # start (or nginx -s reload if already running)
```
### Set Up Cloudflare Tunnel
Cloudflare Tunnel lets you expose your local server without port forwarding or a static IP.
1. Install cloudflared:
```bash
apt install cloudflared
```
2. Authenticate with Cloudflare:
```bash
cloudflared tunnel login
```
3. Create a tunnel:
```bash
cloudflared tunnel create phone-stats
```
4. Create the config at `~/.cloudflared/config.yml`:
```yaml
tunnel: phone-stats
credentials-file: /root/.cloudflared/<tunnel-id>.json
ingress:
- hostname: yoursite.yourdomain.com
service: http://localhost:8001
- service: http_status:404
```
5. Route DNS (in Cloudflare dashboard or via CLI):
```bash
cloudflared tunnel route dns phone-stats yoursite.yourdomain.com
```
6. Run the tunnel:
```bash
cloudflared tunnel run phone-stats
```
### Tailscale for Private Access
For private network access (SSH from anywhere, internal services), use the **Tailscale Android app** instead of the CLI. The app handles the VPN connection at the system level, which works better than trying to run `tailscaled` in Termux.
Install from Play Store, sign in, and your phone gets a stable Tailscale IP you can SSH to from any device on your tailnet.
### Auto-start Everything
Create a startup script at `/root/start-server.sh`:
```bash
#!/bin/bash
# Start services if not already running
# Start nginx (silently skip if already running)
nginx 2>/dev/null
# Start cloudflared if not already connected
if ! curl -s http://127.0.0.1:20241/metrics >/dev/null 2>&1; then
nohup cloudflared tunnel run phone-stats > /var/log/cloudflared.log 2>&1 &
fi
# Start node_exporter if not running
if ! curl -s http://127.0.0.1:9100/metrics >/dev/null 2>&1; then
nohup node_exporter --web.listen-address=":9100" > /var/log/node_exporter.log 2>&1 &
fi
```
Add to your `.bashrc`:
```bash
/root/start-server.sh
```
Now logging into your Linux environment automatically starts all services.
### Using Supervisor (Alternative)
For more robust process management, use Supervisor instead of manual nohup scripts:
```bash
apt install supervisor
```
Create `/etc/supervisor/conf.d/cloudflared.conf`:
```ini
[program:cloudflared]
command=cloudflared tunnel run phone-stats
autostart=true
autorestart=true
stderr_logfile=/var/log/cloudflared.err.log
stdout_logfile=/var/log/cloudflared.out.log
```
Start Supervisor:
```bash
supervisord
supervisorctl status
```
### Logs and Debugging
Check your logs:
```bash
tail -f /var/log/nginx/your-site.access.log
tail -f /var/log/cloudflared.log
```
Test locally before exposing:
```bash
curl http://127.0.0.1:8001
```