Xray for Android is a thin GUI over the Xray-core binary
Xray GUI Client For Android
At a glance
- What is it?
- SaeedDev94/Xray is a simple Android GUI client for the XTLS/Xray-core proxy, built in Kotlin, with the core vendored as a submodule and a separate helper module built alongside it. The repository ships four architecture specific APKs, a Docker based build and an F-Droid listing, and the README is honest about how little it documents.
- Who is it for?
- SaeedDev94/Xray fits an Android user who already runs Xray-core and wants the service lifecycle, configuration import and status handled by an app instead of a shell, with prebuilt APKs for common architectures. It does not fit a user who wants a guided setup, because the README documents none of the routing modes, the permission flow or the configuration format.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 2 days ago.
- What is it written in?
- Mainly Kotlin, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
A front end for a core you already know
SaeedDev94/Xray is described in one sentence as a simple GUI client for XTLS/Xray-core. That framing is the most accurate summary of the project available, because everything else in the repository is the machinery for shipping that core to an Android device.
The app is Kotlin, and the screenshots in the README name the activities: a main screen, an assets screen, and a settings screen with a basic tab and an advanced tab. That is a small surface, and it is the right shape for this kind of client, where the configuration is the real content and the interface is mostly a container for it.
The audience is narrow and specific. It is for a person who has an Xray-core configuration and a server to connect to, and who wants the Android side handled for them, rather than someone who wants a consumer VPN app with a list of friendly server names. A GUI client removes the packaging problem, and it does not remove the need to understand what the core does with your traffic.
The repository topics, tproxy, tun2socks and xray-core, describe the routing mechanisms at play. On Android, capturing traffic from other applications requires either a transparent proxy in the TPROXY mode or a tun device tunnelled into a userspace stack, and both of those need privileges that a normal application does not have, which is why an Android client is a more involved program than it looks. The README does not explain which of these the app implements or how it asks for the permission it needs, and that silence is the first thing to check before you install it.
Four architectures, four version codes
The APK variants guide in the README is short and explains itself.
- arm32 => versionCode + 1
- arm64 => versionCode + 2
- x86 => versionCode + 3
- amd64 => versionCode + 4So a single release is published as four APKs whose version codes are offset from the base one according to the CPU architecture. The reason is the one that bites people who sideload: an Android device refuses to install an APK over another one with the same version code but a different signature or a different architecture, and package managers such as F-Droid and the Play Store need a way to tell the four files apart. Offsetting the version code gives each variant its own identity while keeping the human readable version number the same.
The practical effect for a user is that the architecture matters. A 64 bit phone wants the arm64 build, an emulator may want x86 or amd64, and an older 32 bit device wants arm32. Installing the wrong one produces an error that says nothing about CPU architecture, which is one of those problems that costs an afternoon unless you know what the offset table is for.
It also tells you something about the build system. Producing four signed artefacts from one version requires the build to know the target architecture at packaging time, which is what the Gradle configuration and the release workflow in the repository are for.
The core is a submodule, the helper is a separate build
The repository layout shows the architecture of the build rather than of the app.
There is an XrayCore directory and a .gitmodules file, which together mean the Xray-core sources are pulled in as a git submodule rather than copied in. That is the right choice for a project that has to follow an upstream core: the upstream history stays separate, and a bump of the core is a submodule pointer move, visible in the diff and reversible. The XrayHelper directory sits alongside it as a second component, and the build scripts at the root make the division explicit, with build.sh as the entry point and separate scripts for building the Go parts, the core and the helper. That name is the evidence that the core and the helper are Go programs built with a Go toolchain, while the app itself is a Gradle project. The root also has build.gradle.kts, settings.gradle.kts, gradle.properties, the gradlew wrapper and its Windows counterpart, and updateGradleWrapper.sh for keeping the wrapper current, which is the usual convention for a project that expects contributors to build without a system Gradle install.
The app/ directory holds the Android application module, and metadata/ holds the store assets, including the en-US feature graphic and the phone screenshots referenced from the README. Those two directories are the difference between a project and a publishable one: the metadata is what a store listing needs, and it is generated or maintained alongside the code rather than improvised at release time.
The container build is a Debian image running one script
The Dockerfile at the root is the whole containerised build, and it is shorter than most people expect.
FROM debian:trixie
ENV LANG=C.UTF-8 \
DEBIAN_FRONTEND=noninteractive
COPY build.sh /build.sh
ENTRYPOINT ["/build.sh"]The base image is Debian trixie, the locale is set to a UTF-8 C locale and the frontend is set to noninteractive so that package installs in the script do not stop to ask questions. The build script is copied in as the entrypoint, which means the container does exactly one thing and exits, with no server, no volume and no state.
That design is what makes the build reproducible. A container that runs a single script has no ambient state, so a build that works today works the same way tomorrow, and the base image tag is the only thing that decides the environment. The cost is that the script has to install everything it needs, including the Go toolchain for the core and the helper, so the script is doing more work than its name suggests.
For a contributor, this is the path that avoids the usual local setup problems, since the requirements live in the script rather than in a wiki page of prerequisites. For an operator it means nothing at all, since the published APKs are what reaches users.
Two distribution channels and a release workflow
The download section of the README points at two places: the GitHub releases page of the project, and a F-Droid listing under the package name io.github.saeeddev94.xray. The F-Droid identifier follows the pattern of a package published from a GitHub hosted source, which is how F-Droid builds and signs its own copy rather than redistributing the author's file.
That distinction has a practical consequence. The GitHub release is built by the project's own release workflow, whose badge is shown in the README, and the F-Droid build is assembled by F-Droid's own infrastructure from the same sources. The two artifacts are built separately, so they can differ in signing key, in build environment and in the moment they were produced. It is worth comparing the version you get from each, especially on a device where you will later switch between them, because the Android package manager treats a differently signed package with the same package name as a different application.
The release cadence visible in the tags is fast. The recent list shows v12.9.0, v12.9.2 and v12.9.3 all released on 2026-10-18, with the last push to the repository the same day, and the repository is not archived. A version twelve line for a client of a mature core means the version number tracks the client, not the core.
Fast releases cut both ways. You get fixes quickly, and you also get a moving target, so the sensible habit is to install a specific version and read the release notes before you upgrade, especially when a release happens on the same day as three others.
What the README does not tell you
The documentation is thin, and the gaps are specific enough to name.
There is no explanation of how to import a configuration, no description of the assets screen, no statement of which routing modes the app supports, and no guidance on the Android permission flow that a traffic intercepting application needs. The screenshots show a settings screen with basic and advanced tabs, which implies a good deal of configuration surface, but the README does not enumerate it. There is no privacy section either, which for an application that handles network traffic is the section a reader will look for first.
None of this is a reason to skip the project, but it changes what you have to do to adopt it. The F-Droid listing and the store metadata are where the description of features usually lives, the upstream Xray-core documentation is where the configuration format is explained, and the app's own settings screen is where you find out what the current version actually exposes. Reading the core's documentation is not optional here, because the client is a shell around it.
The licence is MIT, with a LICENSE file at the root, which is the permissive end and appropriate for a client that people are expected to build and modify. The presence of the Dockerfile, the wrapper update script and the store metadata suggests a project that has settled into a repeatable release routine.
Where a GUI client is the wrong approach
The main limitation is that this project removes packaging work and nothing else. Xray-core is a proxy core, and the hard parts of running one are choosing a protocol, managing a server, keeping the configuration current and understanding what the routing modes do to your traffic. A GUI for the core does not answer any of those, and for some users it adds a layer: a native interface on top of a text configuration format that you still need to read.
The second limitation is the transparency of the wrapper. When an app is a front end for a binary, the bugs people report are usually the core's behaviour, and the useful debugging evidence is the core's own documentation and logs rather than the client's source. The client repository here is small, and the submodule arrangement means the interesting code lives elsewhere.
The third is the platform. This is an Android application, and the routing mechanisms it relies on are constrained by what Android allows a normal application to do, which is why the topics name TPROXY and tun2socks rather than anything higher level. If your goal is to route the traffic of a whole network rather than one phone, a client on one device is the wrong shape for the problem, and the same core configured on the gateway itself is the alternative.
That last point is the real alternative to weigh: running the core on a router or a small server, configured once in text, with the Android device acting as an ordinary client on the network. The difference in approach is ownership. A phone-side GUI is convenient, updates from the store and keeps everything on the device, while a core on the gateway covers every device on the network, is configured in a file you can read and diff, and keeps the routing decision in one place.
Editorial conclusion
SaeedDev94/Xray fits an Android user who already runs Xray-core and wants the service lifecycle, configuration import and status handled by an app instead of a shell, with prebuilt APKs for common architectures. It does not fit a user who wants a guided setup, because the README documents none of the routing modes, the permission flow or the configuration format. Before you install it, check the release notes for the version you pick, since v12.9.0, v12.9.2 and v12.9.3 all shipped on 2026-10-18, and check the F-Droid listing against the GitHub build. The last push was on 2026-10-18 and the licence is MIT.
Frequently asked questions
What is the SaeedDev94 Xray app?
It is a simple Android GUI client for XTLS/Xray-core, written in Kotlin, with a main screen, an assets screen and a settings screen with basic and advanced tabs. The repository is licensed MIT and the project publishes releases on GitHub.
Which APK should I download for my Android phone?
The release page publishes four APKs per version, and the README maps arm32, arm64, x86 and amd64 to version codes offset by one to four from the base version code. A 64 bit phone needs the arm64 build, and an emulator usually needs x86 or amd64.
Is the Xray app available outside GitHub?
Yes. The README points at an F-Droid listing under the package name io.github.saeeddev94.xray, in addition to the GitHub releases page. The two are built separately, so the signing and build environment can differ between them.
How do I build the Xray Android app?
The repository has a Dockerfile based on debian:trixie that copies build.sh in and runs it as the entrypoint, so a container build needs no local Go or Gradle setup. The root also has buildGo.sh, buildXrayCore.sh and buildXrayHelper.sh, with the core vendored as a git submodule and the Android app built through the Gradle wrapper.
Is the Xray Android client actively maintained?
The repository is not archived and the last push was on 2026-10-18, the same day as the v12.9.0, v12.9.2 and v12.9.3 releases. That cadence is fast enough that you should read the release notes before upgrading and consider pinning a specific version.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/saeeddev94-xray)Was this useful?