In this full getting started guide, I’ll show you everything you need to know to get up and running with Kestra — the open source workflow orchestration platform that makes it easy to automate your infrastructure tasks, schedule jobs, and build powerful flows without complex scripting.
We’ll start from scratch: setting up a Linux server, installing Docker, and deploying Kestra. Then we’ll dive into building real-world flows — including an HTTP health check monitor, a remote SSH command runner, and an Ansible playbook runner for automated server provisioning.
By the end of this video, you’ll have a working Kestra instance and a solid understanding of how to build and manage your own automation workflows.
Thanks to Kestra for Sponsoring this Deep-Dive! Check out Kestra and automate your workflows for both homelab and business.
Commands and Code used in this video
Below you’ll find the commands and code that were used in the video, so you can follow along and build your own Kestra server!
Preliminary Setup
Refresh the package repository index
apt update
Install updates
apt dist-upgrade
Edit the hostname file to set the hostname
nano /etc/hostname
Edit the hosts file for name resolution
nano /etc/hosts
Create a non-root user account
adduser jay
Add your user to the sudo group
usermod -aG sudo jay
Setting up Docker
Add the required GPG key for the repository:
sudo curl -fsSL https://download.docker.com/linux/debian/gpg -o /etc/apt/keyrings/docker.asc
Add Docker’s repository
sudo nano /etc/apt/sources.list.d/docker.sources
Inside the file, we’ll add the configuration for Docker’s repository:
Types: deb
URIs: https://download.docker.com/linux/debian
Suites: trixie
Components: stable
Architectures: amd64
Signed-By: /etc/apt/keyrings/docker.asc
Refresh our package cache again:
sudo apt update
Install required packages for Docker:
sudo apt install docker-ce docker-ce-cli containerd.io docker-compose-plugin
Run the “Hello World” Container to test Docker:
sudo docker run hello-world
Add your user to the docker group:
sudo usermod -aG docker jay
Installing Kestra
Download the Docker Compose file for Kestra:
curl -o docker-compose.yml \https://raw.githubusercontent.com/kestra-io/kestra/develop/docker-compose.yml
Edit the Docker Compose file and set up basic auth:
nano docker-compose.yml
Launch Kestra:
docker compose up -d
First Flow: HTTP Health Check
id: http-health-check
namespace: learnlinux.tv
description: |
Checks that a list of URLs return HTTP 200.
Sends an email notification if any site is down.
triggers:
- id: every-5-minutes
type: io.kestra.plugin.core.trigger.Schedule
cron: "*/5 * * * *"
tasks:
- id: check-learnlinux-tv
type: io.kestra.plugin.fs.http.Request
uri: https://learnlinux.tv
method: GET
allowFailed: true
- id: notify-if-down
type: io.kestra.plugin.email.MailSend
runIf: "{{ outputs['check-learnlinux-tv'].code is defined and outputs['check-learnlinux-tv'].code != 200 }}"
from: infra-alerts@mail.learnlinux.cloud
to: jay@learnlinux.tv
username: "{{ secret('MAIL_USERNAME') }}"
password: "{{ secret('MAIL_PASSWORD') }}"
host: smtp.mailgun.org
port: 587
transportStrategy: SMTP_TLS
subject: "Kestra: learnlinux.tv is DOWN"
htmlTextContent: |
<p>Site returned unexpected status: {{ outputs['check-learnlinux-tv'].code }}</p>
<p>Checked at: {{ execution.startDate }}</p>
errors:
- id: notify-on-error
type: io.kestra.plugin.email.MailSend
from: infra-alerts@mail.learnlinux.cloud
to: jay@learnlinux.tv
username: "{{ secret('MAIL_USERNAME') }}"
password: "{{ secret('MAIL_PASSWORD') }}"
host: smtp.mailgun.org
port: 587
transportStrategy: SMTP_TLS
subject: "Kestra: learnlinux.tv check FAILED"
htmlTextContent: |
<p>The health check failed with an error (DNS failure or connection refused).</p>
<p>Checked at: {{ execution.startDate }}</p>
Second Flow: Running commands against a server
Create an SSH key:
ssh-keygen -t ed25519 -C “kestra” -f ~/.ssh/kestra_key
Encode the key in base64:
cat ~/.ssh/id_ed25519_kestra | base64 -w 0
Associate the key with your second server:
ssh-copy-id -i ~/.ssh/kestra_key 45.79.213.183
At this point, we shouldn’t need to be logged in as root anymore, so we can exit the root session:
Flow code for the second example
id: ansible-run-linode-provision
namespace: learnlinux.homelab
description: |
Runs a command on a remote Linode VPS via SSH.
tasks:
- id: run-remote-command
type: io.kestra.plugin.fs.ssh.Command
authMethod: OPEN_SSH
host: 45.79.213.183
username: jay
privateKey: "{{ secret('SSH_PRIVATE_KEY') }}"
commands:
- cat /etc/os-release


