Most real workloads need data. This tutorial shows you how to get files into a sandbox, process them, and get results back out. You’ll learn three methods and when to use each one.
Method comparison
| Method | Direction | Best for | Size limit |
|---|
copy_in() / copy_out() | Host ↔ Guest | Files and directories | Large files |
| Volume mounts | Both (shared) | Datasets, config files | No limit |
exec + base64 | Either | Small inline data | ~1 MB (shell limit) |
Prerequisites
npm install @boxlite-ai/boxlite
Method 1: copy_in / copy_out
The most common approach. Upload a file, process it, download the results.
import asyncio
import boxlite
async def main():
async with boxlite.SimpleBox(image="python:slim") as box:
# Upload a script into the box
# Note: destination must be a directory path, not a file path
await box.exec("mkdir", "-p", "/workspace")
await box.copy_in("/host/script.py", "/workspace/")
# Run the script
result = await box.exec("python", "/workspace/script.py")
print(f"Exit code: {result.exit_code}")
print(result.stdout)
# Download the results
await box.copy_out("/workspace/output.json", "/host/")
if __name__ == "__main__":
asyncio.run(main())
The Node.js SDK does not currently expose copyIn() / copyOut() methods. Use exec with shell commands to write files, and exec with cat to read them back.
import { SimpleBox } from '@boxlite-ai/boxlite';
async function main() {
const box = new SimpleBox({ image: 'python:slim' });
try {
// Create workspace and write a script using a heredoc
await box.exec('sh', '-c', `mkdir -p /workspace && cat > /workspace/script.py << 'EOF'
import json
data = {"result": 42}
print(json.dumps(data))
EOF`);
// Run the script
const result = await box.exec('python', '/workspace/script.py');
console.log(`Exit code: ${result.exitCode}`);
console.log(result.stdout);
// Read a file back from the box via exec
const output = await box.exec('cat', '/workspace/script.py');
console.log(output.stdout);
} finally {
await box.stop();
}
}
main();
When to use: Dynamic per-request files — scripts, input data, configuration that varies across runs.
Method 2: Volume mounts
Mount a host directory directly into the box. Files are shared in real time — no explicit copy step needed.
import asyncio
import boxlite
async def main():
async with boxlite.SimpleBox(
image="python:slim",
volumes=[
("/host/datasets", "/mnt/data", True), # Read-only input
("/host/results", "/mnt/results", False), # Writable output
],
) as box:
# Input data is already available at /mnt/data
result = await box.exec(
"python", "-c",
"import os; print(os.listdir('/mnt/data'))"
)
print(result.stdout)
# Write results — they appear on the host immediately
await box.exec(
"python", "-c",
"open('/mnt/results/output.txt', 'w').write('done')"
)
if __name__ == "__main__":
asyncio.run(main())
import { SimpleBox } from '@boxlite-ai/boxlite';
async function main() {
const box = new SimpleBox({
image: 'python:slim',
volumes: [
{ hostPath: '/host/datasets', guestPath: '/mnt/data', readOnly: true },
{ hostPath: '/host/results', guestPath: '/mnt/results' },
],
});
try {
const result = await box.exec(
'python', '-c',
"import os; print(os.listdir('/mnt/data'))"
);
console.log(result.stdout);
} finally {
await box.stop();
}
}
main();
When to use: Shared datasets or configuration that multiple boxes need access to. Use read-only mode for input data to prevent accidental modification.
Volume mounts give the guest direct access to host files. Always use read-only mode (True) for input data. Only use False for designated output directories.
Method 3: Inline data via base64
For small payloads (under ~1 MB), you can send data through a shell command without touching the filesystem.
import asyncio
import base64
import boxlite
async def main():
async with boxlite.SimpleBox(image="python:slim") as box:
# Encode a small script as base64
data = b"print('hello from transferred script')"
encoded = base64.b64encode(data).decode()
# Send and execute in one command
result = await box.exec(
"sh", "-c",
f"echo {encoded} | base64 -d > /tmp/script.py && python /tmp/script.py",
)
print(result.stdout)
if __name__ == "__main__":
asyncio.run(main())
When to use: Trivially small payloads where you want to avoid the overhead of copy_in. Not suitable for binary files or anything over ~1 MB.
Putting it together
Here’s a realistic workflow: upload a CSV, run analysis code on it, and download the results.
import asyncio
import boxlite
async def main():
async with boxlite.SimpleBox(
image="python:slim",
memory_mib=1024,
) as box:
# 1. Create workspace directory
await box.exec("mkdir", "-p", "/workspace")
# 2. Upload the input data
await box.copy_in("/host/sales_data.csv", "/workspace/")
# 3. Upload the analysis script
await box.copy_in("/host/analyze.py", "/workspace/")
# 4. Run the analysis
result = await box.exec("python", "/workspace/analyze.py")
print(f"Analysis completed (exit code: {result.exit_code})")
print(result.stdout)
# 5. Download the results
await box.copy_out("/workspace/report.json", "/host/")
print("Results saved to /host/report.json")
if __name__ == "__main__":
asyncio.run(main())
Use copy_in/copy_out for dynamic per-request files. Use volume mounts for shared datasets. Use inline base64 only for trivially small payloads.
What’s next?