Oct 25, 2025
4 min read
Android,

Some Operations with Termux

Enable Termux to access the Android file system:

termux-setup-storage

This operation will create a storage folder in the Termux $HOME directory, containing soft links to the Android file system.

Enable the SSH service, which makes it more convenient to operate the phone from a computer. Actually, you can just execute sshd directly in Termux to enable the SSH service.

Note that:

  1. The default SSH port for Termux is 8022, not 22
  2. You need to set a password for the SSH service, otherwise you won’t be able to log in. Use the passwd command to set the password.

So the entire SSH login process is as follows:

ssh -p 8022 root@phone_IP

Additionally, ADB can also be used as a file transfer tool. Here we won’t talk about wired connections, only wireless ADB connections. ADB seems to have some bugs where sometimes even if we enter the correct IP and port, ADB will still show a connection failure.

Something like this below:

~ took 54s
❯ adb connect 192.168.1.150:39777
failed to connect to '192.168.1.150:39777': No route to host

~
❯ adb pair 192.168.1.150:41797
Enter pairing code: 164252
error: protocol fault (couldn't read status message): Undefined error: 0

If you’re not specifically doing Android development, troubleshooting this can be completely clueless. If the IP can be pinged, it indicates that the port is definitely incorrect, but the phone does show this port. Actually, if you understand ADB well, you’d probably know that the ADB server has a problem. ADB communicates through the ADB server. When in doubt, restart to solve the issue, we can try restarting the ADB server.

adb kill-server
adb start-server

Now we have 2 ways to transfer files to the Termux on the phone:

  1. Use ADB
  2. Use SSH

However, I don’t like the ADB command very much, generally using the more convenient scrcpy for remote phone control.

Let’s try writing a web server with Rust.

#[tokio::main]
async fn main() {
    let app = Router::new()
        .route("/", get(root));

    let listener = tokio::net::TcpListener::bind("0.0.0.0:12123").await.unwrap();
    axum::serve(listener, app).await.unwrap();
}

async fn root() -> &'static str {
    "Hello, World!"
}

Add target

rustup target install aarch64-linux-android

Build:

cargo build -r --target aarch64-linux-android

Error:

error: linking with `cc` failed: exit status: 1
// omitting some output
[-Wunused-command-line-argument]
          ld: unknown options: --as-needed -Bstatic -Bdynamic --eh-frame-hdr -z --gc-sections -z -z --strip-debug 
          clang: error: linker command failed with exit code 1 (use -v to see invocation)

The reason is that my computer’s default linker cc (actually invoking Clang) passes parameters during linking that are not supported by the target platform, causing the linking to fail.

Which linker should we use? Since we’re cross-compiling for Android, and Android’s cross-compilation tool suite is the NDK, we need the linker provided by the NDK.

I’m using the NDK installed via brew here, which has a different path from manually downloaded NDKs.

brew install android-ndk

Use brew info android-ndk to check the installation path.

❯ brew info android-ndk
==> android-ndk: 29
https://developer.android.com/ndk/index.html
Installed
/opt/homebrew/Caskroom/android-ndk/29 (3.3GB)
// omitting some output

My own NDK linker path is /opt/homebrew/Caskroom/android-ndk/29/AndroidNDK14206865.app/Contents/NDK/toolchains/llvm/prebuilt/darwin-x86_64/bin/aarch64-linux-android29-clang.

Add to .cargo/config.toml:

[target.aarch64-linux-android]
linker = "/opt/homebrew/Caskroom/android-ndk/29/AndroidNDK14206865.app/Contents/NDK/toolchains/llvm/prebuilt/darwin-x86_64/bin/aarch64-linux-android29-clang"
rustflags = ["-C", "link-arg=-fuse-ld=lld"]

Actually, this is quite similar to using Rust for Android development, which also requires using the linker provided by the NDK to compile Rust code. So if we’re developing JNI, it’s similar as well.

If there are no unexpected issues, the compilation should succeed.

To copy to the phone, you can choose ADB or scrcpy (dragging files to the window by default transfers files), which might require accessing the file via the storage directory in Termux. I’m choosing to use the scp command to directly copy to the $HOME directory.

scp -P 8022 ./target/aarch64-linux-android/release/tinyserver root@192.168.1.150:./

Run directly on Termux:

./tinyserver

Termux can run many server software, such as nginx, apache, lighttpd, etc. So in theory, services in other languages can also run. Using an old phone as a server at home with Termux is quite good.