Apptainer sandbox
A ready-to-run example is available here.
The Apptainer sandboxed agent server demonstrates how to run agents in isolated Apptainer containers using ApptainerWorkspace.
Apptainer (formerly Singularity) is a container runtime designed for HPC environments that doesn't require root access, making it ideal for shared computing environments, university clusters, and systems where Docker is not available.
When to use Apptainerโ
Use Apptainer instead of Docker when:
- Running on HPC clusters or shared computing environments
- Root access is not available
- Docker daemon cannot be installed
- Working in academic or research computing environments
- Security policies restrict Docker usage
Prerequisitesโ
Before running this example, ensure you have:
- Apptainer installed (Installation Guide)
- LLM API key set in environment
Basic Apptainer sandbox exampleโ
This example shows how to create an ApptainerWorkspace that automatically manages Apptainer containers for agent execution:
import os
import platform
import time
from pydantic import SecretStr
from faheemcode.sdk import (
LLM,
Conversation,
RemoteConversation,
get_logger,
)
from faheemcode.tools.preset.default import get_default_agent
from faheemcode.workspace import ApptainerWorkspace
logger = get_logger(__name__)
# 1) Ensure we have LLM API key
api_key = os.getenv("LLM_API_KEY")
assert api_key is not None, "LLM_API_KEY environment variable is not set."
llm = LLM(
usage_id="agent",
model=os.getenv("LLM_MODEL", "anthropic/claude-sonnet-4-5-20250929"),
base_url=os.getenv("LLM_BASE_URL"),
api_key=SecretStr(api_key),
)
def detect_platform():
"""Detects the correct platform string."""
machine = platform.machine().lower()
if "arm" in machine or "aarch64" in machine:
return "linux/arm64"
return "linux/amd64"
def get_server_image():
"""Get the server image tag, using PR-specific image in CI."""
platform_str = detect_platform()
arch = "arm64" if "arm64" in platform_str else "amd64"
# If GITHUB_SHA is set (e.g. running in CI of a PR), use that to ensure consistency
# Otherwise, use the latest image from main
github_sha = os.getenv("GITHUB_SHA")
if github_sha:
return f"ghcr.io/alsairy/faheem-code-agent-server:{github_sha[:7]}-python-{arch}"
return "ghcr.io/alsairy/faheem-code-agent-server:latest-python"
# 2) Create an Apptainer-based remote workspace that will set up and manage
# the Apptainer container automatically. Use `ApptainerWorkspace` with a
# pre-built agent server image.
# Apptainer (formerly Singularity) doesn't require root access, making it
# ideal for HPC and shared computing environments.
server_image = get_server_image()
logger.info(f"Using server image: {server_image}")
with ApptainerWorkspace(
# use pre-built image for faster startup
server_image=server_image,
host_port=8010,
platform=detect_platform(),
) as workspace:
# 3) Create agent
agent = get_default_agent(
llm=llm,
cli_mode=True,
)
# 4) Set up callback collection
received_events: list = []
last_event_time = {"ts": time.time()}
def event_callback(event) -> None:
event_type = type(event).__name__
logger.info(f"๐ Callback received event: {event_type}\n{event}")
received_events.append(event)
last_event_time["ts"] = time.time()
# 5) Test the workspace with a simple command
result = workspace.execute_command(
"echo 'Hello from sandboxed environment!' && pwd"
)
logger.info(
f"Command '{result.command}' completed with exit code {result.exit_code}"
)
logger.info(f"Output: {result.stdout}")
conversation = Conversation(
agent=agent,
workspace=workspace,
callbacks=[event_callback],
)
assert isinstance(conversation, RemoteConversation)
try:
logger.info(f"\n๐ Conversation ID: {conversation.state.id}")
logger.info("๐ Sending first message...")
conversation.send_message(
"Read the current repo and write 3 facts about the project into FACTS.txt."
)
logger.info("๐ Running conversation...")
conversation.run()
logger.info("โ
First task completed!")
logger.info(f"Agent status: {conversation.state.execution_status}")
# Wait for events to settle (no events for 2 seconds)
logger.info("โณ Waiting for events to stop...")
while time.time() - last_event_time["ts"] < 2.0:
time.sleep(0.1)
logger.info("โ
Events have stopped")
logger.info("๐ Running conversation again...")
conversation.send_message("Great! Now delete that file.")
conversation.run()
logger.info("โ
Second task completed!")
# Report cost (must be before conversation.close())
cost = conversation.conversation_stats.get_combined_metrics().accumulated_cost
print(f"EXAMPLE_COST: {cost}")
finally:
print("\n๐งน Cleaning up conversation...")
conversation.close()
You can run the example code as-is.
export LLM_API_KEY="your-api-key"
export LLM_MODEL="anthropic/claude-sonnet-4-5-20250929" # or openai/gpt-4o, etc.
cd software-agent-sdk
uv run python examples/02_remote_agent_server/08_convo_with_apptainer_sandboxed_server.py
# https://app.faheemcode.ai/settings/api-keys
export LLM_API_KEY="example-user-api-key"
export LLM_MODEL="faheemcode/claude-sonnet-4-5-20250929"
cd software-agent-sdk
uv run python examples/02_remote_agent_server/08_convo_with_apptainer_sandboxed_server.py
Configuration optionsโ
The ApptainerWorkspace supports several configuration options:
Option 1: pre-built image (recommended)โ
Use a pre-built agent server image for fastest startup:
with ApptainerWorkspace(
server_image="ghcr.io/alsairy/faheem-code-agent-server:main-python",
host_port=8010,
) as workspace:
# Your code here
Option 2: build from base imageโ
Build from a base image when you need custom dependencies:
with ApptainerWorkspace(
base_image="nikolaik/python-nodejs:python3.12-nodejs22",
host_port=8010,
) as workspace:
# Your code here
Option 3: use existing SIF fileโ
If you have a pre-built Apptainer SIF file:
with ApptainerWorkspace(
sif_file="/path/to/your/agent-server.sif",
host_port=8010,
) as workspace:
# Your code here
Key featuresโ
Rootless container executionโ
Apptainer runs completely without root privileges:
- No daemon process required
- User namespace isolation
- Compatible with most HPC security policies
Image cachingโ
Apptainer automatically caches container images:
- First run builds/pulls the image
- Subsequent runs reuse cached SIF files
- Cache location:
~/.cache/apptainer/
Port mappingโ
The workspace exposes ports for agent services:
with ApptainerWorkspace(
server_image="ghcr.io/alsairy/faheem-code-agent-server:main-python",
host_port=8010, # Maps to container port 8010
) as workspace:
# Access agent server at http://localhost:8010
Differences from Dockerโ
While the API is similar to DockerWorkspace, there are some differences:
| Feature | Docker | Apptainer |
|---|---|---|
| Root access required | Yes (daemon) | No |
| Installation | Requires Docker Engine | Single binary |
| Image format | OCI/Docker | SIF |
| Build speed | Fast (layers) | Slower (monolithic) |
| HPC compatibility | Limited | Excellent |
| Networking | Bridge/overlay | Host networking |
Troubleshootingโ
Apptainer not foundโ
If you see apptainer: command not found:
- Install Apptainer following the official guide
- Ensure it's in your PATH:
which apptainer
Permission errorsโ
Apptainer should work without root. If you see permission errors:
- Check that your user has access to
/tmp - Verify Apptainer is properly installed:
apptainer version - Ensure the cache directory is writable:
ls -la ~/.cache/apptainer/
Next stepsโ
- Docker Sandbox - Alternative container runtime
- API Sandbox - Remote API-based sandboxing
- Local Server - Non-sandboxed local execution