r/Magisk • u/MadJackTheThird • 9h ago
Tutorial Tutorial: turn an old Samsung phone into a private, always-on git server (Magisk + Termux + Tailscale), including every mistake I made so you don't
I'm making this post in the hope it will help someone else turn a piece of e-waste in an actually useful piece of tech. I'm not really a "tutorial guy" so please feel free to correct me anytime.
Keep in mind that this tutorial isn't exactly 'non-tech-savvy friendly', so don't be surprised if you turn to your go-to chatbot for a few quick questions to understand what you're doing and if it's correct.
I had two git repos that are too big for GitHub (about 6 GB and 400 MB), and an old phone. Here's how I turned a Galaxy A14 4G (SM-A145R, MediaTek MT6769) into a git-over-SSH server:
- It's reachable only through my Tailscale network.
- It can't reach my PCs.
- Every app on it except two has no network access.
- The battery is held at 60–70% even though the charger is always plugged in.
Below is what to do, and in "DON'T" boxes, what went wrong for me. I did all of it from Ubuntu. Most of it applies to any recent Samsung, and a lot of it to any rootable Android phone.
What you need
- A Samsung phone whose bootloader can be unlocked. Most international/EU models can. US models usually can't: if download mode shows no "OEM LOCK" line, give up.
- A Linux PC (Windows works too, with different tools), a USB cable, and a free Tailscale account.
- Patience: the phone will be wiped several times.
1. Unlock the bootloader
- Settings → Developer options → OEM unlocking on.
- Power off. Hold Vol Up + Vol Down while plugging the USB cable in (PC end connected). Long-press Vol Up to unlock, then confirm. This wipes the phone.
- Do the setup connected to Wi-Fi. Afterwards, the OEM unlocking switch must be greyed out. In download mode,
KG STATUSshould readCHECKING,COMPLETEDorBROKEN.PRENORMALmeans Knox Guard won't accept unofficial images yet: wait for about 168 hours of uptime.
2. Download the exact firmware
Use samloader-rs by topjohnwu (the Magisk author): https://github.com/topjohnwu/samloader-rs. It downloads straight from Samsung's servers and flashes.
- Samsung now only serves the latest firmware, and the request must include the phone's serial or IMEI, otherwise you get error
S01. - Verify the MD5 trailer of every
.tar.md5.
3. Patch the boot image with Magisk
Patch the firmware's AP file with the Magisk app ("Select and Patch a File"), or patch only init_boot.img with Magisk's boot_patch.sh over adb. The result is the same: a patched init_boot, vbmeta with flags = 3 (verification off), and no userdata.img.
4. Flash and wipe
samloader flash -b BL_*.tar.md5 -a magisk_patched.tar -c CP_*.tar.md5 -s CSC_*.tar.md5
Use CSC, not HOME_CSC: the first Magisk install needs a data wipe. If recovery then says "Can't load Android system", choose Factory data reset. That's expected.
DON'T use Heimdall on newer Samsungs. On mine:
- its timeouts were too short (
userdatafailed);- flashing
miscmade the bootloader fail the whole session, leaving the phone on "An error occurred while updating the device software";- every half-finished session blocked all later handshakes until I restarted into download mode with the buttons.
samloader-rs worked first try.
DON'T flash HOME_CSC for the first root ("to keep data"): I got a boot loop.
DON'T flash the stock
init_boot/vbmetaback to "undo" a boot loop. Magisk's docs warn about this, and they're right: it keeps looping until you wipe.DON'T rely on
--erase: on MediaTek it erased "0 sectors".DON'T use
-p partition_namein samloader-rs on MediaTek: it upper-cases the name and the PIT is lowercase. Use-f init_boot.img. Also, a failed command in the middle of a session means a button restart.
5. First boot
- Minimal setup: Wi-Fi, no accounts. Then developer options and USB debugging.
- Before installing anything over adb, disable the Play Store and the Galaxy Store:
pm disable-user --user 0 com.android.vending pm disable-user --user 0 com.sec.android.app.samsungappsRight after setup they restore and install apps (on mine even TikTok) and occupy the install queue, soadb installjust hangs. - Install the full Magisk app and accept its "additional setup" reboot. From then on,
adb shell su -c idgives root.
6. Debloat: in batches, with a failsafe
pm uninstall --user 0 <package> doesn't touch the system partition and can be undone with cmd package install-existing <package>. But:
DON'T remove 170 packages in one go. I did, and the phone stuck on "Phone is starting..." forever, with no USB and no adb. Factory reset.
DON'T remove Google Play Services (
com.google.android.gms), GSF and Chrome on One UI 7, even if you never use Google. Samsung reinstalls them at boot; if you remove them again, boot hangs exactly like above.Some packages (setup wizards, Smart Switch, AdServices and telemetry modules, a few Samsung services) are re-enabled by Samsung at every boot, whether you uninstall or disable them. Don't fight them: firewall them (step 9).
What worked for me:
- Batches, with one reboot test per batch.
- A Magisk
service.dfailsafe: before applying a batch, write its list to/data/adb/.../pending.txt. At boot, a script waits up to 7 minutes for a real home screen (notFallbackHome, the activity behind "Phone is starting..."). If it doesn't come, it reinstalls the batch and reboots. - Test the failsafe on purpose with a fake failure. Mine was broken the first time:
DON'T redirect
cmd package ...output to a file under/data/adbin a Magisk script. system_server isn't allowed to write to that fd (SELinux), so the call fails with "Failed transaction". Capture the output through a pipe (r=$(cmd package ... 2>&1 | cat)).
Rescue without a factory reset. A hang on "Phone is starting..." can be fixed without wiping:
- Add an
overlay.d/*.rcplus a script to the Magisk-patchedinit_bootramdisk.magiskbootfrom the Magisk APK (lib/x86_64) runs on Linux. - The script waits for the package service, runs
cmd package install-existingon what you removed, writes a marker file, and reboots once. - Flash just
init_bootfrom download mode.
That saved me a second full reinstall.
7. Termux + sshd
- Install Termux and Termux:Boot from their GitHub releases, or F-Droid.
pkg install openssh git.- In
sshd_config: port 8022,PasswordAuthentication no,AuthenticationMethods publickey, all forwarding off. - On the phone, put your repos in bare repositories:
git init --bare ~/git/<name>.git.
Running Termux commands from a root shell:
su <termux-uid> -g <termux-uid> -G 3003 -G 9997 -c "export PREFIX=... HOME=... PATH=$PREFIX/bin LD_PRELOAD=$PREFIX/lib/libtermux-exec-ld-preload.so; ..."
DON'T forget
-g: otherwise the first-Gbecomes the primary group and dpkg fails with "unable to change ownership".DON'T forget
-G 3003(inet): without it, Android denies network sockets.DON'T trust Termux:Boot to keep sshd up. Android killed its process as "empty" before sshd started.
Instead, start sshd from a Magisk service.d script as the Termux user, check it every 30 seconds, and hold a kernel wake lock (echo myserver > /sys/power/wake_lock) so the CPU never sleeps.
8. Tailscale
- Install from F-Droid.
- In the admin console, create a single-use, pre-approved auth key with a tag (e.g.
tag:gitserver). In the app: Settings → Accounts → ⋮ → "Use an auth key". - ACLs: your own devices may reach
tag:gitserverontcp:8022. The tag is never a source, so a stolen phone can't connect to your other machines. - Set Tailscale as Always-on VPN (
settings put secure always_on_vpn_app com.tailscale.ipn), otherwise it doesn't come back after a reboot.
DON'T use Private DNS in "strict" mode (e.g.
dns.quad9.net) together with Tailscale on Android: all DNS dies. Use "automatic", and turn "Use Tailscale DNS" off on the phone.
9. Firewall (iptables from a Magisk script)
- Inbound: port 8022 only on
tun+(Tailscale) andlo(adb forward); no new TCP connections from Wi-Fi. Outbound:
- no new connections to private ranges, except Tailscale's own UID (so it can use direct LAN paths), DHCP, and DNS (53) for system UIDs;
- then
-m owner --uid-owner 10000-99999 -j REJECTfor every app except Termux and Tailscale.
The app rule also takes care of the Google/Samsung packages you weren't allowed to remove.
DON'T block DNS to your router: in "automatic" mode Android's resolver uses it, and everything breaks the moment you block the LAN. I found out the hard way.
DON'T
pkill -forgrepfor a pattern that also appears in your own command line (e.g. the script path): you kill your own shell. It happened to me three times.
Re-check the chains every minute, because netd can rebuild them.
10. Battery on a phone that's always plugged in
Cheap Samsungs don't have "Pause USB Power Delivery", but the battery driver exposes:
/sys/class/power_supply/battery/store_mode:1= retail demo mode. Charging stops and the phone runs from the charger, but the battery sits at 100%./sys/class/power_supply/battery/input_suspend:1cuts the charger input, so the phone drains (about 160 mA on mine).
A 5-minute loop holds the battery between 60 and 70%: suspend the input above 70%, restore it at 60%, always restore below 55%. Note that Samsung's gauge shows 100% for a while after real discharge starts.
11. Push the repos
Do the first push over USB (adb forward tcp:18022 tcp:8022), it's much faster. After that, push over Tailscale. Set receive.denyNonFastForwards true on the bare repos so a bad force-push can't destroy history.
Result
The phone boots unattended (no Power press needed at the unlocked-bootloader warning), and SSH over Tailscale is up about 80 seconds after power-on.
This was done with the great help of Claude Opus 5.5.