Kestra
Overview
Kestra is an open-source, event-driven orchestration platform for building and running data and business workflows. You declare pipelines as YAML flows, so the logic is readable and version-controllable. A large plugin catalogue connects them to databases, cloud services, message queues and scripts in any language.
The SecureAnyCloud image ships the Apache-2.0 licensed Kestra open-source core in single-node standalone mode. A bundled PostgreSQL provides a durable repository and queue. Kestra runs the officially supported way, as an upstream container under systemd, fronted by nginx. All images are pinned by digest and captured into the VM, so your instance starts in seconds.
Kestra executes arbitrary tasks and scripts, so the web UI and API are never left open. SecureAnyCloud fronts them with nginx HTTP Basic Auth, using a unique password generated for each VM on first boot, before the port is reachable. Kestra's own authentication uses the same per-VM credential. The Kestra server, its management port and the bundled PostgreSQL are bound to the loopback interface only. Backed by 24/7 SecureAnyCloud support.
Kestra is a trademark of Kestra Technologies Inc. This image is produced by SecureAnyCloud and is not affiliated with, endorsed by, or sponsored by Kestra Technologies Inc. It ships only the Apache-2.0 licensed open-source Kestra core.
What you get:
- Kestra 1.3.28. The Apache-2.0 licensed open-source orchestrator (scheduler, executor, worker and web server, run as
server standalone), pinned by image digest with the full plugin catalogue. Its web interface provides a flow editor, a live executions view with a Gantt and topology breakdown, and a dashboard. - A bundled PostgreSQL 16 database, pinned by image digest, backing the repository and queue so that flows and executions survive restarts. It is reachable only inside a private Docker network and is never published to a host port.
- Docker Engine (Docker CE) with the Kestra UI and management port published to the loopback interface only, fronted by nginx on port
80. kestra.service,kestra-firstboot.serviceandnginx.serviceas systemd units, enabled and active on boot.- A unique web UI password (nginx HTTP Basic Auth, bcrypt, plus Kestra's own authentication with the same credential), a unique PostgreSQL password and a unique secret encryption key, generated per VM on first boot and never baked into the image.
- A clean, freshly initialised database on first boot: no default login, no shipped secret, no prior flows or executions.
- 24/7 SecureAnyCloud support.
Prerequisites
- An active Azure subscription, an SSH key pair, and a VNet plus subnet in the target region.
- Standard_B2ms (2 vCPU / 8 GiB RAM) is a sensible starting point. Increase the size for heavier workloads and higher flow concurrency.
- NSG inbound: allow
22/tcpfrom your management network and80/tcp(and443/tcponce you add TLS) for the web UI.
The web UI serves plain HTTP on port 80. For production, put it behind TLS with your own domain (see the final section).
Step 1 - Deploy from the Azure Marketplace
Sign in to the Azure Portal, choose Create a resource, search the Marketplace for Kestra by SecureAnyCloud, 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) and HTTP (80). Then select Review + create 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:kestra:default:latest
az vm create \
--resource-group <your-rg> \
--name kestra \
--image sac1647283583153:kestra:default:latest \
--size Standard_B2ms \
--admin-username azureuser \
--ssh-key-values ~/.ssh/id_ed25519.pub \
--vnet-name <your-vnet> --subnet <your-subnet> \
--public-ip-sku Standard
az vm open-port --resource-group <your-rg> --name kestra --port 80 --priority 1010Step 3 - Connect to your VM
ssh azureuser@<vm-public-ip>Step 4 - Confirm the services are running
On first boot, the image generates a unique web UI password, database password and encryption key. It then initialises a clean PostgreSQL database, applies the Kestra schema and starts the stack. Confirm that the systemd services are active:
systemctl is-active docker kestra nginxAll three services report active. The Kestra server runs as a container alongside the bundled PostgreSQL under one Docker Compose project. Check that the public liveness endpoint returns 200 with no credentials:
curl -s -o /dev/null -w 'health: HTTP %{http_code}\n' http://127.0.0.1/healthzStep 5 - Secure by default: the password gates everything
Kestra executes arbitrary tasks, so an open UI or API is remote code execution. SecureAnyCloud fronts both with nginx HTTP Basic Auth and configures Kestra's own authentication with the same per-VM credential. Any request to the UI or API without the exact credential is rejected with 401. Confirm it yourself. This request sends no credential:
curl -s -o /dev/null -w 'no credential: HTTP %{http_code}\n' http://127.0.0.1/api/v1/main/flows/searchIt returns HTTP 401. Guessable defaults such as admin@kestra.io:admin are rejected too. Your unique credential authenticates and returns 200. The next block reads the password from the notes file, so it never appears on screen:
P=$(sudo grep '^KESTRA_UI_PASSWORD=' /root/kestra-notes.txt | cut -d= -f2-)
curl -s -o /dev/null -w 'authorized: HTTP %{http_code}\n' -u "admin@kestra.local:$P" http://127.0.0.1/api/v1/main/flows/searchYour unique user and password are written to /root/kestra-notes.txt, readable only by root:
sudo stat -c '%a %U:%G' /root/kestra-notes.txtIt reports 600 root:root. View the notes, which include the web UI user, the web UI password and the URL, with:
sudo cat /root/kestra-notes.txtStep 6 - Open the Kestra web UI
Browse to http://<vm-public-ip>/. At the login prompt, enter the user admin@kestra.local and the password from /root/kestra-notes.txt. The UI opens on the Flows page, which lists every flow on the server with its namespace, labels and last execution status.
Step 7 - Build flows in the code editor
Kestra flows are declarative YAML. Select Create to open the built-in code editor. Write the flow source on the left with autocompletion and validation, read the live flow property documentation on the right, and switch between the code, a no-code form and a live topology view.
A flow needs only an id, a namespace and a list of tasks. triggers, labels and inputs are optional.
Step 8 - Run and inspect executions
The server schedules and executes flows and persists every execution to PostgreSQL. You can trigger a flow from the UI or from the API. This command lists the flows and their completed executions in a namespace, using the API and your per-VM credential:
P=$(sudo grep '^KESTRA_UI_PASSWORD=' /root/kestra-notes.txt | cut -d= -f2-)
curl -s -u "admin@kestra.local:$P" 'http://127.0.0.1/api/v1/main/flows/search?namespace=sac.demo' | jq -r '.results[] | .namespace + "." + .id'Open any execution to see its Gantt view: each task rendered on a timeline with its duration and status, so you can see exactly how a pipeline ran. This history is what makes execution durable. It is persisted to PostgreSQL and can be replayed.
The Executions page lists every run across your flows with a duration and count chart, so you can monitor throughput and drill into any execution.
Step 9 - Schedules and triggers
Flows can carry triggers so that they run automatically. A Schedule trigger runs a flow on a cron spec, so you can drive nightly jobs and periodic pipelines without an external cron. Flow triggers and webhook triggers let flows react to other flows or to external events.
Add a trigger in the flow YAML, and Kestra registers it the moment you save.
Step 10 - Rotate the password
The password is your access key. To set your own, rewrite the nginx Basic Auth file, update Kestra's own credential and restart the services:
sudo htpasswd -B /etc/nginx/kestra.htpasswd admin@kestra.local
sudo nano /etc/kestra-appliance/kestra.env
# update KESTRA_AUTH_PASSWORD to match
sudo systemctl restart kestra nginxStep 11 - Production: your own domain with TLS
For production, front the web UI with your own domain and TLS. Point a DNS record at the VM public IP, then install a certificate with your preferred tool (for example Certbot with the nginx plugin) and reload nginx.
Once TLS is in place, restrict inbound access in your NSG to 443/tcp (and 22/tcp from your management network). Keep the credential confidential, because it grants full control of your flows, which can execute arbitrary tasks.
Support
SecureAnyCloud supports this image 24/7. For help with deployment or configuration, contact support through secureanycloud.com.
Deploy on Azure
Launch Kestra 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
Kestra is an open-source, event-driven orchestration platform for building and running data and business workflows. You declare pipelines as YAML flows, so the logic is readable and version-controllable. A large plugin catalogue connects them to databases, cloud services, message queues, containers and scripts in any language.
The server schedules and executes flows and persists every execution, so work survives restarts. Its web interface provides a flow editor, a live executions view with a Gantt and topology breakdown, and a dashboard.
This SecureAnyCloud image runs the Apache-2.0-licensed Kestra open-source core as the official upstream kestra/kestra container, pinned by digest, in single-node server standalone mode alongside a bundled PostgreSQL. Docker Compose orchestrates both under a single systemd service on a private container network. Kestra publishes its web UI and API to the loopback interface only (127.0.0.1:8080), along with its management and health port (127.0.0.1:8081). PostgreSQL is never published to a host port.
A host nginx reverse proxy terminates TLS on port 443 with a per-instance self-signed certificate and enforces HTTP Basic authentication. Plain HTTP on port 80 redirects to HTTPS, except the public /healthz liveness probe.
Docker Engine and its entire data root, including the pre-pulled digest-pinned images and the PostgreSQL repository, live on a dedicated data volume mounted at /var/lib/docker. Your orchestration state therefore survives OS-disk changes and can be resized independently.
Security is enforced from first boot. Kestra open-source ships no authentication of its own, and because it executes arbitrary tasks, an exposed instance would be remote code execution. This image therefore never leaves it open. Before Kestra starts, the instance generates a per-instance Web UI credential, a per-instance PostgreSQL password, a per-instance secret encryption key and the self-signed TLS certificate.
The same per-instance credential gates two layers: the nginx HTTP Basic front door and Kestra's own basic authentication. Backed by 24/7 SecureAnyCloud support.
What you get:
- The Apache-2.0-licensed Kestra open-source core, shipped as the official
kestra/kestraimage pinned by digest, running as a single-node standalone server. - A bundled PostgreSQL, pinned by digest, providing a durable repository and queue that are reachable only inside a private container network.
- Docker Engine with Kestra on the loopback interface behind nginx over TLS on port 443.
- A per-instance Web UI credential, PostgreSQL password and secret encryption key, generated on first boot and written to a root-only file.
- Defense in depth: the same per-instance credential enforced by both nginx Basic authentication and Kestra's own basic authentication.
- The Docker data root and PostgreSQL repository on a dedicated data volume at
/var/lib/docker. docker.service,kestra.serviceandnginx.serviceas systemd units, enabled and active.- 24/7 SecureAnyCloud support.
Prerequisites
- An AWS account, an EC2 key pair in the target region, and a VPC with a public subnet.
- m5.large (2 vCPU / 8 GiB RAM) is the recommended instance type. Kestra runs a JVM, so give it adequate memory.
- Security group inbound: allow
22/tcpfrom your management network for SSH and443/tcpfor the web UI.
Step 1 - Launch the AMI
Subscribe to the listing in AWS Marketplace. Then launch an instance from the AMI into your VPC with the security group described above. Choose the m5.large instance type and attach your EC2 key pair.
The image keeps the pre-pulled container images on the dedicated data volume. The Kestra stack therefore comes up within a couple of minutes of first boot, with no image re-pull while it runs its database migrations.
Step 2 - Connect to your instance
Connect over SSH with your EC2 key pair. On Ubuntu 24.04, the SSH login user is ubuntu. For example: ssh -i your-key.pem ubuntu@<instance-public-ip>
ssh -i your-key.pem ubuntu@<public-ip>Step 3 - Read your unique credentials
On first boot, the instance generates a per-instance Web UI credential, PostgreSQL password and secret encryption key. It writes them, along with the resolved web URL, to a root-only file. Read the file with sudo:
sudo cat /root/kestra-notes.txtThe file shows the Web UI login (KESTRA_UI_USER / KESTRA_UI_PASSWORD), the PostgreSQL password and the web URL for this instance. There is no default or shared login anywhere in the image, and every secret is unique to this instance.
Step 4 - Verify the security model from the command line
Before you open a browser, confirm that the front door is closed to anyone without the per-instance credential. The nginx TLS proxy on port 443 uses a self-signed certificate, so pass curl -k to accept it.
The public liveness probe answers with 200 and no credential. It exposes only Kestra's health status, no workflow data:
curl -sk -o /dev/null -w 'GET /healthz -> %{http_code}\n' https://127.0.0.1/healthzAn unauthenticated request to the UI or API is refused with 401:
curl -sk -o /dev/null -w 'GET /ui/ (no credential) -> %{http_code}\n' https://127.0.0.1/ui/The same request with the per-instance credential is served with 200:
curl -sk -o /dev/null -w 'GET /ui/ (per-instance credential) -> %{http_code}\n' -u '<KESTRA_UI_USER>:<KESTRA_UI_PASSWORD>' https://127.0.0.1/ui/Plain HTTP on port 80 redirects any non-health path to HTTPS:
curl -s -o /dev/null -w 'GET http://127.0.0.1/ui/ -> %{http_code}\n' http://127.0.0.1/ui/Expected output:
GET /healthz -> 200 GET /ui/ (no credential) -> 401 GET /ui/ (per-instance credential) -> 200 GET http://127.0.0.1/ui/ -> 301Step 5 - Sign in to the web UI
Browse to https://<instance-public-ip>/ and accept the self-signed certificate warning. Answer the HTTP Basic prompt with the KESTRA_UI_USER and KESTRA_UI_PASSWORD from Step 3. Then sign in on Kestra's own login page with the same credentials.
The Overview dashboard shows execution success ratios and run history.
The Flows page lists your declarative YAML workflows with their namespaces, labels and last execution status.
Open a flow to author or edit it in the built-in YAML editor, with inline documentation and a live topology preview.
Every execution is shown as a Gantt timeline with per-task timing, alongside logs, outputs and a topology view.
Step 6 - Author and run a flow through the API
Everything the UI does is available on the REST API, gated by the same per-instance credential. Create a small flow, execute it, and read the result back. Start by creating the flow:
curl -sk -o /dev/null -w 'create flow -> %{http_code}\n' -u '<KESTRA_UI_USER>:<KESTRA_UI_PASSWORD>' \
-X POST https://127.0.0.1/api/v1/main/flows -H 'Content-Type: application/x-yaml' \
--data-binary 'id: getting_started namespace: company.demo tasks: - id: hello type: io.kestra.plugin.core.debug.Return format: "hello from sac"'Trigger an execution and capture its id:
EID=$(curl -sk -u '<KESTRA_UI_USER>:<KESTRA_UI_PASSWORD>' \
-X POST https://127.0.0.1/api/v1/main/executions/company.demo/getting_started | jq -r '.id')
echo "execution id: $EID"Poll the execution and confirm it reached SUCCESS:
sleep 6 curl -sk -u '<KESTRA_UI_USER>:<KESTRA_UI_PASSWORD>' \
https://127.0.0.1/api/v1/main/executions/"$EID" | jq '{flowId, namespace, state: .state.current}'Expected output (the scheduler and executor ran the flow and PostgreSQL persisted its state):
{ "flowId": "getting_started", "namespace": "company.demo", "state": "SUCCESS" }Step 7 - Confirm the services and the data volume
Confirm that all three systemd units are active:
systemctl is-active docker.service kestra.service nginx.serviceKestra binds only to the loopback interface (8080 UI, 8081 management/health). nginx owns the public ports 80 and 443, and PostgreSQL appears on no host port. Check the listeners:
sudo ss -tlnH | awk '{print $4}' | grep -E ':(80|443|8080|8081|5432)$' | sort -uExpected: 0.0.0.0:80, 0.0.0.0:443, 127.0.0.1:8080 and 127.0.0.1:8081, and no :5432.
The Docker data root, which holds the pre-pulled images and the PostgreSQL repository, is a dedicated ext4 data volume. Check it:
findmnt -no SOURCE,TARGET,FSTYPE /var/lib/dockerStep 8 - Replace the self-signed certificate (optional)
The image ships a per-instance self-signed certificate, so the UI is encrypted out of the box. For a browser-trusted certificate, point a DNS name at the instance and install a certificate from your own CA or a public issuer. Update the ssl_certificate and ssl_certificate_key paths in /etc/nginx/sites-available/kestra, then reload nginx.
You can also front the instance with an Application Load Balancer that terminates TLS with an ACM certificate.
Security model
- No known bootstrap credential. Kestra open-source ships no authentication. This image generates a unique Web UI credential, PostgreSQL password and secret encryption key per instance on first boot, all in a root-only file. Nothing is baked in or shared across instances.
- Two authentication layers, one credential. nginx HTTP Basic authentication on port 443 is the authoritative front-door gate. Kestra's own basic authentication validates the same credential, so even a request that reaches Kestra on loopback is rejected without it.
- Least exposure. Kestra binds to
127.0.0.1only. PostgreSQL is on the private container network with no host port. Only 22, 80 and 443 are reachable from outside, and 80 redirects to 443 except the public health probe. - Encryption at rest for secrets. Kestra's
kestra.encryption.secret-keyis generated per instance, so values and secrets stored in the repository are encrypted with a key unique to your instance. - Fresh database per instance. The captured image carries no database, credential or flows. Every instance re-initialises a clean PostgreSQL cluster and mints fresh secrets on first boot.
Support
SecureAnyCloud maintains this image and provides 24/7 support by email and live chat at sales@secureanycloud.com. We help with deployment, upgrades, plugin configuration, flow authoring, performance tuning and PostgreSQL administration.
Kestra is a trademark of its respective holder. This image is not affiliated with, endorsed by or sponsored by Kestra Technologies Inc. The Kestra name is used nominatively to identify the open-source software shipped in this image.
Deploy on AWS
Launch Kestra with 24/7 support from SecureAnyCloud.
View on Marketplace
Need Help?
Our support team is available 24/7.
sales@secureanycloud.com