We only support custom runtimes for Python and JavaScript/TypeScript.
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.
You can create custom runtimes from the Riza Dashboard, but
in this guide, we’ll use the Custom Runtimes API to programmatically create and manage custom runtimes.
Set up
Before getting started, you’ll need an API key from the Riza Dashboard.
Export it as an environment variable within your shell:
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 Python package, which converts
Markdown to HTML.
The required parameters are “name”, “language” and “manifest_file”, which is a simple PyPi requirements file declaring your dependencies.
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();
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))
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
)
}
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.
{
'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 package.
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();
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))
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
)
}
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.
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}`);
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}")
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)
}
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.
Now, you can execute Python code with the markdown package using the custom runtime from the previous step.
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();
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))
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,
)
}
Similarly, you can execute JavaScript code with the marked package using the custom runtime from the previous step.
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();
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))
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,
)
}
Next steps
Check out the Create Runtime Revision
endpoint if you need to
add or remove dependencies.
If you have questions, please drop us a line in Discord.