Xray-install puts Xray-core on a systemd host with one curl command and a known FHS layout
Easiest way to install & upgrade Xray.
At a glance
- What is it?
- Xray-install is a shell script that installs and upgrades Xray-core, places it under the Filesystem Hierarchy Standard paths, and wires up systemd units. It is small, GPL-3.0 licensed and updated often, and its main weakness is that the whole install is a curl pipe, which puts the trust boundary in the wrong place.
- Who is it for?
- Xray-install fits an operator who runs a systemd Linux host and wants Xray-core installed at predictable paths with unit files that a package manager did not write, and it fits people who need the geodata-only refresh or a proxy-aware install that the package managers do not offer. It does not fit Alpine or Gentoo, which need the separate OpenRC instructions, and it does not fit anyone who cannot accept a curl-piped script running as root on their server.
- Can I use it commercially?
- Yes, with conditions. GPL-3.0 is a copyleft licence: if you distribute software that includes it, you must release that software's source code under the same licence. Running it internally without distributing it does not trigger that obligation.
- Is it still maintained?
- Yes. The repository received new commits within the last day.
- What is it written in?
- Mainly Shell, 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
The install is a curl pipe, and the script supports subcommands
Xray-install is a bash script for installing Xray on operating systems such as CentOS, Debian and OpenSUSE that support systemd. The whole tool is essentially one file, install-release.sh, plus the documentation.
The invocation pattern is unusual and worth reading carefully. The script is fetched and handed to bash, and the argument after the @ symbol selects the subcommand:
bash -c "$(curl -L https://github.com/XTLS/Xray-install/raw/main/install-release.sh)" @ installThe same shape covers the other operations. Updating only the geodata files uses install-geodata, removing Xray while keeping the JSON and logs uses remove, and checking for available updates uses check. The full list is available with the help subcommand:
bash -c "$(curl -L https://github.com/XTLS/Xray-install/raw/main/install-release.sh)" @ helpWhat the script does with those subcommands is ordinary work. It fetches the Xray-core release archive for your architecture, drops the binary in /usr/local/bin, copies the JSON configuration files into /usr/local/etc/xray, installs the geodata files, creates a log directory, and writes systemd unit files. None of that is clever; the value is that it is repeatable and that the same command upgrades as well as installs.
One note in the documentation deserves attention: Xray will not log to /var/log/xray/*.log by default, and you have to configure the log setting to specify log files. An operator who creates the log directory and then sees no logs should read that line before debugging anything else.
Everything lands where the Filesystem Hierarchy Standard says it should
The project documents the exact layout it produces, and that documentation is the contract you should check after an install.
installed: /etc/systemd/system/xray.service
installed: /etc/systemd/system/[email protected]
installed: /usr/local/bin/xray
installed: /usr/local/etc/xray/*.json
installed: /usr/local/share/xray/geoip.dat
installed: /usr/local/share/xray/geosite.dat
installed: /var/log/xray/The binary in /usr/local/bin is the conventional place for a locally installed executable, and the configuration in /usr/local/etc/xray keeps the JSON files out of the system configuration tree, which is a deliberate choice that leaves /etc alone. The geodata files, geoip.dat and geosite.dat, go into /usr/local/share/xray where the binary can find them next to the other data it needs.
The two unit files, xray.service and [email protected], are the systemd entry points. The plain service runs one instance, the template service with the @ runs templated instances, and the documentation ties them to the multiple instance mechanism described later in this article.
The two files that matter most for correctness are the ones you edit after installing: the JSON configuration in /usr/local/etc/xray and the systemd units. The install script overwrites the units on upgrade unless you tell it not to, which is one of the reasons the --no-update-service option exists.
A local archive install avoids running fetched code as root
The project offers an alternative to the curl pipe, and it is the one to prefer on a machine you care about.
The documentation says to download the installation script and the Xray-core zip for your architecture, then run as root. For an x86_64 host the command is:
bash install-release.sh install --local ./Xray-linux-64.zipThe difference is where the bytes come from. The curl form fetches the script and executes it in one step, so a compromised or replaced file at that URL runs as root on your host with no review. The local form lets you download the archive, check the file, and then run a script you have already read.
The same reasoning applies to the rest of the tool, and the repository structure supports it: the top level contains install-release.sh, the README files, the LICENSE and the alpinelinux/ directory. There is no package manager metadata, no CI configuration for publishing packages, and no dependency lockfile, because there is nothing to depend on beyond a shell and curl. The cost of that simplicity is that the trust is entirely in the script you downloaded.
The other advanced options follow the same pattern of adding one flag. A pre-release version of Xray-core is installed with the --beta flag, geodata can be skipped with --without-geodata, log rotation can be configured with --logrotate, and a specific version can be pinned with --version. The full table also lists --force, --reinstall, --no-update-service and --without-logfiles, which skips creating the log directory entirely.
Installing through a proxy, and the logrotate files it adds
Two features exist for operators whose network or infrastructure requires them.
The first is proxy-aware installation, for a host that cannot reach GitHub directly:
bash -c "$(curl -x http://127.0.0.1:8118 -L https://github.com/XTLS/Xray-install/raw/main/install-release.sh)" @ install --proxy http://127.0.0.1:8118The --proxy flag applies to the download of the script and to the downloads the script itself performs, and the documentation states that it supports HTTP and SOCKS proxies. The same flag works with the install-geodata and check subcommands, so a host behind a proxy can still refresh its geodata and check for updates without a direct route.
The second is log rotation, enabled with the --logrotate flag, where $time can be given in the format 12:34:56. Enabling it adds three more files to the system:
installed: /etc/systemd/system/[email protected]
installed: /etc/systemd/system/[email protected]
installed: /etc/logrotate.d/xrayA systemd timer and a logrotate configuration, both named after Xray. This matters because of the earlier point about logging: Xray does not write to /var/log/xray by default, so if you have configured a log file and want it rotated, --logrotate is the switch that sets this up. Adding a timer unit for a daemon the script installed by hand is a small thing, but it is the difference between a hand-managed binary and something closer to a packaged service.
JSONS_PATH gives you templated instances from the same binary
Running more than one instance on a host is handled through an environment variable and the systemd template unit.
Set JSONS_PATH when installing, and the script will use that directory for configuration:
JSONS_PATH=/usr/local/etc/xray bash install-release.sh installThe split is simple and precise. The plain xray.service reads the JSONS_PATH directory, and the templated [email protected] reads JSONS_PATH/a/. Each instance's configuration files live in its own directory, and you start one with systemctl enable --now xray@a.
This is the same layout pattern that other templated systemd services use, and it means adding a second instance is a matter of creating a directory under the path and starting another template unit. It also means the configuration directory is not fixed at install time, which is the part that matters for an existing host: an operator who already keeps Xray JSON somewhere else can point the script at it rather than having the install create a second, competing copy.
The variable is read at service start, so the unit files and the directory have to agree. A mismatch produces a service that starts and then does not find its configuration, and the failure is visible in the service status rather than at install time.
GPL-3.0, no releases, and a script that changes often
The licence is GPL-3.0, with a LICENSE file at the repository root. For a shell script that you run on your own server rather than redistribute, the practical obligation is limited, but anyone who modifies install-release.sh and passes it on is working inside copyleft terms, and the same applies to a modified script shipped to users of your own product.
On maintenance, the repository is not archived and the last push was on 2026-10-19, so the script is being changed regularly. There are no GitHub releases, which fits the design: the curl URL points at the main branch of install-release.sh, so running the documented command always fetches the current version of the script rather than a pinned tag. That is convenient for staying current and it is the reason a lock on the script version is impossible unless you use the local archive form.
The three most recent releases belong to Xray-core rather than to this repository, and the project topics name reality, vless, xhttp, xray and xtls, so the transport configuration itself lives in the JSON files that the script installs rather than in the script. The script does not configure your proxy. It places the binary, the sample JSON and the geodata, registers the services, and leaves the protocol setup to the config file.
The alpine/ directory at the repository root holds the OpenRC-specific instructions for Alpine and Gentoo, since those systems do not use systemd and the unit files here would be useless on them.
Editorial conclusion
Xray-install fits an operator who runs a systemd Linux host and wants Xray-core installed at predictable paths with unit files that a package manager did not write, and it fits people who need the geodata-only refresh or a proxy-aware install that the package managers do not offer. It does not fit Alpine or Gentoo, which need the separate OpenRC instructions, and it does not fit anyone who cannot accept a curl-piped script running as root on their server. Before running it, download install-release.sh and read it, use the local archive form with install-release.sh install --local ./Xray-linux-64.zip so you are not executing code fetched over the network at install time, and check whether the JSONS_PATH layout matches the config directory you already use.
Frequently asked questions
How do I install Xray with Xray-install?
Run `bash -c "$(curl -L https://github.com/XTLS/Xray-install/raw/main/install-release.sh)" @ install` on a systemd-based host such as CentOS, Debian or OpenSUSE. The script installs Xray-core into /usr/local/bin, the JSON configuration into /usr/local/etc/xray, the geodata files into /usr/local/share/xray, and the unit files into /etc/systemd/system.
How do I update the geoip.dat and geosite.dat files only?
Use the install-geodata subcommand: `bash -c "$(curl -L https://github.com/XTLS/Xray-install/raw/main/install-release.sh)" @ install-geodata`. The same script also supports a check subcommand to look for available updates.
Does Xray-install work on Alpine or Gentoo?
Not with the main script. Those systems use the OpenRC init system rather than systemd, and the repository points Alpine and Gentoo users to the OpenRC specific instructions in the alpinelinux/ directory, which contain installation scripts and guides for those systems.
How do I run several Xray instances on one host?
Set JSONS_PATH at install time, for example `JSONS_PATH=/usr/local/etc/xray bash install-release.sh install`. The xray.service unit reads that directory, and the templated [email protected] reads the a/ subdirectory, with each instance started using systemctl enable --now xray@a.
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/xtls-xray-install)Was this useful?