Skip to content
Runtime

How to run Java code in a sandbox

Install an OpenJDK package from Ubuntu in an isolated Linux microVM. Write the .java file, then run it with java under a timeout.

On Runtime, Java takes one apt command, or none in an image you build once. A single-file program needs no separate compile step. Every sandbox is a Firecracker microVM with its own kernel and sudo. The code interpreter runs Java cells too, and it keeps variables between them. On 26 October 2026, the first Java cell in a sandbox installed Java from Ubuntu's archive in about 30 seconds, once per sandbox (code interpreter). A 2 vCPU, 4 GiB sandbox costs $0.03125 an hour while it waits and $0.08 with both CPUs busy (pricing).

Which JDK to install

The versions below are from Ubuntu 24.04's archive. They were read on 26 October 2026:

Package Version Notes
openjdk-21-jdk-headless 21.0.12.1 Java 21 LTS without GUI libraries; main archive
default-jdk-headless Java 21 Points at OpenJDK 21
openjdk-25-jdk 25.0.4.1 Java 25, from the universe archive
maven 3.8.7 Maven, from the universe archive
gradle 4.4.1 An old Gradle; use your project's ./gradlew

The headless JDK leaves out the GUI libraries. A command-line program does not need them. So the JDK takes less space on the default 4 GiB disk.

Install Java and run a program

Since JDK 11, the java launcher runs a program given as one source file. No javac step is needed (JEP 330):

TypeScriptimport { Sandbox } from "withruntime";const source = `import java.util.stream.IntStream;public class Main {    public static void main(String[] args) {        int n = Integer.parseInt(args[0]);        System.out.println(IntStream.rangeClosed(1, n).sum());    }}`;await using sbx = await Sandbox.create({ timeoutSeconds: 900, onLeaseEnd: "stop" });await sbx.exec(  "sudo apt-get update -q && sudo apt-get install -y -q --no-install-recommends openjdk-21-jdk-headless",  { check: true, timeoutMs: 600_000 },);await sbx.network.set({ internet: false }); // the program runs offlineawait sbx.files.write("/workspace/Main.java", source);const run = await sbx.exec(["java", "Main.java", "100"], { timeoutMs: 60_000 });console.log(run.exitCode, run.stdout); // 0 5050
Pythonfrom withruntime import Sandboxsource = """import java.util.stream.IntStream;public class Main {    public static void main(String[] args) {        int n = Integer.parseInt(args[0]);        System.out.println(IntStream.rangeClosed(1, n).sum());    }}"""with Sandbox.create(timeout_seconds=900, on_lease_end="stop") as sbx:    sbx.exec("sudo apt-get update -q && sudo apt-get install -y -q --no-install-recommends "             "openjdk-21-jdk-headless", check=True, timeout_ms=600_000)    sbx.network.set(internet=False)  # the program runs offline    sbx.files.write("/workspace/Main.java", source)    run = sbx.exec(["java", "Main.java", "100"], timeout_ms=60_000)    print(run.exit_code, run.stdout)  # 0 5050
  • The argument after the file name reaches main as args[0]. The command is an array, so it runs java directly, and no shell reads it.
  • Compile errors are returned on stderr, with a non-zero exit code.
  • A program that never ends returns timedOut: true and the output it printed.

For several files, compile them with javac -d out $(find src -name '*.java') and run java -cp out Main.

A Java notebook

The interpreter keeps a Java context's variables between cells, as a notebook does:

TypeScriptimport { Sandbox } from "withruntime";await using sbx = await Sandbox.create();await sbx.interpreter.run("record Item(String name, double price) {}", { language: "java" });await sbx.interpreter.run(  'var items = java.util.List.of(new Item("pen", 1.5), new Item("pad", 3.25));',  { language: "java" },);const cell = await sbx.interpreter.run("items.stream().mapToDouble(Item::price).sum()", {  language: "java",});console.log(cell.results[0]?.data["text/plain"]); // 4.75
Pythonfrom withruntime import Sandboxwith Sandbox.create() as sbx:    sbx.interpreter.run("record Item(String name, double price) {}", language="java")    sbx.interpreter.run('var items = java.util.List.of(new Item("pen", 1.5), new Item("pad", 3.25));',                        language="java")    cell = sbx.interpreter.run("items.stream().mapToDouble(Item::price).sum()", language="java")    print(cell["results"][0]["data"]["text/plain"])  # 4.75

The first Java cell installs the JDK from Ubuntu's archive. Keep internet access on for that install, or use an image that already has Java.

A Maven or Gradle project

Upload the project. Let the build tool fetch its dependencies. Then run the tests with the network off:

TypeScriptimport { Sandbox } from "withruntime";await using sbx = await Sandbox.create({  image: "java",  diskMiB: 8192,  timeoutSeconds: 3600,  onLeaseEnd: "stop",});await sbx.files.upload("./my-service", "/workspace/app");await sbx.exec("mvn -q -B dependency:go-offline", {  cwd: "/workspace/app",  check: true,  timeoutMs: 900_000,});await sbx.network.set({ internet: false });const test = await sbx.exec(["mvn", "-B", "-o", "test"], {  cwd: "/workspace/app",  timeoutMs: 1_800_000,  onStdout: (text) => process.stdout.write(text),});console.log("exit", test.exitCode);

For Gradle, run the project's own wrapper (./gradlew test) rather than Ubuntu's Gradle 4.4.1.

Java in every sandbox

TypeScriptimport { Runtime } from "withruntime";const runtime = new Runtime();await runtime.images.build({ name: "java", recipe: { apt: ["openjdk-21-jdk-headless", "maven"] } });await using sbx = await runtime.sandboxes.create({ image: "java", network: { internet: false } });console.log((await sbx.exec(["java", "-version"])).stderr);
Pythonfrom withruntime import Runtimeruntime = Runtime()runtime.images.build(name="java", recipe={"apt": ["openjdk-21-jdk-headless", "maven"]})with runtime.sandboxes.create(image="java", network={"internet": False}) as sbx:    print(sbx.exec(["java", "-version"]).stderr)

The same image serves the code interpreter. A Java cell in a sandbox made from it starts without installing anything. Building an image is free. A stored image is billed by its size (custom images).

A new account gets 100 free hours of a 1 GB machine in its first 30 days. No payment card is needed:

Terminalnpx withruntime sandboxes run -- bash -c 'sudo apt-get update -q && sudo apt-get install -y -q openjdk-21-jdk-headless && java -version'

Sources

Facts on this page were checked on 26 October 2026.

100 free hoursin your first 30 days.

of a 1 GB machine

  • No credit card
  • Four sandboxes at once, 2 vCPU and 4 GiB each
  • Then prepaid credit from $10, no plan fee
Start free, no card
Start free, no card