Skip to content
Runtime

How to run PHP code in a sandbox

Install php-cli in a Linux microVM. Write the script, then run it with php under a time limit and with the network off.

On Runtime, PHP is one apt command away, and every script gets its own kernel. Each sandbox is a Firecracker microVM that runs Ubuntu 24.04.5. There, sudo apt-get install -y php-cli installs PHP 8.3 from Ubuntu's archive. If you bake PHP into an image, that sandbox start is the only wait.

PHP packages on Ubuntu 24.04

PHP is not in the default image. The packages below are for Ubuntu 24.04 (noble). They were read in Ubuntu's archive on 26 October 2026:

Package Version What it adds
php-cli 8.3 (php8.3-cli 8.3.6) The php command
php8.3-sqlite3 8.3.6 PDO and SQLite3 for SQLite
php8.3-pgsql 8.3.6 PDO and pgsql for PostgreSQL
composer 2.7.1 Composer, the dependency manager

For another PHP version, start a custom image from the official php image on Docker Hub. An example is php:8.4-cli, a tag listed there on 26 October 2026.

Run a script

Install PHP once, turn off the internet, then run the script. The script is written as a file and runs from an array command, so nothing passes through a shell.

TypeScriptimport { Sandbox } from "withruntime";const script = `<?php$words = str_word_count("the quick brown fox jumps over the lazy dog", 1);echo json_encode(array_count_values(array_map('strlen', $words))), PHP_EOL;`;await using sbx = await Sandbox.create({ timeoutSeconds: 600, onLeaseEnd: "stop" });await sbx.exec("sudo apt-get update -q && sudo apt-get install -y -q php-cli", {  check: true,  timeoutMs: 300_000,});await sbx.network.set({ internet: false });await sbx.files.write("/workspace/job.php", script);const lint = await sbx.exec(["php", "-l", "job.php"]);if (lint.exitCode !== 0) throw new Error(lint.stdout);const run = await sbx.exec(["php", "-d", "memory_limit=256M", "job.php"], { timeoutMs: 20_000 });console.log(run.exitCode, run.timedOut, run.stdout);
Pythonfrom withruntime import Sandboxscript = """<?php$words = str_word_count("the quick brown fox jumps over the lazy dog", 1);echo json_encode(array_count_values(array_map('strlen', $words))), PHP_EOL;"""with Sandbox.create(timeout_seconds=600, on_lease_end="stop") as sbx:    sbx.exec("sudo apt-get update -q && sudo apt-get install -y -q php-cli", check=True, timeout_ms=300_000)    sbx.network.set(internet=False)    sbx.files.write("/workspace/job.php", script)    lint = sbx.exec(["php", "-l", "job.php"])    if lint.exit_code != 0:        raise SystemExit(lint.stdout)    run = sbx.exec(["php", "-d", "memory_limit=256M", "job.php"], timeout_ms=20_000)    print(run.exit_code, run.timed_out, run.stdout)
  • php -l checks the syntax without running the code, and it reports on stdout. It does not find errors that appear only at run time, such as a call to an undefined function (PHP command-line options).
  • -d sets any php.ini directive for one run, so each job can get its own memory limit.
  • timeoutMs stops the command from outside PHP. An endless loop returns timedOut: true and the output so far.

Why not disable_functions?

PHP's own settings can hide functions such as exec or system. But the code still runs in your process, on your kernel, next to your files and network. A sandbox moves the whole interpreter into a microVM with its own kernel. Leave shell_exec enabled if the job needs it. It reaches only a throwaway machine, and the host enforces that machine's network, CPU, memory and cost (security).

Install Composer packages, then cut the network

Composer needs Packagist and the package hosts while it installs. Let it install with internet access on. Then turn the network off before the project's code runs:

TypeScriptimport { Sandbox } from "withruntime";await using sbx = await Sandbox.create({ timeoutSeconds: 900, onLeaseEnd: "stop" });await sbx.exec("sudo apt-get update -q && sudo apt-get install -y -q php-cli composer unzip", {  check: true,  timeoutMs: 300_000,});await sbx.files.upload("./app", "/workspace/app");await sbx.exec(["composer", "install", "--no-interaction", "--no-progress"], {  cwd: "/workspace/app",  check: true,  timeoutMs: 300_000,});await sbx.network.set({ internet: false });const tests = await sbx.exec(["php", "vendor/bin/phpunit"], {  cwd: "/workspace/app",  timeoutMs: 300_000,});console.log(tests.exitCode, tests.stdout.slice(-3000));

See a PHP page in your browser

PHP's built-in web server (php -S) serves a folder. Start it with spawn, so it keeps running. Then share the port as a private HTTPS preview:

TypeScriptimport { Sandbox } from "withruntime";await using sbx = await Sandbox.create({ image: "php", timeoutSeconds: 1800 });await sbx.files.write("/workspace/site/index.php", "<?php echo 'Hello from PHP ', PHP_VERSION;");await sbx.spawn(["php", "-S", "0.0.0.0:8000", "-t", "/workspace/site"]);const preview = await sbx.previews.create(8000);console.log(preview.urlWithToken);

This uses the php image built below. See previews of agent-built apps for the whole flow.

Start every sandbox with PHP

Pythonfrom withruntime import Runtimeruntime = Runtime()runtime.images.build(name="php", recipe={"apt": ["php-cli", "php8.3-sqlite3", "composer", "unzip"]})with runtime.sandboxes.create(image="php", network={"internet": False}) as sbx:    print(sbx.exec(["php", "-v"]).stdout)
Terminalruntime images build --apt php-cli,php8.3-sqlite3,composer,unzip --name php

Building an image is free. An account without credit stores its first three images at no charge (custom images).

What it costs

Runtime bills the CPU a script uses at $0.025 per vCPU-hour, with a floor of a twentieth of a vCPU. Memory is billed at $0.0075 per GiB-hour. A 2 vCPU, 4 GiB sandbox waiting between requests costs $0.03125 an hour (pricing). 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 php-cli && php -v'

For code a model wrote, see running untrusted LLM code and turning off a sandbox's internet.

Sources

Checked 26 October 2026.

Facts on this page were checked on 26 October 2026.

Related.

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