Programming Languages​Programming ​Languages

Kestra is language agnostic — you can use any programming language inside your workflows.

Kestra works with any programming language, with some having dedicated plugins and libraries that make it easier to send outputs and metrics back to Kestra.

Dedicated Plugins

Kestra currently supports the following programming languages with dedicated plugins:

  1. Python
  2. R
  3. Node.js
  4. Shell
  5. PowerShell
  6. Julia
  7. Ruby
  8. Go
  9. Deno
  10. Lua
  11. Bun
  12. PHP
  13. Perl
  14. Groovy

Each of these plugins provides two task types:

  • Commands: Execute scripts using a command-line interface (ideal for longer, pre-written files).
  • Script: Write your code directly in YAML (best for short inline scripts).

Script example

An example of an inline Python script:

yaml
id: myflow
namespace: company.team

tasks:
  - id: script
    type: io.kestra.plugin.scripts.python.Script
    beforeCommands:
      - pip install requests kestra
    script: |
      from kestra import Kestra
      import requests

      response = requests.get('https://google.com')
      print(response.status_code)

      Kestra.outputs({'status': response.status_code, 'text': response.text})

Commands example

An example using Shell commands, similar to a terminal session:

yaml
id: myflow
namespace: company.team

tasks:
  - id: commands
    type: io.kestra.plugin.scripts.shell.Commands
    outputFiles:
      - first.txt
      - second.txt
    commands:
      - echo "1" >> first.txt
      - echo "2" >> second.txt

Run any language using the Shell task

With the Commands task, you can execute arbitrary commands inside a Docker container. This means you can run any language, as long as:

  1. Its dependencies can be included in a Docker image.
  2. It can be executed from a Shell command.

For handling outputs and metrics, use the same ::{}:: syntax as the Shell task.
Read more in Shell outputs and metrics.

Rust

Here is an example flow that runs a Rust file inside of a container using a rust image:

yaml
id: rust
namespace: company.team

tasks:
  - id: rust
    type: io.kestra.plugin.scripts.shell.Commands
    taskRunner:
      type: io.kestra.plugin.scripts.runner.docker.Docker
    containerImage: rust:latest
    namespaceFiles:
      enabled: true
    commands:
      - rustc hello_world.rs
      - ./hello_world

The Rust code is saved as a namespace file called hello_world.rs:

rust
fn main() {
    println!("Hello, World!");
}

When executed, the print statement is displayed in the Kestra logs:

rust_output

Check out the full guide which includes using outputs and metrics.

Java

You can build custom plugins in Java which enable you to add custom tasks to your workflows. If you're looking to execute something simpler, you can use the Shell task with a Docker container.

Here is an example flow that runs a Java file inside of a container using a eclipse-temurin image:

yaml
id: java
namespace: company.team

tasks:
  - id: java
    type: io.kestra.plugin.scripts.shell.Commands
    taskRunner:
      type: io.kestra.plugin.scripts.runner.docker.Docker
    containerImage: eclipse-temurin:latest
    namespaceFiles:
      enabled: true
    commands:
      - javac HelloWorld.java
      - java HelloWorld

The Java code is saved as a namespace file called HelloWorld.java:

java
class HelloWorld {
    public static void main(String[] args) {
        System.out.println("Hello, World!");
    }
}

When executed, the print statement is displayed in the Kestra logs:

java_output

C

Here is an example flow that runs a C file inside of a container using a gcc image:

yaml
id: c
namespace: company.team

tasks:
  - id: c
    type: io.kestra.plugin.scripts.shell.Commands
    taskRunner:
      type: io.kestra.plugin.scripts.runner.docker.Docker
    containerImage: gcc:latest
    namespaceFiles:
      enabled: true
    commands:
      - gcc hello_world.c
      - ./a.out

The C code is saved as a namespace file called hello_world.c:

c
#include <stdio.h>

int main() {
   printf("Hello, World!");
   return 0;
}

When executed, the print statement is displayed in the Kestra logs:

c_output

C++

Here is an example flow that runs a C++ file inside of a container using a gcc image:

yaml
id: cplusplus
namespace: company.team

tasks:
  - id: cpp
    type: io.kestra.plugin.scripts.shell.Commands
    taskRunner:
      type: io.kestra.plugin.scripts.runner.docker.Docker
    containerImage: gcc:latest
    namespaceFiles:
      enabled: true
    commands:
      - g++ hello_world.cpp
      - ./a.out

The C++ code is saved as a namespace file called hello_world.cpp:

cpp
#include <iostream>

int main() {
    std::cout << "Hello World!";
    return 0;
}

When executed, the print statement is displayed in the Kestra logs:

cpp_output

TypeScript

You can execute TypeScript using the NodeJS plugin. To do so, you need to install TypeScript and compile our code to JavaScript using tsc.

Once done, you can then execute with NodeJS. However, do note that the file is now a .js file.

yaml
id: typescript
namespace: company.team

tasks:
  - id: ts
    type: io.kestra.plugin.scripts.node.Commands
    namespaceFiles:
      enabled: true
    commands:
      - npm i -D typescript
      - npx tsc example.ts
      - node example.js

This example can be found in the Node.js docs. The file is saved as example.ts.

typescript

type User = {
  name: string;
  age: number;
};

function isAdult(user: User): boolean {
  return user.age >= 18;
}

const justine: User = {
  name: 'Justine',
  age: 23,
};

const isJustineAnAdult: boolean = isAdult(justine);
console.log(isJustineAnAdult)

When executed, the print statement is displayed in the Kestra logs:

ts_output

For more information, you can read more about Node.js with TypeScript on their official site.

PHP

Here is an example flow that runs a PHP file inside of a container using a php image:

yaml
id: php
namespace: company.team

tasks:
  - id: php
    type: io.kestra.plugin.scripts.shell.Commands
    taskRunner:
      type: io.kestra.plugin.scripts.runner.docker.Docker
    containerImage: php:8.4-rc-alpine
    namespaceFiles:
      enabled: true
    commands:
      - php hello_world.php

The PHP code is saved as a namespace file called hello_world.php:

php
<?php
echo "Hello, World!";
?>

When executed, the print statement is displayed in the Kestra logs:

php_output

As of Kestra version 0.24, there is a dedicated PHP plugin. An example flow might look like the following using a script task:

yaml
id: php_script
namespace: company.team

tasks:
  - id: script
    type: io.kestra.plugin.scripts.php.Script
    script: |
      #!/usr/bin/php
      <?php
      echo "Hello, World!\\n";
      ?>

Check out the following example for the commands task:

yaml
id: php_commands
namespace: company.team
tasks:
  - id: commands
    type: io.kestra.plugin.scripts.php.Commands
    inputFiles:
      main.php: |
        #!/usr/bin/php
        <?php
        echo "Hello, World!\\n";
        ?>
    commands:
      - php main.php

Scala

Here is an example flow that runs a Scala file inside of a container using a sbtscala/scala-sbt image:

yaml
id: scala
namespace: company.team

tasks:
  - id: scala
    type: io.kestra.plugin.scripts.shell.Commands
    taskRunner:
      type: io.kestra.plugin.scripts.runner.docker.Docker
    containerImage: sbtscala/scala-sbt:eclipse-temurin-17.0.4_1.7.1_3.2.0
    namespaceFiles:
      enabled: true
    commands:
      - scalac HelloWorld.scala
      - scala HelloWorld

The Scala code is saved as a namespace file called HelloWorld.scala:

scala
object HelloWorld {
    def main(args: Array[String]) = {
        println("Hello, World!")
    }
}

When executed, the print statement is displayed in the Kestra logs:

scala_output

Perl

Here is an example flow that runs a Perl file inside of a container using a perl image:

yaml
id: perl
namespace: company.team

tasks:
  - id: perl
    type: io.kestra.plugin.scripts.shell.Commands
    taskRunner:
      type: io.kestra.plugin.scripts.runner.docker.Docker
    containerImage: perl:5.41.2
    namespaceFiles:
      enabled: true
    commands:
      - perl hello_world.pl

The Perl code is saved as a namespace file called hello_world.pl:

perl
#!/usr/bin/perl
use warnings;
print("Hello, World!\n");

When executed, the print statement is displayed in the Kestra logs:

perl_output

As of Kestra version 0.24, there is a dedicated Perl plugin. An example flow might look like the following using a script task:

yaml
id: perl_inline
namespace: company.team
tasks:
  - id: perl_script
    type: io.kestra.plugin.scripts.perl.Script
    script: |
      my $message = "Hello from an inline Perl script!";
      print $message . "\\n";

Check out the following example for the commands task:

yaml
id: perl_commands
namespace: company.team
tasks:
  - id: perl
    type: io.kestra.plugin.scripts.perl.Commands
    commands:
      - perl -e 'print "Hello from Kestra!

Run any language using a custom Docker image

You can pre-install any language using a custom Docker image and use the Process Task Runner to execute it. Example using Go:

dockerfile
FROM kestra/kestra:latest
USER root

# Install Go
RUN apt-get update -y && apt-get install -y wget && \
    wget -qO- https://go.dev/dl/go1.24.3.linux-amd64.tar.gz | tar -C /usr/local -xzf - && \
    echo 'export PATH=$PATH:/usr/local/go/bin' > /etc/profile.d/golang.sh

# Set Go environment variables for Docker
ENV PATH="/usr/local/go/bin:${PATH}"

Then, point to that Dockerfile in your docker-compose.yml file:

yaml
services:
  kestra:
    build:
      context: .
      dockerfile: Dockerfile
    image: kestra-go:latest

Once you start Kestra using docker compose up -d, you can create a flow that directly runs Go tasks with your custom dependencies using the io.kestra.plugin.core.runner.Process task runner:

yaml
id: golang_process
namespace: company.team

tasks:
  - id: go_custom_dependencies
    type: io.kestra.plugin.scripts.go.Script
    taskRunner:
      type: io.kestra.plugin.core.runner.Process
    beforeCommands:
      - go mod init go_script
      - go get github.com/go-gota/gota/dataframe
      - go mod tidy
    script: |
      package main
      import (
          "os"
          "github.com/go-gota/gota/dataframe"
          "github.com/go-gota/gota/series"
      )
      func main() {
          names := series.New([]string{"Alice", "Bob", "Charlie"}, series.String, "Name")
          ages := series.New([]int{25, 30, 35}, series.Int, "Age")
          df := dataframe.New(names, ages)
          file, _ := os.Create("output.csv")
          df.WriteCSV(file)
          defer file.Close()
      }
    outputFiles:
      - output.csv

Was this page helpful?