If you run XCP-ng in your homelab, you’ve probably already heard of Xen Orchestra. It’s the web-based management interface for your XCP-ng hosts — think VM consoles, backup scheduling, storage management, live migration, and more, all in a browser. The only catch: the fully-featured version requires a paid subscription from Vates.
The good news is that Xen Orchestra is open source, and you can build it from source yourself for free. The not-so-good news is that the build process involves Node.js, Yarn, a handful of config files, and enough steps to make it easy to get wrong — especially when you want to keep it updated over time.
That’s exactly why I built install_xen_orchestra — a Bash script that handles the full lifecycle: fresh install, updates, backups, restores, rebuilds, and even XO Proxy deployment, all from a single script.
What It Does
At its core, the script automates what the official Xen Orchestra source installation docs walk you through manually. But it goes well beyond a one-time install — it’s designed to be the tool you run every time you need to touch your XO instance.
| Function | CLI Flag | Description |
|---|---|---|
| Install | --install |
Fresh install of Xen Orchestra from source |
| Update | --update |
Pull latest source, rebuild, and restart (with backup) |
| Restore | --restore |
Restore from a previous backup |
| Rebuild | --rebuild |
Fresh clone + clean build, preserves your settings |
| Reconfigure | --reconfigure |
Apply config changes without a full rebuild |
| XO Proxy | --proxy |
Deploy XO Proxy to a Xen pool master |
Running the script with no arguments drops you into an interactive menu instead.
The Interactive Menu
Like my other tools, this one has a keyboard-navigable menu for those who prefer not to memorize flags. It also shows you at a glance whether your local script and XO installation are up to date vs. the upstream master branch:
╔══════════════════════════════════════════════════════════════════════════════════╗
║ Install Xen Orchestra from Sources Setup and Update ║
╚══════════════════════════════════════════════════════════════════════════════════╝
Current Script Commit : 693f4 (Branch: main)
Master Script Commit : 693f4 (Branch: main)
Current XO Commit : a1b2c (Branch: master)
Master XO Commit : d4e5f (Branch: master)
Current Node : v24.15.0
────────────────────────────────────────────────────────────────────────────────
▸ [✓] Install Xen Orchestra [ ] Reconfigure Xen Orchestra
[ ] Update Xen Orchestra [ ] Rebuild Xen Orchestra
[ ] Rename Sample-xo-config.cfg [ ] Edit xo-config.cfg
[ ] Install XO Proxy
────────────────────────────────────────────────────────────────────────────────
Selected: 1
↑↓←→ Navigate SPACE Select/Deselect ENTER Confirm Q Quit
Use arrow keys to navigate, Space to select, and Enter to run. You can queue multiple actions at once.
Quick Start
bash
git clone https://github.com/acebmxer/install_xen_orchestra.git
cd install_xen_orchestra
cp sample-xo-config.cfg xo-config.cfg
nano xo-config.cfg # configure to your liking
./install-xen-orchestra.sh
Do not run with
sudo. The script handles privilege escalation internally — run it as a normal user with sudo access.
If xo-config.cfg doesn’t exist when the script runs, it’s automatically created from the sample config — so the first-time setup is forgiving.
Configuration
Everything is controlled through xo-config.cfg. Here are the key options:
| Setting | Default | Description |
|---|---|---|
HTTP_PORT |
80 | HTTP port XO listens on |
HTTPS_PORT |
443 | HTTPS port |
INSTALL_DIR |
/opt/xen-orchestra | Where XO is installed |
GIT_BRANCH |
master | Git branch or tag to build from |
NODE_VERSION |
24.15.0 | Node.js version to use |
SERVICE_USER |
xo-service | User the XO service runs as |
BACKUP_KEEP |
5 | Number of backups to retain |
BIND_ADDRESS |
0.0.0.0 | Network bind address |
REVERSE_PROXY_TRUST |
false | Trust X-Forwarded headers (for reverse proxy setups) |
One thing worth noting on BACKUP_KEEP: the rotation logic only applies to backups created by the current version of the script. If you’re upgrading from an older version, old backups with a different naming convention won’t be pruned automatically — worth a manual check of your backup directory after upgrading.
Smart Update Safety — Running Task Detection
This is one of my favorite features. Before applying an update, the script queries the Xen Orchestra REST API to check for any active tasks — things like running backups or VM exports. If any are found, the update is aborted automatically to prevent data loss or mid-operation corruption.
Authentication for the task check supports three methods, resolved in priority order:
| Priority | Method |
|---|---|
| 1 | Auth token (XO_TASK_CHECK_TOKEN in config) |
| 2 | Credentials (XO_TASK_CHECK_USER / XO_TASK_CHECK_PASS in config) |
| 3 | Interactive prompt at runtime |
The recommended approach is to create a dedicated admin account in the XO web UI (e.g. task-checker@local.net) and generate an auth token for it. Tokens can be revoked independently and are more secure than storing a password in a config file. Here’s how to generate one:
bash
curl -X POST -u 'task-checker@local.net:yourpassword' \
https://localhost/rest/v0/users/me/authentication_tokens -k
Copy the id from the response and drop it into your config:
XO_TASK_CHECK_TOKEN=UlTBEnFeL12XocK-7Qx-DKvOYbPn0eG7Z2oMvOniNjg
If you’d rather not deal with tokens, plain credentials work too — and if neither is configured, the script will just prompt you interactively each time you update.
Backup and Restore
Every update and rebuild automatically takes a backup first. The BACKUP_KEEP setting controls how many are retained (default: 5). If something goes sideways after an update, restoring is a single command:
bash
./install-xen-orchestra.sh --restore
The restore function presents a list of available backups to choose from — no digging through directories manually.
XO Proxy Support
The script also handles XO Proxy deployment (--proxy), which is needed when you have Xen pools that aren’t directly reachable from your XO server — common in multi-site homelab setups or when pools are on isolated VLANs.
Supported Distributions
The script works across all major server distributions:
- Debian 10, 11, 12, 13
- Ubuntu (all supported releases)
- RHEL / CentOS / AlmaLinux / Rocky Linux
- Fedora
A Few Gotchas Worth Knowing
Low RAM hosts: The Yarn build is memory-intensive. On hosts with less than 2 GB RAM, the OOM killer can take out the Node process mid-build. If that happens, add a swapfile before retrying:
bash
sudo fallocate -l 2G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
Air-gapped hosts: If your server can’t reach the NodeSource repo, set NODE_VERSION to a specific patch version (e.g. 24.15.0) in the config — the script will download a pre-built binary from nodejs.org instead.
SELinux on RHEL/Rocky/Alma: If xo-server fails to bind ports, check for AVC denials with ausearch -m avc -ts recent | grep xo-server and generate a local policy with audit2allow if needed.
Git “dubious ownership” errors: This happens when the install directory is owned by a different user than the one running the script. Fix it with:
bash
sudo chown -R xo-service:xo-service /opt/xen-orchestra
Getting Started
If you’re running XCP-ng and want a fully-featured Xen Orchestra instance without the subscription, this script gets you there without having to piece together the process from the docs every time.
bash
git clone https://github.com/acebmxer/install_xen_orchestra.git
cd install_xen_orchestra
./install-xen-orchestra.sh
The full source is on GitHub. If you run into an issue on your distro or have a feature request, feel free to open an issue.