Run Unity 6 VR projects on Linux with a Meta Quest 3 headset via SteamVR + ALVR — no Windows required.
Unity's OpenXR plugin ships an Android x86_64 native binary (libUnityOpenXR.so) even on desktop Linux. This binary was compiled against Android's bionic libc and expects Android-specific APIs (JNI, EGL, XR_KHR_android_create_instance). On a standard Linux desktop with glibc, it simply crashes.
This project provides a set of compatibility shims and API layers that bridge the gap, making the Android OpenXR binary work transparently on desktop Linux with SteamVR.
- Bionic-to-glibc compatibility shims — Shared libraries that export bionic's
LIBC-versioned symbols, forwarding calls to their glibc equivalents - OpenXR API layer — Strips Android-specific extensions (
XR_KHR_android_create_instance) from OpenXR instance creation - Vulkan implicit layer — Force-enables
VK_KHR_timeline_semaphorethat SteamVR requires but Unity doesn't enable - Fake JVM shim — Provides a stub
JavaVM/JNIEnvviaLD_PRELOADso the Android binary's JNI code doesn't crash - Patched OpenXR loader path — Directs Unity to use the system
libopenxr_loader.soinstead of a Windows/UWP binary - Launch script — Orchestrates all of the above with proper environment variables
Working. Both editor play mode and standalone APK builds are functional.

| Mode | Status | Details |
|---|---|---|
| Play from Editor (Linux) | Tested | Via SteamVR + ALVR streaming to Quest 3. Use launch_unity_vr.sh |
| Play from Editor (Android build profile) | Tested | Same as above — build profile doesn't matter for editor play |
| Build APK → Meta Quest 3 | Tested | Direct on-device VR via USB ADB. No ALVR/SteamVR needed |
- Arch Linux (kernel 6.x)
- Unity 6 (tested on 6000.3.11f1)
- Meta Quest 3
- ALVR 20.x + SteamVR (for editor streaming)
- Vulkan renderer
Controllers, head tracking, and rendering all function. This should work on other glibc-based Linux distributions (Ubuntu, Fedora, etc.) with appropriate package name adjustments.
- A PC with a Vulkan-capable GPU (NVIDIA or AMD)
- Meta Quest 3 (or other Quest headset)
- WiFi connection between PC and Quest (or USB cable)
| Package | Arch Linux | Ubuntu/Debian equivalent |
|---|---|---|
| GCC | base-devel |
build-essential |
| OpenXR loader | openxr |
libopenxr-loader1, libopenxr-dev |
| Vulkan drivers | vulkan-radeon or nvidia |
mesa-vulkan-drivers or nvidia-driver |
| Vulkan validation layers | vulkan-validation-layers |
vulkan-validationlayers |
| SteamVR | Steam → Tools → SteamVR | Same |
| ALVR | AUR: alvr or GitHub releases |
GitHub releases |
- Unity 6 (tested on 6000.3.11f1) with Linux Build Support
- Android Build Support (for APK builds) — install via Unity Hub → Add Modules
- Required packages (via Unity Package Manager):
com.unity.xr.openxr(1.16.1)com.unity.xr.management(4.5.4) (installed automatically with openxr)com.unity.inputsystem(1.19.0) (installed automatically with openxr)com.unity.xr.interaction.toolkit(3.3.1) — for controller input (XR Interaction Toolkitin package manager)
- Developer mode enabled on Quest
- For editor streaming: ALVR client installed on Quest
- For APK builds: USB cable + ADB debugging enabled
- Clone this repository
- Install system packages (see table above)
- Set up ALVR:
- Install ALVR on both PC and Quest
- Pair and connect them (WiFi or USB)
- Ensure SteamVR launches via ALVR
- Edit
launch_unity_vr.sh:- Update
UNITY_EDITORpath to your Unity installation - Adjust
GDK_SCALE/GDK_DPI_SCALEif you don't have a HiDPI display (remove or set to 1)
- Update
- Launch:
The script auto-builds all native shims on first run.
chmod +x launch_unity_vr.sh ./launch_unity_vr.sh
- In Unity: Press Play. The scene should appear in your Quest headset.
If you want to add Linux VR support to an existing Unity project:
Copy the entire NativeFix directory into your Unity project root (next to Assets/).
Delete those files if they are exist (or unity will crash):
- NativeFix/openxr_layer/libXrApiLayer_strip_android.so
- NativeFix/vulkan_layer/libVkLayer_force_timeline_sem.so
- NativeFix/libfake_jvm.so
Copy launch_unity_vr.sh to your project root. Edit:
UNITY_EDITOR— path to your Unity editor binary- Remove HiDPI lines if not needed
(you can skip this and just copy Packages/com.unity.xr.openxr to your project from this one)
In Packages/com.unity.xr.openxr/Runtime/OpenXRLoader.cs, find the LoadOpenXRSymbols() method and add a Linux-specific loader path. Look for the #elif UNITY_EDITOR_OSX section and add after it:
#elif UNITY_EDITOR_LINUX
loaderPath = "/usr/lib/libopenxr_loader.so";Also add this guard around the EditorBuildSettings.TryGetConfigObject block so it doesn't override the Linux path:
#if UNITY_EDITOR && !UNITY_EDITOR_LINUX
// Pass down active loader path to plugin
// ...existing code...
#endifIn Unity Editor:
Edit → Project Settings → XR Plug-in Management → OpenXR:
- Ensure OpenXR is enabled for Standalone
- Enable "Vulkan Additional Graphics Queue"
- Under Interaction Profiles, enable Oculus Touch Controller Profile (not "Meta Quest Touch Plus" — SteamVR doesn't support the latter)
Use the XR Interaction Toolkit sample scenes, or add an XR Origin (XR Rig) to your scene:
- Import XRI Starter Assets from the Package Manager samples
- Use the
DemoScene(Assets/Samples/XR Interaction Toolkit/3.3.1/Starter Assets/DemoScene.unity) orXR Interaction Setupprefab
Open Assets/XR/Settings/OpenXR Package Settings.asset
This setting should be with value 1:
m_EditorClassIdentifier: Unity.XR.OpenXR::UnityEngine.XR.OpenXR.Features.Interactions.OculusTouchControllerProfile
m_enabled: 1Note: Both PC/Android profiles in Edit → Project Settings → XR Plug-in Management → OpenXR → Enabled interaction profiles should be Oculus Touch Controller Profile (not "Meta Quest Touch Plus" — SteamVR doesn't support the latter)
./launch_unity_vr.shNote: This error can appear in editor console, but this is not a blocker for a game:
Callback unregistration failed. Callback not registered. UnityEngine.XR.Management.XRGeneralSettings:AttemptInitializeXRSDKOnLoad ()
The NativeFix/ shims and layers are only needed for editor play mode on Linux. APK builds run natively on Android — no compatibility hacks required.
- Android Build Support installed in Unity Hub (includes SDK, NDK, JDK)
- Developer mode enabled on Quest 3
- USB cable connected, with USB debugging authorized on the Quest
- Switch platform: File → Build Settings → Android → Switch Platform
- Configure Player Settings (Edit → Project Settings → Player → Android tab):
- Minimum API Level: Android 10.0 (API level 29) or higher
- Target Architecture: ARM64 only
- Scripting Backend: IL2CPP (required for ARM64)
- Enable OpenXR for Android: Edit → Project Settings → XR Plug-in Management → Android tab → check OpenXR
- Enable Meta Quest Support: Under OpenXR features (Android tab), enable:
- Meta Quest Support — required for Quest initialization and manifest setup
- Oculus Touch Controller Profile — base controller input
- Meta Quest Touch Plus Controller Profile — Quest 3 native controllers
- Update your active scene in build settings
- Build: File → Build Settings → Build and Run (with Quest connected via USB)
Unity's bundled Android NDK may have broken symbolic links on Linux. The symlinks in .../NDK/toolchains/llvm/prebuilt/linux-x86_64/bin/ (like clang++, ld) point to a non-existent android-ndk-r27c/ subdirectory.
Symptom: Build fails with /bin/sh: line 1: .../clang++: No such file or directory
Fix: Recreate the symlinks to point to the actual binaries in the same directory:
NDK_BIN="$HOME/Unity/Hub/Editor/6000.3.11f1/Editor/Data/PlaybackEngines/AndroidPlayer/NDK/toolchains/llvm/prebuilt/linux-x86_64/bin"
cd "$NDK_BIN"
ln -sf clang-18 clang
ln -sf clang clang++
ln -sf lld ld.lld
ln -sf lld lld-link
ln -sf lld ld64.lld
ln -sf lld wasm-ld
ln -sf ld.lld ld
ln -sf llvm-ar llvm-lib
ln -sf llvm-ar llvm-dlltool
ln -sf llvm-ar llvm-ranlib
ln -sf llvm-symbolizer llvm-addr2line
ln -sf llvm-objcopy llvm-strip
ln -sf llvm-readobj llvm-readelf
ln -sf llvm-rc llvm-windresIf the APK starts but shows only a black window (not fullscreen VR), the Meta Quest Support feature is not enabled for Android builds. This feature is critical — it sets up the Quest's OpenXR loader initialization and Android manifest entries.
Go to: Edit → Project Settings → XR Plug-in Management → OpenXR (Android tab) → Enable Meta Quest Support.
If you use UFW (Uncomplicated Firewall) on Linux, ALVR's automatic firewall configuration may create rules with multiport entries that don't work correctly. This causes the Quest and ALVR to fail to discover each other.
Symptom: Quest and PC ALVR can't connect despite being on the same network.
Fix: Either add the ALVR ports manually as individual rules, or restart your PC — the reboot clears the problematic multiport rules and ALVR will re-create them correctly on next launch.
├── launch_unity_vr.sh # Main launch script
├── NativeFix/
│ ├── build_compat.sh # Builds bionic-to-glibc shims
│ ├── compat_libc.c # libc shim (stdio, string, pthread, etc.)
│ ├── compat_libm.c # libm shim (math functions)
│ ├── compat_libdl.c # libdl shim (dlopen, dlsym, etc.)
│ ├── version_libc.map # Linker version script for LIBC tag
│ ├── version_libm.map # Linker version script for LIBC tag
│ ├── version_libdl.map # Linker version script for LIBC tag
│ ├── fake_jvm.c # LD_PRELOAD JNI/JavaVM stub
│ ├── openxr_layer/
│ │ ├── strip_android_layer.c # OpenXR API layer source
│ │ └── build_layer.sh # Build + install layer manifest
│ └── vulkan_layer/
│ ├── force_timeline_sem.c # Vulkan implicit layer source
│ └── build_vk_layer.sh # Build + install layer manifest
├── Packages/
│ └── com.unity.xr.openxr/
│ └── Runtime/OpenXRLoader.cs # Patched: Linux loader path
├── Assets/
│ └── XR/Settings/
│ ├── OpenXRPackageSettings.asset # Oculus Touch profile enabled
│ └── OpenXR Editor Settings.asset # Vulkan queue settings
└── Documentation/
├── TECHNICAL.md # Detailed technical documentation
└── AI_GUIDE.md # Guide for AI agents
| Symptom | Likely Cause | Fix |
|---|---|---|
| Unity crash | Delete those files if they are exist: NativeFix/openxr_layer/libXrApiLayer_strip_android.so, NativeFix/vulkan_layer/libVkLayer_force_timeline_sem.so, NativeFix/libfake_jvm.so | It will generate them automatically |
| Unity crash (step 1 doesn't help) | Use AI and Cursor, claude 4.6 opus can fix this. Point it to AI documentation. | Cursor can read logs and most likely fix the issue |
DllNotFoundException: .../libm.so.6: version 'LIBC' not found |
Compat shims not built or not on LD_LIBRARY_PATH |
Run via launch_unity_vr.sh; it builds them automatically |
Failed to load openxr runtime loader |
Unity trying to load Windows/UWP OpenXR loader | Patch OpenXRLoader.cs (Step 3 above) |
xrCreateInstance fails |
Android extension not stripped | Ensure STRIP_ANDROID_XR_LAYER=1 is set and layer is built |
SIGSEGV in libUnityOpenXR.so during Play |
Missing timeline semaphore or JVM | Ensure Vulkan layer + fake JVM shim are active |
| No image in headset but no crash | ALVR not connected, or wrong graphics API | Check ALVR connection; ensure -force-vulkan flag is used |
| Controllers don't work | Wrong interaction profile enabled | Enable "Oculus Touch Controller Profile" in OpenXR settings and check OculusTouchControllerProfile (mentioned in readme) |
| UI too small/large | HiDPI scaling | Adjust GDK_SCALE/GDK_DPI_SCALE in launch script |
APK build fails: clang++: No such file or directory |
Broken NDK symlinks on Linux | See NDK Broken Symlinks fix above |
| APK: black window on Quest, not fullscreen VR | Meta Quest Support feature disabled | Enable it in OpenXR settings for Android |
| ALVR can't find Quest on network | UFW multiport rule bug | Restart PC or add ALVR ports manually |
MIT License. See LICENSE for details.
Stridemann
Full disclosure: The author used Cursor IDE with Claude (claude-4.16-opus-high) to diagnose and build this entire compatibility layer. The process involved iterative debugging of native crashes, reverse-engineering the Android binary's dependencies, and building four separate native shims/layers. See Documentation/TECHNICAL.md for the full story.
This project is provided as-is with no support. It is a proof-of-concept demonstrating that Unity VR can work on Linux with Meta Quest. If it breaks, the technical documentation and AI guide should give you (or your AI assistant) enough context to debug it.