> ## Documentation Index
> Fetch the complete documentation index at: https://docs.riza.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Custom Runtimes

> Customize your Riza runtime environment using the API.

<Note>
  We only support custom runtimes for Python and JavaScript/TypeScript.
</Note>

Custom runtimes allow you to install dependencies for use in your Python and JavaScript code.
After building a custom runtime, you can use it with the [Execute Code endpoint](/api-reference/command/execute-code).

You can create custom runtimes from the [Riza Dashboard](https://dashboard.riza.io), but
in this guide, we'll use the [Custom Runtimes API](/api-reference/runtime/create-runtime) to programmatically create and manage custom runtimes.

## Set up

Before getting started, you'll need an API key from the [Riza Dashboard](https://dashboard.riza.io).
Export it as an environment variable within your shell:

```sh theme={null}
export RIZA_API_KEY=<your-api-key>
```

## Create a Python custom runtime

In this example, we'll create a custom Riza runtime environment with the [Markdown](https://pypi.org/project/Markdown/) Python package, which converts
Markdown to HTML.

The required parameters are "name", "language" and "manifest\_file", which is a simple PyPi [requirements](https://pip.pypa.io/en/stable/reference/requirements-file-format/) file declaring your dependencies.

<CodeGroup>
  ```js Node theme={null}
  import Riza from "@riza-io/api";

  const riza = new Riza();

  async function main() {
    const resp = await riza.runtimes.create({
      name: "acme_corp_custom_runtime",
      language: "python",
      manifest_file: {
        name: "requirements.txt",
        contents: "markdown==3.7",
      },
    });
    console.log(resp);
  }

  main();
  ```

  ```py Python theme={null}
  from rizaio import Riza

  riza = Riza()

  resp = riza.runtimes.create(
      name="acme_corp_custom_runtime",
      language="python",
      manifest_file={
          "name": "requirements.txt",
          "contents": "markdown==3.7",
      },
  )

  print(dict(resp))
  ```

  ```go Go theme={null}
  package main

  import (
  	"context"
  	"log"
  	"log/slog"

  	"github.com/riza-io/riza-api-go"
  )

  func main() {
  	client := riza.NewClient()
  	params := riza.RuntimesCreateParams{
  		Name: riza.F("acme_corp_custom_runtime"),
  		Language: riza.F(riza.RuntimeNewParamsLanguagePython),
  		ManifestFile: &riza.ManifestFile{
  			Name:    riza.F("requirements.txt"),
  			Contents: riza.F("markdown==3.7"),
  		},
  	}
  	resp, err := client.Runtimes.Create(context.Background(), params)
  	if err != nil {
  		log.Fatal(err)
  	}
  	slog.Info(
  		"created custom runtime",
  		"id", resp.ID,
  		"revisionId", resp.RevisionID,
  		"status", resp.Status
  	)
  }
  ```
</CodeGroup>

The resulting runtime object you receive from the API will look like this. Take note of the `id` and `revision_id` parameters, which we'll use in future steps.

```sh theme={null}
{
	'id': '01JFESTGXM...',
	'language': 'python',
	'name': 'acme_corp_custom_runtime',
	'revision_id': '01JFETP2VD...',
	'status': 'pending',
	'additional_python_imports': '',
	'manifest_file': ManifestFile(contents='markdown==3.7', name='requirements.txt')
}
```

Each custom runtime will have one or more associated revisions. The first revision is automatically created when you create a runtime.
You'll have a new `revision_id` each time you update your dependencies, while the runtime `id` will remain the same.

## Create a JavaScript custom runtime

In this example, we'll create a custom Riza JavaScript runtime environment with the [marked](https://www.npmjs.com/package/marked) package.

<CodeGroup>
  ```js Node theme={null}
  import Riza from "@riza-io/api";

  const riza = new Riza();

  async function main() {
    const resp = await riza.runtimes.create({
      name: "acme_corp_custom_runtime",
      language: "javascript",
      manifest_file: {
        name: "package.json",
        contents: '{"dependencies": {"marked": "^15.0.6"}}',
      },
    });
    console.log(resp);
  }

  main();
  ```

  ```py Python theme={null}
  from rizaio import Riza

  riza = Riza()

  resp = riza.runtimes.create(
      name="acme_corp_custom_runtime",
      language="javascript",
      manifest_file={
          "name": "package.json",
          "contents": '{"dependencies": {"marked": "^15.0.6"}}',
      },
  )

  print(dict(resp))
  ```

  ```go Go theme={null}
  package main

  import (
  	"context"
  	"log"
  	"log/slog"

  	"github.com/riza-io/riza-api-go"
  )

  func main() {
  	client := riza.NewClient()
  	params := riza.RuntimesCreateParams{
  		Name: riza.F("acme_corp_custom_runtime"),
  		Language: riza.F(riza.RuntimeNewParamsLanguageJavascript),
  		ManifestFile: &riza.ManifestFile{
  			Name:    riza.F("package.json"),
  			Contents: riza.F('{"dependencies": {"marked": "^15.0.6"}}'),
  		},
  	}
  	resp, err := client.Runtimes.Create(context.Background(), params)
  	if err != nil {
  		log.Fatal(err)
  	}
  	slog.Info(
  		"created custom runtime",
  		"id", resp.ID,
  		"revisionId", resp.RevisionID,
  		"status", resp.Status
  	)
  }
  ```
</CodeGroup>

## Wait for the custom runtime to build

Building a custom runtime is not instant. It will likely take a few seconds, and could take more than a minute.

To check a build's status, use the Riza API to fetch the custom runtime. The runtime object's `status` parameter indicates whether the latest build is complete and whether it succeeded or failed.

To monitor progress, periodically call the API until the status is either `"succeeded"` or `"failed"`. The code below demonstrates this approach.

<CodeGroup>
  ```js Node theme={null}
  import Riza from "@riza-io/api";

  const riza = new Riza();

  async function pollRuntimeStatus(runtimeId) {
    const pollingIntervalSeconds = 2000;

    while (true) {
      const resp = await riza.runtimes.get(runtimeId);
      console.log(resp.status);

      if (resp.status === "succeeded" || resp.status === "failed") {
        return resp.status;
      }

      await new Promise((resolve) => setTimeout(resolve, pollingIntervalSeconds));
    }
  }

  const finalStatus = await pollRuntimeStatus("01JFESTGXM..."); // runtime `id` from previous step
  console.log(`Final status: ${finalStatus}`);
  ```

  ```py Python theme={null}
  import asyncio
  from rizaio import AsyncRiza

  riza = AsyncRiza()

  async def poll_runtime_status(runtime_id):
      polling_interval_seconds = 2

      while True:
          resp = await riza.runtimes.get(id=runtime_id)
          status = dict(resp)["status"]
          print(status)

          if status in ["succeeded", "failed"]:
              return status

          await asyncio.sleep(polling_interval_seconds)

  # Usage
  final_status = asyncio.run(poll_runtime_status("01JFESTGXM...")) # runtime `id` from previous step
  print(f"Final status: {final_status}")
  ```

  ```go Go theme={null}
  package main

  import (
  	"context"
  	"log/slog"
  	"time"

  	"github.com/riza-io/riza-api-go"
  )

  func pollRuntimeStatus(client *riza.Client, runtimeID string) (string, error) {
  	pollingInterval := 2 * time.Second
  	ticker := time.NewTicker(pollingInterval)
  	defer ticker.Stop()

  	for {
  		<-ticker.C
  		resp, err := client.Runtimes.Get(context.Background(), runtimeID)
  		if err != nil {
  			return "", err
  		}

  		slog.Info("Polling runtime status", "status", resp.Status)

  		switch resp.Status {
  		case "succeeded", "failed":
  			return resp.Status, nil
  		}
  	}
  }

  func main() {
  	client := riza.NewClient()
  	runtimeID := "01JFESTGXM..." // runtime `id` from previous step

  	finalStatus, err := pollRuntimeStatus(client, runtimeID)
  	if err != nil {
  		slog.Error("Error polling runtime status", "error", err)
  		return
  	}
  	slog.Info("Final status", "final_status", finalStatus)
  }
  ```
</CodeGroup>

## Use the custom runtime

If the custom runtime build succeeds, you can now use it to run code that
imports the Markdown package.

You'll need the runtime **revision** ID from the first step to use the
runtime with the [Execute Code endpoint](/api-reference/command/execute-code).

Now, you can execute Python code with the `markdown` package using the custom runtime from the previous step.

<CodeGroup>
  ```js Node theme={null}
  import Riza from "@riza-io/api";

  const pythonCode = `
  import sys
  import markdown

  print(markdown.markdown(sys.stdin.read()))
  `;

  const riza = new Riza();

  async function main() {
    const resp = await riza.command.exec({
      runtime_revision_id: "01JFETP2VD...",
      code: pythonCode,
      stdin: "**Hello, Markdown!**",
    });
    console.log(resp);
  }

  main();
  ```

  ```py Python theme={null}
  from rizaio import Riza

  CODE = """
  import sys
  import markdown

  print(markdown.markdown(sys.stdin.read()))
  """


  client = Riza()

  resp = client.command.exec(
      runtime_revision_id="01JFETP2VD...",
      code=CODE,
      stdin="**Hello, Markdown!**",
  )

  print(dict(resp))
  ```

  ```go Go theme={null}
  package main

  import (
  	"context"
  	"log"
  	"log/slog"

  	"github.com/riza-io/riza-api-go"
  )

  const pythonCode = `
  import sys
  import markdown

  print(markdown.markdown(sys.stdin.read()))
  `

  func main() {
  	client := riza.NewClient()
  	params := riza.CommandExecParams{
  		RuntimeRevisionId: riza.F("01JFETP2VD..."),
  		Code:    riza.F(pythonCode),
  		Stdin:   riza.F("**Hello, Markdown!**"),
  	}
  	resp, err := client.Command.Exec(context.Background(), params)
  	if err != nil {
  		log.Fatal(err)
  	}
  	slog.Info(
  		resp.Stdout,
  		"code", resp.ExitCode,
  		"stderr", resp.Stderr,
  	)
  }
  ```
</CodeGroup>

Similarly, you can execute JavaScript code with the `marked` package using the custom runtime from the previous step.

<CodeGroup>
  ```js Node theme={null}
  import Riza from "@riza-io/api";

  const jsCode = `
  import { marked } from 'marked'

  const input = await new Response(process.stdin).text();

  console.log(marked(input));
  `;

  const riza = new Riza();

  async function main() {
    const resp = await riza.command.exec({
      runtime_revision_id: "01JFETP2VD...",
      code: jsCode,
      stdin: "**Hello, Markdown!**",
    });
    console.log(resp);
  }

  main();
  ```

  ```py Python theme={null}
  from rizaio import Riza

  CODE = """
  import { marked } from 'marked'

  const input = await new Response(process.stdin).text();

  console.log(marked(input));
  """


  client = Riza()

  resp = client.command.exec(
      runtime_revision_id="01JFETP2VD...",
      code=CODE,
      stdin="**Hello, Markdown!**",
  )

  print(dict(resp))
  ```

  ```go Go theme={null}
  package main

  import (
  	"context"
  	"log"
  	"log/slog"

  	"github.com/riza-io/riza-api-go"
  )

  const jsCode = `
  import { marked } from 'marked'

  const input = await new Response(process.stdin).text();

  console.log(marked(input));
  `

  func main() {
  	client := riza.NewClient()
  	params := riza.CommandExecParams{
  		RuntimeRevisionId: riza.F("01JFETP2VD..."),
  		Code:    riza.F(jsCode),
  		Stdin:   riza.F("**Hello, Markdown!**"),
  	}
  	resp, err := client.Command.Exec(context.Background(), params)
  	if err != nil {
  		log.Fatal(err)
  	}
  	slog.Info(
  		resp.Stdout,
  		"code", resp.ExitCode,
  		"stderr", resp.Stderr,
  	)
  }
  ```
</CodeGroup>

## Next steps

Check out the [Create Runtime Revision
endpoint](/api-reference/runtime/create-runtime-revision) if you need to
add or remove dependencies.

If you have questions, please drop us a line in [Discord](https://discord.gg/4P6PUeJFW5).
