uTask
Overview
uTask is an open source automation engine from OVH that turns operational runbooks into auditable, repeatable, self-service tasks. You describe a process once as a YAML task template, with typed and validated inputs and steps that declare their dependencies, conditions, loops and schema checks on each step's output. uTask then runs it as a resumable, fully audited task. It retries what can be retried, pauses for human validation where you ask it to, and keeps a complete history of what ran, who requested it and what it returned.
This SecureAnyCloud image runs uTask on Ubuntu 24.04 on Azure. It installs uTask 1.34.0 from the official upstream release (a single self-contained Go binary, SHA-256 verified during the build), with its PostgreSQL database on the same machine and the official prebuilt Angular dashboard. A web dashboard and a REST API expose your templates to the people and pipelines that need them.
Authentication follows the model uTask prescribes. uTask ships with no built-in login. Upstream documents that it "is meant to be placed behind a reverse proxy that provides a username through the x-remote-user http header". In this image, nginx is that reverse proxy on port 80 and the only network-facing surface. It authenticates every request with HTTP Basic against a per-instance credential and passes the verified username to uTask, overwriting anything a client tries to claim. uTask listens on 8081 and is kept off the network by a host firewall (nftables), so the proxy cannot be bypassed.
No shared credentials. Nothing secret is baked into the image, and the image ships with no database at all. On the first boot of every instance, uTask generates the PostgreSQL role password, the dashboard/API password and the storage encryption key that protects every task input and result at rest. It then creates the database, loads the schema and writes the credentials to a root-only file. No build-time data and no shared key carry over into your deployment.
What you get:
- uTask 1.34.0 (BSD-3-Clause) from the official upstream release binary, run under systemd (
utask.service) as an unprivilegedutaskuser, bound to127.0.0.1:8081 - PostgreSQL (
postgresql.service), uTask's only dependency, bound to loopback and created fresh with a per-instance role password on first boot - nginx (
nginx.service) on port 80 as the only network-facing service, authenticating with HTTP Basic against a per-instance credential and injecting the authenticated username asx-remote-user - An nftables rule (
nftables.service) that drops inbound8081from anywhere except the loopback interface, so the reverse proxy cannot be bypassed - Two self-contained example templates (
hello-worldandhttp-healthcheck), preloaded so the dashboard is useful from your first login - A per-instance dashboard/API password, database password and storage encryption key, generated on first boot, with the values written to
/root/utask-credentials.txt
Prerequisites
You need an active Azure subscription, an SSH public key, and a VNet with a subnet in the target region. Subscribe to the uTask listing on Azure Marketplace.
Configure Network Security Group rules that allow TCP 22 (administration) and TCP 80 (the dashboard and REST API) from the operators and pipelines that use uTask.
Recommended virtual machine size: Standard_B2s (2 vCPU, 4 GB RAM) is ample for a team automation engine. Choose a larger size if you run many concurrent tasks or store a large task history.
Deploy from the Azure Portal
Search Marketplace for uTask, select the SecureAnyCloud publisher, and choose Create. Configure the Network Security Group to allow TCP 80 from the workstations and pipelines that will use uTask, and TCP 22 for administration.
Keep port 80 restricted to the networks you serve. For production, put your own TLS certificate and domain in front of it.
Deploy from the Azure CLI
RG="utask-prod"; LOCATION="eastus"; VM_NAME="utask1" IMAGE_URN="sac1647283583153:utask-ubuntu-24-04:default:latest" SSH_KEY="$(cat ~/.ssh/id_rsa.pub)" az group create --name "$RG" --location "$LOCATION"
# Accept the Marketplace terms once per subscription (required before the first CLI deploy)
az vm image terms accept --urn sac1647283583153:utask-ubuntu-24-04:default:latest
az vm create \
--resource-group "$RG" --name "$VM_NAME" \
--image "$IMAGE_URN" \
--size Standard_B2s \
--admin-username azureuser \
--ssh-key-values "$SSH_KEY" \
--public-ip-sku Standard
az vm open-port --resource-group "$RG" --name "$VM_NAME" --port 80 --priority 1001
az vm open-port --resource-group "$RG" --name "$VM_NAME" --port 22 --priority 1002First boot and per-instance credentials
On first boot, the image generates the per-instance secrets, creates the PostgreSQL database and role, loads the schema, starts uTask and writes everything to /root/utask-credentials.txt. This completes within seconds. SSH in as azureuser and read the credentials file:
sudo cat /root/utask-credentials.txtThe file is 0600 root:root. It records the dashboard URL, the admin username and password for the dashboard and API, and the database name, role and password. These values are unique to this instance.
The storage encryption key that protects task data at rest lives in /etc/utask/utask.env. It is also unique per instance.
Confirm the services are running
SSH to the instance. Confirm that the four services are active and that nginx on port 80 is the only network-facing listener, while PostgreSQL is bound to loopback:
systemctl is-active postgresql nftables utask nginx ss -tlnp | grep -E ':80 |:8081 |:5432 'Expected output:
active active active active LISTEN 0.0.0.0:80 LISTEN *:8081 LISTEN 127.0.0.1:5432nginx listens on port 80 on all interfaces, and PostgreSQL is bound to loopback. uTask binds 8081 on all interfaces because it has no bind address option of its own. The nftables rule is what keeps 8081 unreachable from the network.
Verify the authentication model
uTask has no authentication of its own and trusts the x-remote-user header. This image therefore places it behind an authenticating reverse proxy and firewalls it off the network. Run the checks below to confirm three things. The dashboard and API require the per-instance credential. A client cannot reach uTask directly to spoof its identity. nginx sets the authenticated username itself.
PW=$(sudo grep -E '^UTASK_ADMIN_PASSWORD=' /root/utask-credentials.txt | cut -d= -f2-)
HOSTIP=$(hostname -I | awk '{print $1}')
# public liveness routes curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1/healthz curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1/unsecured/mon/ping
# the API is rejected without the per instance credential, accepted with it curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1/template curl -s -o /dev/null -w '%{http_code}\n' -u admin:"$PW" http://127.0.0.1/template
# uTask on :8081 is unreachable off loopback, so x-remote-user cannot be spoofed curl -s -o /dev/null -w '%{http_code}\n' -m 5 -H 'x-remote-user: admin' http://"$HOSTIP":8081/meta
# nginx reports the AUTHENTICATED username, overwriting any client supplied value curl -s -u admin:"$PW" -H 'x-remote-user: not-this-user' http://127.0.0.1/metaExpected output:
200 200 401 200 000 {"application_name":"uTask","user_is_admin":true,"username":"admin","user_groups":[],"version":"v1.34.0",...}The /template API returns 401 with no credential and 200 with the per-instance credential. The direct request to 8081 returns nothing (000) because the firewall drops it. The /meta response reports admin, the authenticated user, not the not-this-user value the client tried to inject.
Run a task through the engine
Create a task from the preloaded hello-world template through the REST API, using the per-instance credential. Then watch the engine resolve it to state DONE and return a result:
PW=$(sudo grep -E '^UTASK_ADMIN_PASSWORD=' /root/utask-credentials.txt | cut -d= -f2-)
# create a task from the hello-world template
TID=$(curl -s -u admin:"$PW" -H 'Content-Type: application/json' \
-d '{"template_name":"hello-world","input":{"name":"sac"}}' \
http://127.0.0.1/task | python3 -c 'import json,sys; print(json.load(sys.stdin)["id"])')
echo "created task: $TID"
# poll until the engine resolves it curl -s -u admin:"$PW" http://127.0.0.1/task/"$TID" | python3 -m json.tool | grep -A3 '"result"'Expected output:
created task: 47ac2de2-f738-4d97-888e-71e1df1c2007 "result": { "greeting": "Hello sac, this task ran on uTask by sac" },The greeting field was produced by the template's echo step. The engine executed the workflow and recorded its output.
Open the dashboard
Open http://<vm-public-ip>/ui/dashboard/ in a browser. When the browser prompts for HTTP Basic credentials, enter admin and the UTASK_ADMIN_PASSWORD from the credentials file.
The Tasks page lists every task and its state. The task you ran through the API in Step 6 appears in green DONE state. Select any task to see its status, the request that created it, the execution, and the Result panel with the exact output the engine produced.
The Templates page lists the automations available to run. The image ships two self-contained example templates.
Select New task to launch an automation from the browser. Pick a template, fill in its typed inputs and submit. Your team can use this self-service path without touching the API.
Author your own task templates
Templates live in /opt/utask/templates as YAML files. A template declares typed inputs, then steps with dependencies, conditions and action blocks. uTask ships built-in actions for HTTP calls, SSH commands, scripts, email, notifications and subtasks. Copy the bundled hello-world.yaml as a starting point:
sudo cp /opt/utask/templates/hello-world.yaml /opt/utask/templates/my-first-template.yaml
sudo nano /opt/utask/templates/my-first-template.yaml
sudo systemctl restart utaskuTask loads templates from that directory at startup, so restart the service after you add or edit a template. Confirm that it was picked up through the API:
PW=$(sudo grep -E '^UTASK_ADMIN_PASSWORD=' /root/utask-credentials.txt | cut -d= -f2-)
curl -s -u admin:"$PW" http://127.0.0.1/template | python3 -c 'import json,sys; print([t["name"] for t in json.load(sys.stdin)])'The full template authoring reference is documented upstream at github.com/ovh/utask. It covers value templating, step conditions, loops, JSON schema validation and the built-in action catalogue.
Per-instance credentials and the encryption key
Every secret on the instance is generated at first boot and is unique to that instance:
sudo cat /root/utask-credentials.txt
sudo -u postgres psql -tAc 'SELECT name FROM task_template ORDER BY name' utaskThe credentials file is 0600 root:root. The storage encryption key in /etc/utask/utask.env is a 32-byte aes-gcm key that encrypts every task input and result at rest.
Back up the key before you rotate it. Previously stored task data becomes unreadable without the key it was written with. The key rotation procedure is documented upstream.
Security recommendations
Front uTask with your own TLS certificate and domain. nginx serves plain HTTP on port 80. For production, put your own certificate and a real hostname in front so credentials and task data are encrypted in transit.
Restrict port 80 to the networks you serve. A private automation engine rarely needs to be reachable from the whole internet. Scope the Network Security Group to your operators and pipelines.
Integrate your own identity provider. For teams, replace the single HTTP Basic credential. Have your reverse proxy or identity provider set x-remote-user (and optionally group headers) from real authenticated users. uTask uses that username for authorization and for its admin_usernames list.
Rotate the dashboard credential and back up the encryption key. The per-instance dashboard password lives in the nginx htpasswd file. Rotate it with htpasswd -B. Always back up the storage encryption key before you rotate it.
Keep the base image patched. Unattended security upgrades are enabled. Reboot after kernel updates.
Support and Licensing
This is a repackaged open source software product, with additional charges for SecureAnyCloud support services. uTask is licensed under the BSD 3-Clause License. All product and company names are trademarks or registered trademarks of their respective holders. Their use does not imply any affiliation with or endorsement by them.
Deploy on Azure
The SecureAnyCloud uTask image is available on the Azure Marketplace. Search for uTask by SecureAnyCloud, deploy it into your subscription, and follow this guide.
Need Help?
SecureAnyCloud provides 24/7 technical support for this uTask product. We help with deployment and first-boot configuration, authoring and debugging task templates, fronting the dashboard with your own TLS certificate and custom domain, integrating your identity provider, wiring notification backends, encryption key rotation, PostgreSQL sizing and backup, and uTask version upgrades. Critical issues receive a one-hour average response time. Contact sales@secureanycloud.com.
Deploy on Azure
Launch uTask 1.34.0 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
Overview
uTask is OVH's open source automation engine. It turns an operational runbook into an auditable, resumable task. You describe a process once as a YAML task template with typed inputs, dependent steps, conditions, loops and JSON schema checks. uTask executes it as a task with a full history, retrying what can be retried and pausing for human approval where you ask it to.
This SecureAnyCloud image runs uTask 1.34.0 on Ubuntu 24.04 LTS and exposes your templates through a REST API and an Angular web dashboard.
What you get:
- A single-instance stack: PostgreSQL 16 on loopback, uTask on 127.0.0.1:8081, and nginx on port 80 as the only network surface
- nginx on port 80 performing HTTP Basic authentication against a per-instance credential and injecting the authenticated username as
x-remote-user, overwriting anything a client tries to supply - An nftables rule that drops inbound traffic to port 8081 on every non-loopback interface, so the proxy cannot be bypassed
- Three per-instance secrets generated on first boot: the PostgreSQL role password, the storage encryption key that protects every task input and result at rest, and the dashboard/API password
- Credentials written to
/root/utask-credentials.txt(mode0600, root only) - A PostgreSQL data tier (task templates, task history and every encrypted payload) under
/var/lib/postgresqlon a dedicated, independently resizable EBS data volume, separate from the operating system disk - A default security group that opens port 22 (SSH) and port 80 (HTTP) only
PostgreSQL 16 (loopback) <-- uTask 127.0.0.1:8081 <-- nginx :80 (sole network surface)uTask ships with no built-in authentication. Upstream's design is that uTask sits behind a trusted reverse proxy that authenticates the caller and passes the verified username in the x-remote-user header. This image implements that design.
Nothing secret is baked into the image, and the image ships with no database at all. On the first boot of every deployed instance, a one-shot service creates the database and schema from scratch. No build-time data and no shared key carry over into your deployment.
Prerequisites
- An AWS account subscribed to this product in AWS Marketplace
- An EC2 key pair in your target region for SSH access
- A security group that allows inbound TCP 22 (SSH) from your IP and TCP 80 (HTTP) from your users
- Recommended instance type:
m5.largeor larger
Connecting to your instance
SSH in as the default login user for your operating system variant, using the key pair you launched with.
OS variant Login user Example
Ubuntu 24.04 ubuntu ssh -i your-key.pem ubuntu@<instance-public-ip>
Step 1 - Launch from the AWS Marketplace console
- Open the product page in AWS Marketplace. Choose Continue to Subscribe, then Continue to Configuration.
- Select the software version and your AWS Region, then choose Continue to Launch.
- Choose an instance type (
m5.largeor larger), your VPC subnet, the security group described above, and your EC2 key pair. - Launch the instance. Wait for it to reach the running state with status checks passed.
Step 2 - Launch from the AWS CLI (alternative)
Run the command below. Replace the AMI id, key name, security group and subnet with your own values.
aws ec2 run-instances \
--image-id ami-xxxxxxxxxxxxxxxxx \
--instance-type m5.large \
--key-name your-key \
--security-group-ids sg-xxxxxxxx \
--subnet-id subnet-xxxxxxxx \
--metadata-options "HttpTokens=required" \
--tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=utask}]'Step 3 - Retrieve the per-instance credentials
SSH in and read the credentials file that first boot generated. The dashboard username is admin. The password is unique to your instance.
sudo cat /root/utask-credentials.txtThe file also records the PostgreSQL role name and its per-instance password. The storage encryption key lives in /etc/utask/utask.env (mode 0640, root:utask). Back it up before you rotate it, or previously stored task data becomes unreadable.
Confirm the engine is healthy. /healthz is served by nginx, and /unsecured/mon/ping is uTask's own unauthenticated liveness route, proxied through nginx:
curl -s http://127.0.0.1/healthz
curl -s http://127.0.0.1/unsecured/mon/pingStep 4 - Sign in to the dashboard
Browse to http://<instance-public-ip>/ui/dashboard. When the browser prompts for HTTP Basic credentials, sign in as admin with the password from Step 3. The Tasks view opens and lists every task and its state. Two example templates are preloaded.
For production, front the dashboard with your own TLS certificate and domain (Step 8). You can also wire nginx to your identity provider so the reverse proxy passes real usernames.
Step 5 - Run a task and watch the engine resolve it
Open the Templates view to see the reusable YAML workflows exposed to your team.
Choose hello-world, then New task, enter a name and run it. uTask executes the step and moves the task to DONE, recording the full history and the result. You can do the same from the REST API. This command creates a task from the hello-world template and prints the created task:
ADMIN_PASS=$(sudo grep '^UTASK_ADMIN_PASSWORD=' /root/utask-credentials.txt | cut -d= -f2-)
curl -s -u admin:"$ADMIN_PASS" -H 'Content-Type: application/json' \
-d '{"template_name":"hello-world","input":{"name":"there"}}' \
http://127.0.0.1/taskList the templates the engine has loaded, authenticating through nginx:
ADMIN_PASS=$(sudo grep '^UTASK_ADMIN_PASSWORD=' /root/utask-credentials.txt | cut -d= -f2-)
curl -s -u admin:"$ADMIN_PASS" http://127.0.0.1/template | python3 -m json.tool | head -30Open a finished task to inspect every step, who requested it, and the result the engine produced.
Step 6 - Write your own template
Templates are plain YAML files in /opt/utask/templates. Copy one of the shipped examples as a starting point and edit it. uTask loads it on restart:
sudo cp /opt/utask/templates/hello-world.yaml /opt/utask/templates/my-first.yaml
sudo nano /opt/utask/templates/my-first.yaml
sudo systemctl restart utaskuTask ships built-in actions for HTTP calls, SSH commands, scripts, email, notifications and subtasks. You can require human validation on steps that need a second pair of eyes. See the upstream documentation at https://github.com/ovh/utask for the full template reference.
Step 7 - How the authentication model protects you
- nginx is the only network surface. It authenticates every request against the per-instance credential and sets
x-remote-userfrom the authenticated username. A client cannot assert its own identity, because the header it sends is overwritten. - uTask is unreachable except through nginx. uTask has no bind-address option and would otherwise listen on
0.0.0.0:8081and trustx-remote-userunconditionally. An nftables rule drops inbound traffic to 8081 on every non-loopback interface. You can confirm from off-box that the port is blocked. On the instance, you can list the table:
sudo nft list table inet sac_utask- Every secret is per-instance. The database password, dashboard password and storage encryption key are generated on first boot and are never shared between instances.
Step 8 - Enable HTTPS (recommended for production)
For production, terminate TLS in front of uTask. Either put the instance behind an AWS Application Load Balancer or CloudFront with an ACM certificate, or obtain a Let's Encrypt certificate on the instance itself once a DNS name points at it.
sudo apt-get update && sudo apt-get install -y certbot python3-certbot-nginx
sudo certbot --nginx -d your-domain.example.comcertbot updates the nginx site in place and sets up automatic renewal.
Step 9 - Backups, storage and maintenance
- Database volume. All uTask state is in PostgreSQL under
/var/lib/postgresqlon a dedicated EBS volume. Take EBS snapshots on a schedule, and grow the volume (then the filesystem) as your task history grows. - Encryption key.
/etc/utask/utask.envholds the storage encryption key. Include it in your backup plan and store it securely. Rotating it makes previously stored task inputs and results unreadable unless you keep the old key. - Service management. The product runs as four systemd services. Check their state:
systemctl is-active postgresql nftables utask nginxRestart uTask after changing templates or configuration:
sudo systemctl restart utask- Updates. The base OS has unattended security upgrades enabled. SecureAnyCloud can advise on uTask version upgrades and PostgreSQL maintenance.
Support
SecureAnyCloud provides 24/7 technical support for this product by email (sales@secureanycloud.com) and live chat, with a one-hour average response for critical issues. We help with modeling your runbooks as task templates, template authoring and debugging, fronting the dashboard with your own TLS certificate and domain, integrating uTask with your identity provider and monitoring, storage encryption key rotation, PostgreSQL sizing and backup, and uTask version upgrades.
Deploy on AWS
Launch uTask on AWS with 24/7 support from SecureAnyCloud.
View on Marketplace
Need Help?
Our support team is available 24/7.
sales@secureanycloud.com