TL;DR No Docker, no Android Studio. Just Guix and a few commands.
In a previous post, I shared how to set up React Native development on Guix using Docker. It works, but there’s a simpler way.
Guix has a neat trick: guix shell --container --emulate-fhs. Android SDK binaries expect libraries at standard paths like /lib64/ld-linux-x86-64.so.2, which don’t exist on Guix. The --emulate-fhs flag creates these paths, so cmake, ninja, and the NDK toolchain just work. No Docker required.
Setup
First, download the Android SDK. Guix packages sdkmanager from the F-Droid project:
export ANDROID_HOME=$PWD/android-sdk
mkdir -p $ANDROID_HOME
guix shell sdkmanager -- sh -c "
export ANDROID_HOME='$ANDROID_HOME'
echo y | sdkmanager --licenses
sdkmanager 'platforms;android-36' 'build-tools;35.0.0' 'ndk;27.1.12297006'
"
Build
Now build your APK or AAB:
guix shell --container --emulate-fhs --network --pure \
--share="$HOME/.gradle"="$HOME/.gradle" \
--share="$ANDROID_HOME"="$ANDROID_HOME" \
--share="$PWD"="$PWD" \
openjdk@17:jdk node pnpm coreutils bash grep sed \
glibc gcc-toolchain zlib which findutils diffutils \
-- sh -c "
cd '$PWD'
export ANDROID_HOME='$ANDROID_HOME'
export JAVA_HOME=\$(dirname \$(dirname \$(readlink -f \$(which java))))
unset C_INCLUDE_PATH CPLUS_INCLUDE_PATH CPATH
pnpm install
cd android && ./gradlew --no-daemon assembleRelease
"
Output: android/app/build/outputs/apk/release/app-release.apk
For an AAB (Play Store), use bundleRelease instead of assembleRelease.
What’s happening?
A few key flags make this work:
--emulate-fhscreates/lib64/ld-linux-x86-64.so.2and standard FHS paths that Android SDK binaries expect--puregives you a clean environment without inherited variables--networkallows Gradle to download dependenciesunset C_INCLUDE_PATHis extra insurance against header conflicts
The container shares your gradle cache, SDK, and project directory, so subsequent builds are fast.
Signed Release
For a signed release, pass the keystore details as Gradle properties:
guix shell --container --emulate-fhs --network --pure \
--share="$HOME/.gradle"="$HOME/.gradle" \
--share="$ANDROID_HOME"="$ANDROID_HOME" \
--share="$PWD"="$PWD" \
--share="/path/to/keys"="/path/to/keys" \
openjdk@17:jdk node pnpm coreutils bash grep sed \
glibc gcc-toolchain zlib which findutils diffutils \
-- sh -c "
cd '$PWD'
export ANDROID_HOME='$ANDROID_HOME'
export JAVA_HOME=\$(dirname \$(dirname \$(readlink -f \$(which java))))
unset C_INCLUDE_PATH CPLUS_INCLUDE_PATH CPATH
pnpm install
cd android && ./gradlew --no-daemon \
-PMYAPP_UPLOAD_STORE_FILE=/path/to/keys/release.keystore \
-PMYAPP_UPLOAD_STORE_PASSWORD=yourpassword \
-PMYAPP_UPLOAD_KEY_ALIAS=youralias \
-PMYAPP_UPLOAD_KEY_PASSWORD=yourpassword \
-PuseReleaseSigning=true \
bundleRelease
"
Troubleshooting
Gradle permission errors: If you’ve previously run Docker builds as root, the gradle cache may have wrong permissions:
sudo rm -rf ~/.gradle/native/
CMake crashes (exit 139): Re-download the SDK cmake:
rm -rf android-sdk/cmake
# It re-downloads on next build
Header conflicts / stubs-32.h errors: Make sure you’re using --pure and unsetting the C include paths.
Conclusion
This approach eliminates Docker from the React Native build process on Guix. The FHS container gives you the compatibility you need, while keeping everything in user space. Build times are comparable to Docker, and you have one less layer to debug.