Use this file to discover all available pages before exploring further.
Sandbox images allow you to create customized & reusable sandbox environments. They define the tools, languages, frameworks, and configurations that will be available when you spawn a new sandbox instance.Images are particularly useful for teams who need standardized environments or for creating many specialized sandboxes for repeated use cases (codegen agent, Git PR reviews agent, etc.).
Initial setup: Follow this guide to create your sandbox image for the first time. Use bl push to push the image to Blaxel, or bl deploy to push the image and also create a sandbox from it.
Build phase: Your Dockerfile is used to create a container with all required tools and configurations.
Initialization phase: The sandbox API is injected and startup commands are executed.
Instantiation: New sandboxes can be spawned from this image in seconds.
You cannot directly use “library” container images (such as those hosted on Docker Hub and other registries) as sandbox images. Instead, you must create one or more custom images for your sandboxes using Dockerfiles and ensure that each image includes Blaxel’s sandbox API binary. This is necessary for sandbox functionality like process management and file operations.
The Dockerfile is the heart of your image. It defines what will be available in your sandbox environment.
# Choose a base imageFROM node:22-alpine# Set working directoryWORKDIR /app# Copy sandbox API (required)COPY --from=ghcr.io/blaxel-ai/sandbox:latest /sandbox-api /usr/local/bin/sandbox-api# Install system dependenciesRUN apk update && apk add --no-cache \ git curl python3 make g++ netcat-openbsd \ && rm -rf /var/cache/apk/*# Copy and set up entrypointCOPY entrypoint.sh /entrypoint.shRUN chmod +x /entrypoint.shENTRYPOINT ["/entrypoint.sh"]
Always include the sandbox-api binary from the Blaxel base image. This is required for sandbox functionality like process management and file operations.
3. Configure image settings
The blaxel.toml file defines your image’s runtime configuration:
name = "mytemplate"type = "sandbox"description = "Full-stack development environment with Node.js and Python"[runtime]generation = "mk3"memory = 8192 # 8GB RAM# Define exposed ports[[runtime.ports]]name = "dev-server"target = 3000protocol = "tcp"[[runtime.ports]]name = "another-api"target = 8888protocol = "tcp"# Set environment variables[env]NODE_ENV = "development"PYTHON_ENV = "development"
Currently, it is not possible to add or update environment variables for a sandbox after it is created. Ensure that any required environment variables are defined in your Dockerfile, your blaxel.toml file, or at sandbox creation time using the Blaxel SDKs.
4. Define initialization
The entrypoint.sh script runs when a sandbox is created from this image:
#!/bin/sh# Start the sandbox API (required)/usr/local/bin/sandbox-api &# Wait for sandbox API to be readyecho "Waiting for sandbox API..."while ! nc -z 127.0.0.1 8080; do sleep 0.1doneecho "Sandbox API ready"# Initialize your environmentecho "Setting up development environment..."# Example: Start a development server in the backgroundif [ -f /app/package.json ]; then cd /app npm install # Execute curl command, we execute it through the sandbox-api so that you can access logs, # process status and everything you can do with the sandbox api echo "Running Next.js dev server..." curl http://127.0.0.1:8080/process -X POST -d '{"workingDir": "/app", "command": "npm run dev", "waitForCompletion": false}' -H "Content-Type: application/json"fi# Keep the container runningwait
5. Build and test locally
Before pushing the image to Blaxel, test it locally:
# Build the Docker imagemake build# Run locally to testmake run# Access your sandbox-api on exposed ports# e.g., http://127.0.0.1:8080# Example: curl http://127.0.0.1:8080/process
6. Push the image
Once satisfied with your configuration, push the image to Blaxel:
bl push
This will:
Build your Docker image
Push it to Blaxel’s registry (private to your workspace)
Return an image ID you can use for creating sandboxes
Use bl deploy instead if you also want Blaxel to automatically create a first sandbox from this image, so you can test it:
bl deploy
You can monitor the sandbox deployment with:
bl get sandbox mytemplate --watch
You can safely delete the sandbox afterwards, and keep using the image for new sandboxes.
The Blaxel SDK includes a declarative image builder that lets you define custom sandbox images directly in your code using a fluent, chainable API. Instead of writing Dockerfiles manually, you describe your image step by step, and the SDK generates the Dockerfile, builds the image, and deploys it as a sandbox.This approach is ideal for dynamic image definitions, CI/CD pipelines, or when your image configuration depends on runtime logic.
The Declarative Image Builder is available in the TypeScript and Python SDKs. The Go SDK does not support this feature.
Define: Use the fluent API to describe your image starting from a base image.
Chain: Each method returns a new immutable ImageInstance, so you can branch and reuse configurations.
Build: Call build() to generate a Dockerfile, package it with any local files, upload it to Blaxel, and deploy it as a sandbox.
The SDK automatically injects the Blaxel sandbox API binary and sets a default entrypoint (if you haven’t specified one), so your image is ready to use as a sandbox without manual configuration.
Select a base image
Start by selecting a base image from any Docker registry:
import { ImageInstance } from "@blaxel/core";const image = ImageInstance.fromRegistry("ubuntu:22.04");
The SDK provides convenience methods for common package managers. These generate optimized RUN commands with sensible defaults (e.g., cache cleanup for apt).
Local files are resolved to absolute paths, included in the build context, and COPY-ed into the image. You can optionally provide a contextName (TypeScript) / context_name (Python) parameter to control the filename in the build context.
const sandbox = await image.build({ name: "my-sandbox", memory: 8192, // Memory in MB (default: 4096) timeout: 900000, // Timeout in ms (default: 900000 = 15 min) — NOTE: Python SDK uses seconds sandboxVersion: "latest", // Sandbox API version onStatusChange: (status) => { console.log(`Build status: ${status}`); },});
The build() method automatically:
Injects the Blaxel sandbox API binary
Sets a default entrypoint if none was specified
Packages the Dockerfile and local files into a ZIP archive
Uploads and deploys the image as a sandbox
Polls until the sandbox reaches DEPLOYED or FAILED status
Inspect the generated Dockerfile
You can preview the Dockerfile without building:
const image = ImageInstance.fromRegistry("python:3.11-slim") .aptInstall("git") .workdir("/app") .pipInstall("requests");console.log(image.dockerfile);// FROM python:3.11-slim// RUN apt-get update && apt-get install -y --no-install-recommends git && rm -rf /var/lib/apt/lists/*// WORKDIR /app// RUN pip install requests
Write to disk
You can also write the image to a folder for manual inspection or use with bl deploy:
// Write to a specific directoryconst buildDir = image.write("./output", "my-image");// Write to a temporary directoryconst tempDir = image.writeTemp();
Understand immutability and branching
Each method returns a new ImageInstance, leaving the original unchanged. This lets you create base images and branch from them:
// Define a shared baseconst base = ImageInstance.fromRegistry("python:3.11-slim") .aptInstall("git", "curl") .workdir("/app");// Branch for different use casesconst mlImage = base .pipInstall("torch", "transformers", "datasets") .env({ CUDA_VISIBLE_DEVICES: "0" });const webImage = base .pipInstall("fastapi", "uvicorn", "sqlalchemy") .expose(8000) .entrypoint("python", "-m", "uvicorn", "main:app", "--host", "0.0.0.0");// Build both independentlyconst mlSandbox = await mlImage.build({ name: "ml-sandbox", memory: 16384 });const webSandbox = await webImage.build({ name: "web-sandbox", memory: 4096 });
Example script
A complete example building a Node.js / Python development sandbox:
Although less common, it is also possible to create a sandbox image and sandbox by directly interacting with the Blaxel API.Ensure that you have the following:
The workspace name, found at the bottom left corner of the Blaxel Console or via bl workspaces
1. Create a ZIP archive
Create a directory with the following project contents:
Dockerfile (required) - Defines your custom sandbox image and must include sandbox-api
Any custom scripts (e.g., entrypoint.sh for initialization logic)
Configuration files or data files as needed
Additional dependencies or binaries your sandbox requires
Here is an example of the expected project structure:
mytemplate/├── Dockerfile # Required - defines your image└── entrypoint.sh # Optional - for custom initialization
The Dockerfile is the heart of your image. It defines what will be available in your sandbox environment. See an example.
The entrypoint.sh script runs when a sandbox is created from this image. See an example.
The Dockerfile can reference and use any files included in the ZIP archive. Everything gets extracted and built together as a Docker image.Once the files are ready, create a ZIP archive containing the files:
(cd mytemplate && zip -r ../mytemplate.zip .)
2. Create a sandbox resource
Set your Blaxel API key and workspace as environment variables:
Note the upload=true query parameter in the request, which indicates intent to upload custom code.The Blaxel API returns a JSON response. The response contains an x-blaxel-upload-url response header, containing the target URL to use when uploading your image. The URL is in the response headers, not the JSON body.Example response:
Use an HTTP PUT request to upload the ZIP file to the upload URL. Replace the placeholder URL in the command below with the value of the x-blaxel-upload-url response header received earlier.
The upload is successful when you receive a 200 OK status code.
4. Monitor deployment status
After uploading, poll the sandbox status endpoint to track the build and deployment progress.Make a GET request to https://api.blaxel.ai/v0/sandboxes/<SANDBOX-NAME>, where SANDBOX-NAME is the metadata.name specified in the initial POST request.
The status field of the response will progress through these values:
Status
Description
UPLOADING
Code archive is being uploaded
BUILDING
Docker image is being built
DEPLOYING
Container is being deployed to the cluster
DEPLOYED
Sandbox is ready to use
FAILED
Deployment failed (check build logs)
DEACTIVATED
Sandbox has been deactivated
Continue polling every 3-5 seconds until the status reaches DEPLOYED or FAILED.A first sandbox from that image is automatically created on Blaxel once deployment succeeds. You can safely delete the sandbox and keep using the image for new sandboxes.
Example script
Here’s a complete example script that performs all the steps above:
#!/bin/bash# Real deployment script that executes the documented API workflowset -e# ConfigurationSANDBOX_NAME="my-custom-sandbox-$(date +%s)"SOURCE_DIR="mytemplate"ZIP_FILE="mytemplate.zip"BASE_URL="https://api.blaxel.ai/v0"# Colors for outputGREEN='\033[0;32m'RED='\033[0;31m'YELLOW='\033[1;33m'BLUE='\033[0;34m'NC='\033[0m' # No Color# Cleanup functioncleanup() { if [ -f "$ZIP_FILE" ]; then rm -f "$ZIP_FILE" echo -e "\n${GREEN}✓ Cleaned up temporary files${NC}" fi}# Set trap to cleanup on exittrap cleanup EXIT# Validate credentialsif [ -z "$BL_API_KEY" ]; then echo -e "${RED}Error: BL_API_KEY not set${NC}" echo "Get your API key from workspace settings and set it with:" echo " export BL_API_KEY='your-api-key'" exit 1fiif [ -z "$BL_WORKSPACE" ]; then echo -e "${RED}Error: BL_WORKSPACE not set${NC}" echo "Set your workspace name with:" echo " export BL_WORKSPACE='your-workspace-name'" exit 1fi# Check if source directory existsif [ ! -d "$SOURCE_DIR" ]; then echo -e "${RED}Error: $SOURCE_DIR directory not found${NC}" exit 1fi# Create ZIP archiveecho -e "${BLUE}Creating ZIP archive from $SOURCE_DIR...${NC}"cd "$SOURCE_DIR"zip -q -r "../${ZIP_FILE}" .cd ..FILE_SIZE=$(wc -c < "$ZIP_FILE" | tr -d ' ')echo -e "${GREEN}✓ ZIP archive created: $ZIP_FILE ($FILE_SIZE bytes)${NC}\n"# Step 1: Create sandbox and get upload URLecho -e "${BLUE}[1/4] Creating sandbox '$SANDBOX_NAME'...${NC}"# Create temporary file for headersHEADERS_FILE=$(mktemp)CREATE_RESPONSE=$(curl -s -D "$HEADERS_FILE" -w "\n%{http_code}" -X POST "$BASE_URL/sandboxes?upload=true" \ -H "Authorization: Bearer $BL_API_KEY" \ -H "X-Blaxel-Workspace: $BL_WORKSPACE" \ -H "Content-Type: application/json" \ -d '{ "apiVersion": "blaxel.ai/v1alpha1", "kind": "Sandbox", "metadata": { "name": "'"$SANDBOX_NAME"'" }, "spec": { "runtime": { "memory": 2048 }, "region": "us-pdx-1" } }')HTTP_CODE=$(echo "$CREATE_RESPONSE" | tail -n 1)RESPONSE_BODY=$(echo "$CREATE_RESPONSE" | sed '$d')if [ "$HTTP_CODE" != "200" ] && [ "$HTTP_CODE" != "201" ]; then echo -e "${RED}✗ Failed to create sandbox (HTTP $HTTP_CODE)${NC}" echo "$RESPONSE_BODY" | jq . 2>/dev/null || echo "$RESPONSE_BODY" rm -f "$HEADERS_FILE" exit 1fi# Extract upload URL from response headersUPLOAD_URL=$(grep -i "x-blaxel-upload-url:" "$HEADERS_FILE" | cut -d' ' -f2- | tr -d '\r\n')if [ -z "$UPLOAD_URL" ]; then echo -e "${RED}✗ No upload URL received in response headers${NC}" rm -f "$HEADERS_FILE" exit 1firm -f "$HEADERS_FILE"echo -e "${GREEN}✓ Sandbox created${NC}"echo ""# Step 2: Upload ZIP fileecho -e "${BLUE}[2/4] Uploading code archive ($FILE_SIZE bytes)...${NC}"UPLOAD_RESPONSE=$(curl -s -w "%{http_code}" -X PUT "$UPLOAD_URL" \ -H "Content-Type: application/zip" \ --data-binary "@$ZIP_FILE")HTTP_CODE="${UPLOAD_RESPONSE: -3}"if [ "$HTTP_CODE" != "200" ]; then echo -e "${RED}✗ Upload failed with status $HTTP_CODE${NC}" exit 1fiecho -e "${GREEN}✓ Upload completed${NC}"echo ""# Step 3: Monitor deployment statusecho -e "${BLUE}[3/4] Monitoring deployment status...${NC}"MAX_WAIT=900 # 15 minutesSTART_TIME=$(date +%s)LAST_STATUS=""while true; do # Check timeout CURRENT_TIME=$(date +%s) ELAPSED=$((CURRENT_TIME - START_TIME)) if [ $ELAPSED -gt $MAX_WAIT ]; then echo -e "${RED}✗ Deployment timed out after $MAX_WAIT seconds${NC}" exit 1 fi # Get current status STATUS_RESPONSE=$(curl -s -X GET "$BASE_URL/sandboxes/$SANDBOX_NAME" \ -H "Authorization: Bearer $BL_API_KEY" \ -H "X-Blaxel-Workspace: $BL_WORKSPACE") STATUS=$(echo "$STATUS_RESPONSE" | jq -r '.status // empty') if [ -z "$STATUS" ]; then echo -e "${YELLOW}Warning: Could not get status, retrying...${NC}" sleep 3 continue fi # Log status changes if [ "$STATUS" != "$LAST_STATUS" ]; then echo " Status: $STATUS" LAST_STATUS=$STATUS fi # Check terminal states if [ "$STATUS" = "DEPLOYED" ]; then IMAGE=$(echo "$STATUS_RESPONSE" | jq -r '.spec.runtime.image // empty') echo "" echo -e "${GREEN}🎉 Deployment complete!${NC}" echo "Sandbox: $SANDBOX_NAME" echo "Image: $IMAGE" echo "" # Step 4: Show how to use it echo -e "${BLUE}[4/4] How to use your sandbox:${NC}" echo "" echo "Run a command:" echo " bl run sandbox $SANDBOX_NAME" echo "" echo "Get sandbox details:" echo " bl get sandbox $SANDBOX_NAME" echo "" echo "View logs:" echo " bl logs sandbox $SANDBOX_NAME" echo "" echo "Delete sandbox:" echo " bl delete sandbox $SANDBOX_NAME" echo "" exit 0 elif [ "$STATUS" = "FAILED" ]; then echo "" echo -e "${RED}✗ Deployment failed${NC}" echo "" echo "Check build logs with:" echo " curl -X GET '$BASE_URL/sandboxes/$SANDBOX_NAME/build-logs' \\" echo " -H 'Authorization: Bearer \$BL_API_KEY' \\" echo " -H 'X-Blaxel-Workspace: \$BL_WORKSPACE'" exit 1 elif [ "$STATUS" = "DEACTIVATED" ] || [ "$STATUS" = "DEACTIVATING" ] || [ "$STATUS" = "DELETING" ]; then echo "" echo -e "${RED}✗ Unexpected status: $STATUS${NC}" exit 1 fi # Wait before next poll sleep 3done
The script accepts a source directory path containing the Dockerfile and related code and uses it to deploy a sandbox on Blaxel. It monitors the deployment status until completion and also cleans up temporary files.To use this script, first export your API key and credentials as below:
Once an image is successfully pushed, you can spawn new sandboxes instantly by using its image ID.Use the following command to retrieve the image ID of the most recently pushed image:
# Retrieve your IMAGE_IDbl get image sandbox/mytemplate --latest
Once you have a custom image, you can share it with other workspaces in the same account, or with other accounts - for example, to promote an image from development to production without rebuilding or re-pushing, or to share an image between different teams. Only the metadata record is copied to the target workspace or account; the underlying storage stays in the source.When you share an image:
The metadata is copied to the target workspace or account, pointing to the same underlying data in the source.
The target workspace or account can use the shared image to create sandboxes, just like a locally-owned image.
Storage billing stays with the source workspace or account.
New tags pushed to the source image are automatically propagated to all workspaces and accounts it is shared with.
Shared images in the consuming workspace or account include a sourceWorkspace field. In the Blaxel Console, they are marked with a badge showing the source workspace or account name.
When sharing between workspaces or accounts, you must be an administrator of the source workspace. When sharing between accounts, your share request will be completed only when it receives approval from an administrator of the target account.
The image must be locally owned in the source workspace or account. You cannot re-share an image that was shared to you from elsewhere.
Currently, the Blaxel CLI only supports image sharing across workspaces. To share images across accounts, use the Blaxel Console or the Management API instead.
On the image detail page in the Blaxel Console, images shared across workspaces or accounts are listed with the option to revoke sharing for each workspace or account individually.
Unsharing removes the metadata record from the target workspace or account. The image data in the source workspace is not affected.You cannot delete an image (or individual tags) while it is shared. You must unshare from all connected workspaces or accounts first.
After unsharing, any deployments in the target workspace or account that reference the shared image will fail on their next restart or scale-up. Make sure no active deployments depend on the image before unsharing.
Storage billing is tied to the source workspace. Metering and costs remain with the workspace that originally pushed the image, regardless of how many workspaces or accounts it is shared with.The consuming workspace or account pays nothing for image storage of shared images.
No re-sharing: A shared image cannot be re-shared from the consuming workspace to a third workspace or account. Only the original owner can share.
Deletion protection: You cannot delete an image (or individual tags) while it is shared with other workspaces or accounts. You must unshare from all target workspaces first.
Whole image only: Sharing applies to the entire image with all its tags. You cannot share individual tags.
Admin-only: Both sharing and unsharing require administrator permissions.