xyOps
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. 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 SecureAnyCloud 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. The conductor starts the bundled xySat satellite job runner in the same VM, so this one machine can 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. First boot generates a unique admin password and a unique session signing secret for every VM. It also proves the upstream default admin/admin login is rejected before it opens the public port.
What you get:
- xyOps 1.0.88 (pinned upstream release, BSD-3-Clause) running as the
xyopssystem 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
/healthzendpoint for load balancer probes, with every API route requiring authentication - A unique
adminpassword 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 secret baked into the image
- 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 license audit generated at build time at
/opt/xyops/SAC-DEPENDENCY-LICENCES.txt - 24/7 SecureAnyCloud 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, and never open port 80 to the whole internet. Put a TLS-terminating proxy in front of it before running it in production. Step 3 and the Maintenance section below show how.
xyOps is developed by PixlCore LLC. SecureAnyCloud 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
You need 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.
For inbound NSG rules, allow 22/tcp from your management network and 80/tcp from your management network only (plus 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, and search the Marketplace for xyOps by SecureAnyCloud. Select Create.
On the Basics tab, 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. Add a scoped HTTP rule after deployment, as shown in Step 3, then review and create.
Step 2 - Deploy from the Azure CLI
# Accept the Marketplace terms once per subscription (required before the first CLI deploy)
az vm image terms accept --urn sac1647283583153:xyops-ubuntu-24-04:default:latest
az vm create \
--resource-group <your-rg> \
--name xyops \
--image sac1647283583153:xyops-ubuntu-24-04:default:latest \
--size Standard_B2s \
--admin-username azureuser \
--ssh-key-values ~/.ssh/id_ed25519.pub \
--vnet-name <your-vnet> --subnet <your-subnet> \
--public-ip-sku StandardStep 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 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 InboundThen 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 versionBoth 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. nginx listens on port 80 and reverse-proxies to it.
Step 5 - Retrieve your admin password
First boot generates a unique xyOps admin password for your VM and writes it to a root-only file:
sudo stat -c '%a %U:%G %n' /root/xyops-credentials.txt
sudo cat /root/xyops-credentials.txtThe file contains XYOPS_URL, XYOPS_USERNAME (admin) and XYOPS_PASSWORD. It is mode 0600 and 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. Confirm it yourself:
curl -s -H 'Content-Type: application/json' \
-d '{"username":"admin","password":"admin"}' \
http://localhost/api/user/loginThe response is {"code":"login","description":"Username or password incorrect."}. xyOps always answers this endpoint with HTTP 200 and reports the outcome in the code field of the JSON body. Read the body rather than the status code.
Step 6 - Confirm the health endpoint and prove a real job runs
nginx serves an unauthenticated health endpoint for load balancers and probes. The image also ships a self-test that does more than check that a page loads. It signs in with your per-VM password, confirms the default and a wrong password are both rejected, and confirms the local job runner is registered. Then it 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.shBoth requests return HTTP 200. 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 repairs the xyOps failed-login counters afterwards, so it is safe to run as often as you like.
Step 7 - Sign in to the web console
Browse to http://<vm-public-ip>/ to open the xyOps User Login page. Enter admin as the username and the password from Step 5, then select Login.
Step 8 - Explore the dashboard
After you sign in, the Dashboard opens. 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.
Step 9 - Create a scheduled event
Select Events in the sidebar, then New Event.... Give the event a title, then choose a Category, a Target and a Plugin:
- Category: the image ships a ready-made
Generalcategory. - Target: the
Main Groupcovers this VM's own runner. - 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. The Run, Edit and History actions are on every row.
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 and the Job Output block holding exactly what your script printed. The summary covers which event, which plugin and which server ran, when the job started and finished, how long it took, and average CPU and memory.
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. 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 licenses shipped with your image
Every image carries its upstream licenses and a production dependency license audit generated when the image was built:
head -3 /opt/xyops/LICENSE.md head -5 /opt/xyops/SAC-DEPENDENCY-LICENCES.txtxyOps and the bundled xySat runner are both BSD-3-Clause. The audit lists every package in the production dependency tree with its license. It resolves the license from the package's own LICENSE file where its package.json omits the field.
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 withdu -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 password, and start the service 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_urlin/opt/xyops/conf/config.jsonto yourhttps://URL so the links xyOps generates in emails, tickets and web hooks are correct. 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
adminprivilege grants everything, so prefer a role with only the privileges each person needs. - Upgrades:
sudo -u xyops /opt/xyops/bin/control.sh upgradeupgrades in place and preserves your data, configuration and users. Take a backup first. - Security patches: unattended-upgrades stays enabled, so the OS continues to receive security updates automatically.
Support
SecureAnyCloud provides 24/7 expert support for this image. Contact sales@secureanycloud.com.
Deploy on Azure
Launch xyOps Workflow Automation on Ubuntu 24.04 LTS on Azure with 24/7 support from SecureAnyCloud.
View this image
Need Help?
Our support team is available 24/7.
sales@secureanycloud.com