Start HTTP servers and JSON APIs inside a sandbox, forward ports to the host, and access them from your application.
Sometimes you need a service running inside a sandbox — an HTTP server, a JSON API, a background worker — that your host application can talk to. This tutorial shows how to start background processes inside a box, forward ports to the host, and make requests to services running in complete isolation.
Launch a box with port forwarding, start a simple HTTP server inside it, and access it from the host.
Python
Node.js
http_server.py
import asyncioimport requestsimport boxliteasync def main(): async with boxlite.SimpleBox( image="python:slim", ports=[(8080, 8080, "tcp")] # (host_port, guest_port, protocol) ) as box: # Start a simple HTTP server in the background # Redirect stdout/stderr so exec() returns immediately await box.exec("sh", "-c", "python -m http.server 8080 --directory /tmp > /dev/null 2>&1 &" ) # Give the server a moment to start await asyncio.sleep(2) # Access the server from the host response = requests.get("http://localhost:8080") print(f"Status: {response.status_code}") print(f"Response:\n{response.text[:200]}")if __name__ == "__main__": asyncio.run(main())
http_server.js
import { SimpleBox } from '@boxlite-ai/boxlite';function sleep(ms) { return new Promise((resolve) => setTimeout(resolve, ms));}async function main() { const box = new SimpleBox({ image: 'python:slim', ports: [{ hostPort: 8080, guestPort: 8080, protocol: 'tcp' }], }); try { // Start a simple HTTP server in the background // Redirect stdout/stderr so exec() returns immediately await box.exec('sh', '-c', 'python -m http.server 8080 --directory /tmp > /dev/null 2>&1 &' ); // Give the server a moment to start await sleep(2000); // Access the server from the host const response = await fetch('http://localhost:8080'); const text = await response.text(); console.log(`Status: ${response.status}`); console.log(`Response:\n${text.slice(0, 200)}`); } finally { await box.stop(); }}main();
What’s happening:
The ports parameter maps host port 8080 to guest port 8080 over TCP
sh -c "... > /dev/null 2>&1 &" starts the server as a background process with output redirected, so exec() returns immediately
You can then make HTTP requests from the host to http://localhost:8080
Background processes must redirect stdout and stderr (e.g., > /dev/null 2>&1 &). Without the redirect, exec() waits for all output streams to close — which never happens for a long-running server — causing your script to hang.
Create a multi-route JSON API inside the sandbox using Python’s built-in http.server or Node.js’s http module.
Python
Node.js
json_api.py
import asyncioimport requestsimport boxliteasync def main(): async with boxlite.SimpleBox( image="python:slim", ports=[(5050, 5050, "tcp")] ) as box: # Write a JSON API server using Python's standard library await box.exec("sh", "-c", """cat > /tmp/api.py << 'PYEOF'import http.serverimport jsonclass APIHandler(http.server.BaseHTTPRequestHandler): def do_GET(self): if self.path == "/": body = {"message": "Hello from sandboxed API!"} elif self.path.startswith("/compute/"): n = int(self.path.split("/")[-1]) body = {"sum_to": n, "result": sum(range(n))} else: self.send_response(404) self.end_headers() return self.send_response(200) self.send_header("Content-Type", "application/json") self.end_headers() self.wfile.write(json.dumps(body).encode()) def log_message(self, format, *args): pass # Suppress request logsserver = http.server.HTTPServer(("0.0.0.0", 5050), APIHandler)server.serve_forever()PYEOF""") # Start the API in the background await box.exec("sh", "-c", "python /tmp/api.py > /dev/null 2>&1 &") await asyncio.sleep(2) # Make requests from the host response = requests.get("http://localhost:5050/") print(response.json()) # {"message": "Hello from sandboxed API!"} response = requests.get("http://localhost:5050/compute/100") print(response.json()) # {"sum_to": 100, "result": 4950}if __name__ == "__main__": asyncio.run(main())
json_api.js
import { SimpleBox } from '@boxlite-ai/boxlite';function sleep(ms) { return new Promise((resolve) => setTimeout(resolve, ms));}async function main() { const box = new SimpleBox({ image: 'node:20-slim', ports: [{ hostPort: 5050, guestPort: 5050, protocol: 'tcp' }], }); try { // Write a JSON API server using Node.js's built-in http module await box.exec('sh', '-c', `cat > /tmp/api.js << 'JSEOF'const http = require('http');const url = require('url');const server = http.createServer((req, res) => { const path = url.parse(req.url).pathname; res.setHeader('Content-Type', 'application/json'); if (path === '/') { res.end(JSON.stringify({ message: 'Hello from sandboxed API!' })); } else if (path.startsWith('/compute/')) { const n = parseInt(path.split('/').pop()); const result = Array.from({length: n}, (_, i) => i).reduce((a, b) => a + b, 0); res.end(JSON.stringify({ sum_to: n, result })); } else { res.statusCode = 404; res.end(JSON.stringify({ error: 'Not found' })); }});server.listen(5050, '0.0.0.0');JSEOF`); // Start the API in the background await box.exec('sh', '-c', 'node /tmp/api.js > /dev/null 2>&1 &'); await sleep(2000); // Make requests from the host const indexRes = await fetch('http://localhost:5050/'); console.log(await indexRes.json()); // { message: "Hello from sandboxed API!" } const computeRes = await fetch('http://localhost:5050/compute/100'); console.log(await computeRes.json()); // { sum_to: 100, result: 4950 } } finally { await box.stop(); }}main();
Port forwarding changes the VM’s network configuration, which disables outbound internet access (DNS resolution). Use standard library modules (like Python’s http.server or Node.js’s http) that don’t require package installation. For services that need third-party packages, build a custom Docker image with your dependencies pre-installed.
Host and guest ports don’t have to match. Use (3000, 80, "tcp") to access a service listening on port 80 inside the VM via http://localhost:3000 on the host.