Xw
Developer Tools Azure

xyOps Workflow Automation on Ubuntu 24.04 on Azure User Guide

| Product: xyOps Workflow Automation on Ubuntu 24.04 LTS on Azure

Overview

xyOps is an open source workflow automation and server monitoring platform. You define events that say what to run, where to run it and when; the scheduler fires them on a timetable, on an interval, once, or on demand; and every launch becomes a job with a live streaming log, a progress meter, captured output and full history. Jobs chain into other jobs or into visual workflows with branching, fan out and fan in, while the same platform collects CPU, memory, disk and network metrics from every registered server and raises alerts and tickets when a threshold is crossed.

This cloudimg image ships xyOps 1.0.88 as a complete single server appliance. The conductor (web console, scheduler, API and storage) runs as the dedicated unprivileged xyops system user on Node.js 22 LTS, and the bundled xySat satellite job runner is started by the conductor in the same VM, so this one machine can actually execute work with no second node to provision. The conductor is bound to 127.0.0.1:5522 and nginx on port 80 reverse proxies to it, including the WebSockets that carry live job logs and server metrics. A unique admin password and a unique session signing secret are generated on the first boot of every VM, and first boot proves the upstream default admin/admin login is rejected before it ever opens the public port. Backed by 24/7 cloudimg support.

What is included:

  • xyOps 1.0.88 (pinned upstream release, BSD-3-Clause) running as the xyops system service on Node.js 22 LTS
  • The bundled xySat 1.0.39 job runner (also BSD-3-Clause), started automatically by the conductor, so a single VM can run jobs out of the box
  • The stock embedded storage engine (SQLite document store plus a filesystem binary store) at /opt/xyops/data, so there is no external database to run
  • xyOps bound to 127.0.0.1:5522, fronted by nginx on port 80 including the live-log WebSocket
  • Port 80 serving an unauthenticated /healthz endpoint for load balancer probes, with every API route requiring authentication
  • A unique admin password generated on first boot and written to a root only file, so no shared or default admin ships in the image
  • A fresh per VM session and satellite signing secret generated on first boot, never a shared image baked secret
  • Stock plugins out of the box: Shell Script, HTTP Request, Docker Run, Fire Web Hook and a Test Plugin
  • /usr/local/sbin/xyops-selftest.sh, a self test that proves your instance can still create and run a job end to end
  • A production dependency licence audit generated at build time at /opt/xyops/CLOUDIMG-DEPENDENCY-LICENCES.txt
  • 24/7 cloudimg support

Security note before you start. xyOps runs shell commands on your servers on your behalf. That is what it is for, and it means an exposed, weakly protected instance is remote code execution. Keep this VM behind a restricted network security group or a VPN, never open port 80 to the whole internet, and put a TLS terminating proxy in front of it before production use. Step 3 and the Maintenance section below show how.

xyOps is developed by PixlCore LLC. cloudimg is not affiliated with, endorsed by or sponsored by PixlCore LLC; the product name is used only to identify the unmodified open source software packaged in this image. xyOps is the successor to the same author's earlier Cronicle scheduler.

Prerequisites

An active Azure subscription, an SSH key pair, and a VNet plus subnet in the target region. Standard_B2s (2 vCPU / 4 GiB RAM) is a reasonable starting point; size up if you plan to run many concurrent jobs or register a large fleet of servers. NSG inbound: allow 22/tcp from your management network and 80/tcp from your management network only (and 443/tcp if you add TLS). The web console is served over plain HTTP by default, so for production put your own domain and a trusted certificate in front of it (see Maintenance).

Step 1 - Deploy from the Azure Marketplace

Sign in to the Azure Portal, choose Create a resource, search the Marketplace for xyOps by cloudimg, and select Create. On Basics pick your subscription, resource group, region and size; under Administrator account choose SSH public key and paste your key; under Inbound port rules allow SSH (22) only, then add a scoped HTTP rule after deployment as shown in Step 3. Review and create.

Step 2 - Deploy from the Azure CLI

az vm create \
  --resource-group <your-rg> \
  --name xyops \
  --image <marketplace-image-urn> \
  --size Standard_B2s \
  --admin-username azureuser \
  --ssh-key-values ~/.ssh/id_ed25519.pub \
  --vnet-name <your-vnet> --subnet <your-subnet> \
  --public-ip-sku Standard

Step 3 - Restrict who can reach the console, then connect

Because xyOps executes commands for you, open port 80 to your own network rather than to the internet. Replace <your-office-cidr> with the address range you will administer from:

az network nsg rule create \
  --resource-group <your-rg> \
  --nsg-name <your-nsg> \
  --name allow-xyops-web \
  --priority 1010 \
  --source-address-prefixes <your-office-cidr> \
  --destination-port-ranges 80 \
  --access Allow --protocol Tcp --direction Inbound

Then connect over SSH:

ssh azureuser@<vm-public-ip>

Step 4 - Confirm the services are running

systemctl is-active xyops.service nginx.service
ss -tlnp | grep -E ':5522|:80 '
/opt/xyops/bin/control.sh version

Both services report active, and the version prints 1.0.88. Note where each process listens: xyOps itself is bound to 127.0.0.1:5522 and is never exposed directly on a public interface, while nginx listens on port 80 and reverse proxies to it.

xyops.service and nginx.service reporting active, with the xyOps conductor bound to loopback 127.0.0.1:5522 behind nginx on port 80, and xyOps version 1.0.88

Step 5 - Retrieve your admin password

The xyOps admin password is generated uniquely on the first boot of your VM and written to a root only file:

sudo stat -c '%a %U:%G  %n' /root/xyops-credentials.txt
sudo cat /root/xyops-credentials.txt

The file contains XYOPS_URL, XYOPS_USERNAME (admin) and XYOPS_PASSWORD. It is mode 0600 owned by root, so only an administrator can read it. Store the password somewhere safe.

The upstream project ships a documented default admin/admin login. It does not exist on your VM: first boot mints a 28 character password, proves the default is rejected, and only then opens port 80. You can confirm that yourself:

curl -s -H 'Content-Type: application/json' \
  -d '{"username":"admin","password":"admin"}' \
  http://localhost/api/user/login

The response is {"code":"login","description":"Username or password incorrect."}. xyOps always answers this endpoint with HTTP 200 and reports the outcome in the JSON body's code field, so read the body rather than the status code.

The root only 0600 credentials file with the per VM password redacted, and the upstream default admin/admin login being rejected by the running instance

Step 6 - Confirm the health endpoint and prove a real job runs

nginx serves an unauthenticated health endpoint for load balancers and probes, and the image ships a self test that does not just check that a page loads: it signs in with your per VM password, confirms the default and a wrong password are both rejected, confirms the local job runner is registered, then creates an event, runs it, and checks the captured output.

curl -s -o /dev/null -w 'GET /healthz  HTTP %{http_code}\n' http://localhost/healthz
curl -s -o /dev/null -w 'GET /         HTTP %{http_code}\n' http://localhost/
sudo /usr/local/sbin/xyops-selftest.sh

Both requests return HTTP 200, and the self test prints a line beginning OK with the job id it ran, how long it took and how many bytes of output it captured. It cleans up the event it created, and it repairs xyOps' failed login counters afterwards so it is safe to run as often as you like.

The unauthenticated health endpoint returning HTTP 200 and the xyOps self test reporting OK after creating and running a real job

Step 7 - Sign in to the web console

Browse to http://<vm-public-ip>/. You land on the xyOps User Login page. Enter admin as the username and the password from Step 5, then select Login.

The xyOps user login page served through nginx on port 80

Step 8 - Explore the dashboard

After signing in you land on the Dashboard. It shows the size of your cluster (conductors and servers), current alerts, active jobs, today's job counts and success rate, and resource usage. A fresh appliance shows 1 conductor and 1 server: the conductor is this VM, and the server is the bundled xySat job runner that lets a single VM execute work.

The xyOps dashboard after signing in, showing one conductor, one registered server, job counts and job success rate

Step 9 - Create a scheduled event

Select Events in the sidebar, then New Event.... Give the event a title, choose a Category (the image ships a ready made General category), a Target (the Main Group covers this VM's own runner) and a Plugin - the image ships Shell Script, HTTP Request, Docker Run, Fire Web Hook and a Test Plugin. Paste your script, add a Trigger (a schedule, an interval, a single shot or a manual run), then save.

The screenshot below shows two real events in the list: a Nightly log rotation event on a 03:30 daily schedule, and an Hourly disk-space check on the hour. The Status column shows the result of each event's most recent job, and the Run, Edit and History actions are on every row.

The xyOps event list showing two scheduled events in the General category, one of them reporting a Success status, with Run, Edit and History actions

Step 10 - Run a job and read its captured output

Select Run next to an event to launch it immediately. While it runs, xyOps streams progress, CPU and memory usage and the live log over a WebSocket; when it finishes, the job moves to All Completed with its full record retained.

Open the job to see everything xyOps captured: the success banner, the job summary (which event, which plugin, which server, when it started and finished, how long it took, average CPU and memory) and the Job Output block holding exactly what your script printed.

A completed xyOps job showing the job completed successfully banner, the full job summary, and the Job Output block containing the real output the shell script printed

You can do the same thing from the API, which is useful for wiring xyOps into your own tooling. Substitute the value of XYOPS_PASSWORD from Step 5 for <XYOPS_PASSWORD>; because the command embeds your unique password, run it interactively rather than from a script. Note that authenticated POST calls need both the session id from the login cookie and the csrf_token from the login body:

LOGIN=$(mktemp) ; HDR=$(mktemp)
curl -s -D "$HDR" -o "$LOGIN" -H 'Content-Type: application/json' \
  -d '{"username":"admin","password":"<XYOPS_PASSWORD>"}' \
  http://localhost/api/user/login
SID=$(grep -i '^set-cookie' "$HDR" | sed -E 's/.*session_id=([^;]+).*/\1/' | tr -d '\r' | head -1)
CSRF=$(python3 -c 'import json,sys
try: print(json.load(open(sys.argv[1])).get("csrf_token",""))
except Exception: print("")' "$LOGIN")
curl -s -H 'Content-Type: application/json' -H "X-Session-ID: $SID" -H "X-CSRF-Token: $CSRF" \
  "http://localhost/api/app/get_active_servers/v1" \
  | python3 -c 'import json,sys
try: d=json.loads(sys.stdin.read() or "{}")
except Exception: d={}
print("registered job runners:", len(d.get("rows") or []))'
rm -f "$LOGIN" "$HDR"

This prints registered job runners: 1 on a single VM appliance. The same session works for create_event, run_event and the rest of the REST API; see the xyOps API reference for the full list.

Step 11 - Confirm the licences shipped with your image

Every image carries its upstream licences and a production dependency licence audit generated when the image was built:

head -3 /opt/xyops/LICENSE.md
head -5 /opt/xyops/CLOUDIMG-DEPENDENCY-LICENCES.txt

xyOps and the bundled xySat runner are both BSD-3-Clause. The audit lists every package in the production dependency tree with its licence, resolving the licence from the package's own LICENSE file where its package.json omits the field.

The upstream BSD 3-Clause licences for xyOps and the bundled xySat runner, and the build time production dependency licence audit summarising 245 packages

Maintenance

  • Where your data lives: the embedded storage (events, workflows, jobs and their output, users, servers, alerts and tickets) is under /opt/xyops/data, logs are under /opt/xyops/logs, and configuration is /opt/xyops/conf/config.json. Check size with du -sh /opt/xyops/data.
  • Backups: back up the storage directory while the service is stopped, to a root only location. Do not copy /opt/xyops/conf/ or /root/xyops-credentials.txt into a backup archive you move off box without protecting them: the config holds this VM's session signing secret and the credentials file holds your admin password. bash sudo systemctl stop xyops.service sudo install -d -o root -g root -m 0700 /var/backups/xyops sudo tar czf /var/backups/xyops/xyops-data-$(date +%F).tar.gz -C /opt/xyops data sudo chmod 0600 /var/backups/xyops/xyops-data-*.tar.gz sudo systemctl start xyops.service
  • Rotate the admin password: stop the service, write a new one, and start it again. bash sudo systemctl stop xyops.service sudo runuser -u xyops -- /usr/bin/node /opt/xyops/bin/storage-cli.js admin admin '<new-password>' sudo systemctl start xyops.service Use runuser rather than sudo -u so the new password is not written into the systemd journal.
  • Locked out after too many bad logins? xyOps locks an account after five failed logins in an hour. Rotating the password with the command above clears the lock.
  • Serve a real domain and TLS: point a DNS name at the VM, obtain a trusted certificate (for example with certbot), and front xyOps with nginx TLS on port 443. Then set base_app_url in /opt/xyops/conf/config.json to your https:// URL so the links xyOps generates in emails, tickets and web hooks are correct, and restart the service.
  • Add worker servers: this appliance runs jobs on itself out of the box. To spread work across more machines, open Servers, select Add Server..., and run the generated one line installer on the target host; it installs the xySat agent and joins it to this conductor. Make sure the workers can reach this VM by hostname.
  • Users and access control: add teammates and set per privilege access from User Accounts and User Roles under ADMIN. The admin privilege grants everything; prefer a role with only the privileges each person needs.
  • Upgrades: sudo -u xyops /opt/xyops/bin/control.sh upgrade upgrades in place and preserves your data, configuration and users. Take a backup first.
  • Security patches: unattended-upgrades remains enabled so the OS continues to receive security updates automatically.

Support

cloudimg provides 24/7 expert support for this image. Contact support@cloudimg.co.uk.