# How to run C# (.NET) code in a sandbox Install the .NET SDK in a Linux microVM, then build and run the C# there with `dotnet`, a time limit and no network while it runs. **On Runtime the .NET SDK comes from Ubuntu's own archive and one C# file runs with no project.** Each sandbox is a Firecracker microVM running Ubuntu 24.04.5, where `dotnet-sdk-10.0` and `dotnet-sdk-8.0` are both apt packages, and .NET 10's file-based apps run a single `.cs` file with `dotnet run app.cs`. Runtime bills the CPU a build uses at $0.025 per vCPU-hour, so a sandbox waiting for its next job costs a twentieth of a vCPU ([pricing](/docs/pricing)). ## Which .NET to install .NET is not in the default image. Ubuntu 24.04 (noble) carries both SDKs, read on packages.ubuntu.com on 25 September 2026: | Package | Version on noble | Runs a lone `.cs` file? | | ----------------- | ------------------------ | ------------------------ | | `dotnet-sdk-10.0` | 10.0.112 (noble-updates) | Yes: `dotnet run app.cs` | | `dotnet-sdk-8.0` | 8.0.131 | No: needs a `.csproj` | File-based apps apply to the .NET 10 SDK and later, according to Microsoft's documentation ([file-based apps](https://learn.microsoft.com/en-us/dotnet/core/sdk/file-based-apps)). Two environment variables keep the output clean for a program to read: `DOTNET_CLI_TELEMETRY_OPTOUT=1` turns off the SDK's usage telemetry, and `DOTNET_NOLOGO=true` drops the first-run welcome text ([.NET CLI telemetry](https://learn.microsoft.com/en-us/dotnet/core/tools/telemetry)). ## Run one C# file Install the SDK, build the file while the network is on (a build restores from NuGet), then turn the internet off and run what was built. ```ts check import { Sandbox } from "withruntime"; const program = `using System; using System.Linq; var primes = Enumerable.Range(2, 50).Where(n => Enumerable.Range(2, n - 2).All(d => n % d != 0)); Console.WriteLine(string.Join(", ", primes)); `; const env = { DOTNET_CLI_TELEMETRY_OPTOUT: "1", DOTNET_NOLOGO: "true" }; await using sbx = await Sandbox.create({ diskMiB: 8192, timeoutSeconds: 900, onLeaseEnd: "stop" }); await sbx.exec("sudo apt-get update -q && sudo apt-get install -y -q dotnet-sdk-10.0", { check: true, timeoutMs: 600_000, }); await sbx.files.write("/workspace/app/primes.cs", program); const build = await sbx.exec(["dotnet", "build", "primes.cs"], { cwd: "/workspace/app", env, timeoutMs: 300_000, }); if (build.exitCode !== 0) throw new Error(build.stdout + build.stderr); await sbx.network.set({ internet: false }); const run = await sbx.exec(["dotnet", "run", "primes.cs", "--no-build"], { cwd: "/workspace/app", env, timeoutMs: 30_000, }); console.log(run.exitCode, run.stdout); ``` ```python check from withruntime import Sandbox program = """using System; using System.Linq; var primes = Enumerable.Range(2, 50).Where(n => Enumerable.Range(2, n - 2).All(d => n % d != 0)); Console.WriteLine(string.Join(", ", primes)); """ env = {"DOTNET_CLI_TELEMETRY_OPTOUT": "1", "DOTNET_NOLOGO": "true"} with Sandbox.create(disk_mib=8192, timeout_seconds=900, on_lease_end="stop") as sbx: sbx.exec("sudo apt-get update -q && sudo apt-get install -y -q dotnet-sdk-10.0", check=True, timeout_ms=600_000) sbx.files.write("/workspace/app/primes.cs", program) build = sbx.exec(["dotnet", "build", "primes.cs"], cwd="/workspace/app", env=env, timeout_ms=300_000) if build.exit_code != 0: raise SystemExit(build.stdout + build.stderr) sbx.network.set(internet=False) run = sbx.exec(["dotnet", "run", "primes.cs", "--no-build"], cwd="/workspace/app", env=env, timeout_ms=30_000) print(run.exit_code, run.stdout) ``` - **Disk:** ask for 8 GiB. The default 4 GiB sandbox has about 2.5 GiB free ([the sandbox environment](/docs/sandbox-environment#disk-cpu-and-memory)), and the SDK and its build output share it. - **Timeouts:** an apt install and a first build run past the 60-second default for a command, so give each its own `timeoutMs`. - **Compiler errors** from `dotnet build` are printed with the file and line; hand them back to the model that wrote the code. - **NuGet packages** go at the top of the file as `#:package Name@version`, and the build restores them while the network is still on. ## Build and test a project For a solution with `.csproj` files, upload the folder and use the usual commands. `dotnet-sdk-8.0` works here too, for projects that target .NET 8. ```python check import sys from withruntime import Sandbox env = {"DOTNET_CLI_TELEMETRY_OPTOUT": "1", "DOTNET_NOLOGO": "true"} with Sandbox.create(disk_mib=8192, timeout_seconds=1800, on_lease_end="stop") as sbx: sbx.exec("sudo apt-get update -q && sudo apt-get install -y -q dotnet-sdk-8.0", check=True, timeout_ms=600_000) sbx.files.upload("./Service", "/workspace/Service") sbx.exec(["dotnet", "restore"], cwd="/workspace/Service", env=env, check=True, timeout_ms=600_000) sbx.network.set(internet=False) tests = sbx.exec(["dotnet", "test", "--no-restore"], cwd="/workspace/Service", env=env, timeout_ms=900_000, on_stdout=sys.stdout.write) sys.exit(tests.exit_code or 0) ``` Restoring first and testing with `--no-restore` means the tests never touch the network. Output streams back as the tests run. ## Start every sandbox with .NET Install the SDK once in a [custom image](/docs/images#build-from-an-image-or-a-recipe) and set the two variables for every command: ```ts check import { Runtime } from "withruntime"; const runtime = new Runtime(); await runtime.images.build({ name: "dotnet", recipe: { apt: ["dotnet-sdk-10.0"], env: { DOTNET_CLI_TELEMETRY_OPTOUT: "1", DOTNET_NOLOGO: "true" }, }, }); await using sbx = await runtime.sandboxes.create({ image: "dotnet", diskMiB: 8192 }); console.log((await sbx.exec(["dotnet", "--version"])).stdout); ``` Building an image is free and uses no trial hours; a stored image is charged on its size. ## Serve an ASP.NET Core app Start the app with `spawn` so it keeps running after the command returns, and share its port as a private HTTPS [preview](/docs/javascript#share-a-port). `spawn` returns at once; the process outlives your connection. ```ts check import { Sandbox } from "withruntime"; await using sbx = await Sandbox.create({ image: "dotnet", diskMiB: 8192, timeoutSeconds: 1800 }); await sbx.files.upload("./WebApi", "/workspace/WebApi"); await sbx.spawn("cd WebApi && dotnet run --urls http://0.0.0.0:5000"); await sbx.exec("npx wait-on http://localhost:5000", { check: true, timeoutMs: 300_000 }); const preview = await sbx.previews.create(5000); console.log(preview.urlWithToken); ``` New accounts get 50 free sandbox hours, no card: ```bash no-run npx withruntime login ``` See also [running untrusted LLM code](/use-cases/run-untrusted-llm-code), [grading student code](/use-cases/grade-student-code) and [how to run C++ code in a sandbox](/languages/cpp). ## Sources Checked 25 September 2026. - Ubuntu 24.04 (noble) packages: [dotnet-sdk-10.0](https://packages.ubuntu.com/noble/dotnet-sdk-10.0), [dotnet-sdk-8.0](https://packages.ubuntu.com/noble/dotnet-sdk-8.0) - Microsoft Learn: [file-based apps](https://learn.microsoft.com/en-us/dotnet/core/sdk/file-based-apps), [.NET SDK and CLI telemetry](https://learn.microsoft.com/en-us/dotnet/core/tools/telemetry) Facts on this page were checked on 25 September 2026.