# How to run C# (.NET) code in a sandbox Install the .NET SDK in a Linux microVM. Then build and run the C# code with `dotnet`, under a time limit and with no network access 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 that runs Ubuntu 24.04.5. Both `dotnet-sdk-10.0` and `dotnet-sdk-8.0` are apt packages in that image. The file-based apps in .NET 10 run a single `.cs` file with `dotnet run app.cs`. Runtime bills the CPU a build uses at $0.025 per vCPU-hour. A sandbox that waits 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) includes both SDKs. The versions below were read on Ubuntu's archive on 26 October 2026: | Package | Version on noble | Runs a lone `.cs` file? | | ----------------- | ------------------------ | ------------------------ | | `dotnet-sdk-10.0` | 10.0.113 (noble-updates) | Yes: `dotnet run app.cs` | | `dotnet-sdk-8.0` | 8.0.132 | No: needs a `.csproj` | According to Microsoft's documentation, file-based apps work with the .NET 10 SDK and later ([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. `DOTNET_NOLOGO=true` hides 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, because a build restores packages from NuGet. Then turn off internet access and run the built program. ```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:** request 8 GiB. The default 4 GiB sandbox has about 2.5 GiB free ([the sandbox environment](/docs/sandbox-environment#disk-cpu-and-memory)). The SDK and its build output share that space. - **Timeouts:** an apt install and a first build can run longer than the 60-second default for a command. Give each one its own `timeoutMs`. - **Compiler errors** from `dotnet build` include the file and line. Return them to the model that wrote the code. - **NuGet packages** go at the top of the file as `#:package Name@version`. 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 run the usual commands. `dotnet-sdk-8.0` also works, 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 then testing with `--no-restore` means the tests never use the network. Output streams back while 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). 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 without credit it counts toward the account's hours. A stored image is billed by its size. ## Serve an ASP.NET Core app Start the app with `spawn`, so it keeps running after the command returns. Share its port as a private HTTPS [preview](/docs/javascript#share-a-port). `spawn` returns immediately. The process keeps running after your connection ends. ```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); ``` A new account gets 100 free hours of a 1 GB machine in its first 30 days. No payment card is needed: ```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 26 October 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 26 October 2026.