On Runtime, each MySQL server runs on its own machine, and that machine is deleted with it. A Firecracker microVM with its own kernel runs Docker and the database. An agent can run migrations, drop tables or load a customer's dump with no route to your real servers. Each sandbox you start is a fresh server with nothing left over. It costs $0.03125 an hour for 2 vCPU and 4 GiB while idle, and $0.08 with both CPUs busy (pricing).
Which MySQL
On 25 October 2026:
| Source | Version | Start it with |
|---|---|---|
Docker Official Image mysql |
9.7.4 (tags 9.7, lts); 8.4.13 (8.4) |
docker run mysql:9.7, after sudo enable-docker |
Ubuntu 24.04 package mysql-server |
8.0.46 | sudo apt-get update && sudo apt-get install -y mysql-server |
| Clients | PyMySQL 1.2.3 and mysql-connector-python 26.10.0 on PyPI; mysql2 3.24.6 on npm |
pip install, npm install |
The samples use the Docker image. It has the current long-term release. Its entrypoint creates the database, the user and your schema from environment variables and files.
Load a dump and query it
On the first container start, Docker runs the files in /docker-entrypoint-initdb.d
that end in .sql, .sql.gz or .sh. It runs them in alphabetical order.
TypeScriptimport { writeFile } from "node:fs/promises";import { Sandbox } from "withruntime";await using sbx = await Sandbox.create({ diskMiB: 8192, timeoutSeconds: 1800 });await sbx.exec("sudo enable-docker", { check: true, timeoutMs: 600_000 });await sbx.files.upload("./dump.sql", "/workspace/init/01-dump.sql");await sbx.exec( "docker run -d --name db -e MYSQL_ROOT_PASSWORD=dev -e MYSQL_DATABASE=app " + "-p 127.0.0.1:3306:3306 -v /workspace/init:/docker-entrypoint-initdb.d:ro mysql:9.7", { check: true, timeoutMs: 600_000 },);await sbx.exec( "timeout 300 bash -c 'until docker exec db mysqladmin ping -h 127.0.0.1 -uroot -pdev --silent; do sleep 2; done'", { check: true, timeoutMs: 330_000 },);const rows = await sbx.exec( `docker exec db mysql -uroot -pdev app -e "SELECT COUNT(*) FROM orders"`, { check: true },);console.log(rows.stdout);await sbx.exec("docker exec db mysqldump -uroot -pdev app > /workspace/after.sql", { check: true, timeoutMs: 300_000,});await writeFile("after.sql", await sbx.files.read("/workspace/after.sql"));Pythonfrom withruntime import Sandboxwith Sandbox.create(disk_mib=8192, timeout_seconds=1800) as sbx: sbx.exec("sudo enable-docker", check=True, timeout_ms=600_000) sbx.files.upload("dump.sql", "/workspace/init/01-dump.sql") sbx.exec("docker run -d --name db -e MYSQL_ROOT_PASSWORD=dev -e MYSQL_DATABASE=app " "-p 127.0.0.1:3306:3306 -v /workspace/init:/docker-entrypoint-initdb.d:ro mysql:9.7", check=True, timeout_ms=600_000) sbx.exec("timeout 300 bash -c 'until docker exec db mysqladmin ping -h 127.0.0.1 " "-uroot -pdev --silent; do sleep 2; done'", check=True, timeout_ms=330_000) print(sbx.exec('docker exec db mysql -uroot -pdev app -e "SELECT COUNT(*) FROM orders"', check=True).stdout) sbx.exec("docker exec db mysqldump -uroot -pdev app > /workspace/after.sql", check=True, timeout_ms=300_000) sbx.files.download("/workspace/after.sql", "after.sql")devis a throwaway password for a server that lives and dies with the sandbox. Nothing on the internet can connect in to a sandbox. The flag-p 127.0.0.1:3306:3306publishes the port inside the sandbox only.- The loop waits until the server answers over TCP. A large dump takes longer to load, so raise the 300 seconds to match.
mysqlprints a warning about a password on the command line to stderr. The warning does not change the result.- For a password that matters, such as one for a real database, pass it as
envon the command.envvalues are never echoed back, and Runtime's journals record a hash of them (run commands).
Let an agent try a migration safely
A sandbox is cheap enough to throw away after every attempt. Load the dump once. Then fork the sandbox, so each migration attempt starts from the same loaded database, with MySQL already running in every copy. A copy that breaks the schema is stopped. The next attempt starts from the fork's source again. Each fork call makes 1 to 100 copies.
Open it in MySQL Workbench or DBeaver
Terminalruntime sandboxes port-forward <id> 13306:3306mysql -h 127.0.0.1 -P 13306 -uroot -p appThe forward carries any TCP port through Runtime's API. It uses your key and
works without credit (forward ports). In code:
sbx.forwardPort(3306, { localPort: 13306 }) or
sbx.forward_port(3306, local_port=13306).
Pull data from a MySQL outside the sandbox
To copy tables from a staging database into the sandbox, run mysqldump in the
sandbox against that host. MySQL listens on 3306, so this needs a paid sandbox.
A sandbox without credit reaches ports 80 and 443 only. A paid sandbox reaches
any public host on any port after the account has made a purchase. A network
allow list can limit it to that one database
(the network).
Sizing
- Disk. The image and its data live on the sandbox's disk. The default 4 GiB disk had about 2.5 GiB free on 24 September 2026, so the samples ask for 8 GiB. Without credit, a sandbox has up to 10 GiB.
- Memory. A sandbox has the memory you ask for with
memoryMiB. InnoDB's buffer pool should fit inside it. - Startup.
sudo enable-dockerinstalls Docker once per sandbox. A custom image whose recipe runsenable-docker --no-startskips that step (Docker in a sandbox).
For MySQL next to the app that uses it, see Docker Compose integration tests.
Sources
- MySQL Docker Official Image, https://hub.docker.com/_/mysql, read 25 October 2026
- Docker Hub tags for mysql, https://hub.docker.com/v2/repositories/library/mysql/tags, read 25 October 2026
- Ubuntu 24.04 mysql-server package, from the noble-updates package index at http://archive.ubuntu.com/ubuntu/dists/noble-updates/main/binary-amd64/, read 25 October 2026
- PyPI PyMySQL and mysql-connector-python, and npm mysql2, read 25 October 2026
Facts on this page were checked on 25 October 2026.