Hermes Agent Terminal Backends: Comparing Local, Docker, SSH, Daytona, Singularity, and Modal

Hermes Agent supports six terminal backends—local, Docker, SSH, Daytona, Singularity, and Modal—each implemented as a BaseEnvironment subclass in tools/environments/ and selectable via the terminal.backend configuration setting, offering distinct trade-offs between isolation, persistence, and resource management.

Hermes Agent is an open-source AI agent framework developed by NousResearch that enables flexible code execution across diverse compute environments. The platform provides six distinct terminal backends that determine where and how shell commands execute, ranging from direct host execution to cloud-based sandboxes. Understanding the architectural differences between these backends is essential for configuring secure, persistent, and resource-efficient agent workflows.

Architecture Overview

All six backends inherit from the abstract BaseEnvironment class defined in tools/environments/base.py. The tools/terminal_tool.py module dispatches terminal commands to the appropriate environment implementation based on the terminal.backend setting in ~/.hermes/config.yaml. Each subclass implements an execute(command, cwd, timeout, stdin_data) method that returns a standardized {"output": ..., "returncode": ...} dictionary, along with a cleanup() method for resource management.

Local Backend: Direct Host Execution

The local backend runs commands directly on the host machine where Hermes Agent starts, using standard subprocess.Popen with a bash -lic wrapper. Located in tools/environments/local.py, this backend injects a fence marker (__HERMES_FENCE…) around commands to strip shell startup noise and supports interruption via the global tools.interrupt._interrupt_event.

This backend provides no containerization, running with the current user's privileges and full access to the host filesystem without sandboxing or resource limits. It is ideal for development scenarios requiring direct hardware access or minimal latency, but offers no security isolation.

Docker Backend: Hardened Containerization

The docker backend launches commands inside hardened Docker containers managed through tools/environments/docker.py. Security hardening includes --cap-drop ALL, --security-opt no-new-privileges, PID limits, and a read-only host root. Resource constraints are configurable via --cpus, --memory, and optional disk quotas (--storage-opt size=).

Persistence works through bind-mounts (/workspace and /root) when terminal.container_persistent is enabled; otherwise, the container uses --writable-tmpfs for ephemeral storage. The implementation leverages mini-swe-agent's DockerEnvironment class, starting containers once per task and stopping them on cleanup.

SSH Backend: Remote Execution

The ssh backend executes commands on remote machines via SSH, implemented in tools/environments/ssh.py. It establishes persistent connections using OpenSSH's ControlMaster=auto with a dedicated control socket stored in ~/.hermes/ssh/..., enabling connection reuse across multiple commands.

This backend provides no containerization—isolation depends entirely on the remote host's configuration. Interrupt handling terminates the local SSH process and sends a remote kill command through the control socket to terminate running processes on the remote host.

Daytona Backend: Cloud VM Sandboxes

The daytona backend provisions server-side Linux VMs through the Daytona platform, implemented in tools/environments/daytona.py. Using the official Daytona Python SDK, it creates sandboxes with configurable CPU, memory, and disk resources (capped at 10 GB).

When persistent_filesystem is enabled, the sandbox is stopped rather than destroyed during cleanup, allowing VM state and installed packages to survive across sessions. Commands execute via the SDK's exec method, wrapped in shell timeout commands to guarantee termination.

Singularity Backend: Apptainer Instances

The singularity backend (supporting Apptainer) runs commands within container instances using tools/environments/singularity.py. It applies security hardening via --containall (isolating PID, IPC, and mount namespaces) and --no-home, with capabilities dropped entirely. Resource limits (--cpus, --memory) require proper cgroup configuration.

Persistence is achieved through writable overlay directories (overlay-<task_id>) attached to instances when persistent_filesystem is enabled, surviving container restarts while keeping the base SIF image immutable.

The modal backend executes commands in Modal's serverless container environment through tools/environments/modal.py. It wraps SwerexModalEnvironment to provide cloud isolation with snapshot-based persistence: when container_persistent is true, cleanup() calls Modal's snapshot_filesystem API, storing the snapshot ID in ~/.hermes/modal_snapshots.json for restoration on subsequent runs.

Resource configuration passes through modal_sandbox_kwargs for CPU, memory, and disk allocation. Commands run in background threads to enable interrupt polling, with forced stops calling self._inner.stop().

Configuring Terminal Backends

Configure your preferred backend in ~/.hermes/config.yaml:

terminal:
  backend: docker
  cwd: .
  timeout: 180
  container_cpu: 2
  container_memory: 8192
  container_disk: 20480
  container_persistent: true
  docker_image: nikolaik/python-nodejs:python3.11-nodejs20

Change the backend value to local, docker, ssh, daytona, singularity, or modal to switch execution environments. Backend-specific options can be set programmatically:

from hermes_cli.config import set_config_value

set_config_value("terminal.backend", "ssh")
set_config_value("TERMINAL_SSH_HOST", "remote.server.com")
set_config_value("TERMINAL_SSH_USER", "ubuntu")

Filesystem Persistence Mechanisms

Each backend implements persistence differently based on its architecture:

  • Local: Uses the host filesystem directly with no sandboxing.
  • Docker: Uses bind-mounts (/workspace, /root) or --writable-tmpfs when persistence is disabled.
  • Daytona: Stops VMs rather than destroying them, preserving the entire filesystem state.
  • Singularity: Creates writable overlay directories that persist modifications while keeping the base image read-only.
  • Modal: Snapshots the filesystem to Modal's storage on cleanup, restoring from ~/.hermes/modal_snapshots.json on initialization.
  • SSH: Relies on the remote host's native filesystem with no automatic persistence handling.

Security Isolation Comparison

Security characteristics vary significantly across the six Hermes Agent terminal backends:

  • Docker (tools/environments/docker.py): Capability dropping (--cap-drop ALL), no-new-privileges, and read-only root provide strong container isolation.
  • Singularity (tools/environments/singularity.py): Namespace isolation (--containall) and capability dropping offer comparable security for HPC environments.
  • Daytona and Modal: Rely on cloud platform sandboxing with server-side isolation boundaries.
  • SSH: Depends entirely on the remote host's security configuration and user permissions.
  • Local: No isolation; runs with full host privileges and unrestricted filesystem access.

Summary

  • Hermes Agent provides six terminal backends selectable via terminal.backend in ~/.hermes/config.yaml, each implemented as a BaseEnvironment subclass in tools/environments/.
  • Local offers zero-overhead execution on the host but lacks isolation, while Docker and Singularity provide hardened containerization with optional resource limits through cgroup constraints.
  • SSH enables remote execution on existing infrastructure using persistent ControlMaster connections for performance and connection reuse.
  • Daytona and Modal provide cloud-native sandboxes with VM-level or serverless container isolation, supporting snapshot-based filesystem persistence across sessions.
  • Resource limits are enforced via Docker/Apptainer flags for local containers, or through platform APIs (Resources for Daytona, modal_sandbox_kwargs for Modal) for cloud backends.
  • All backends implement standardized execute() and cleanup() interfaces defined in tools/environments/base.py, ensuring consistent behavior across tools/terminal_tool.py invocations.

Frequently Asked Questions

Which Hermes Agent terminal backend offers the strongest security isolation?

The Docker and Singularity backends provide the strongest security isolation for sensitive workloads. According to the source code in tools/environments/docker.py, the Docker implementation applies hardened security flags including --cap-drop ALL, --security-opt no-new-privileges, and read-only root filesystem mounts. Similarly, tools/environments/singularity.py enforces --containall to isolate PID, IPC, and mount namespaces while dropping all capabilities. These measures prevent privilege escalation and limit attack surfaces compared to the local or SSH backends.

How does filesystem persistence work when switching between Daytona and Modal backends?

Daytona and Modal implement persistence through different mechanisms in their respective environment classes. In tools/environments/daytona.py, enabling persistent_filesystem stops the cloud VM rather than destroying it, preserving the entire filesystem state for subsequent sessions. Conversely, tools/environments/modal.py uses Modal's snapshot_filesystem API during cleanup() to create a filesystem snapshot stored in ~/.hermes/modal_snapshots.json, which is then restored when the environment initializes. Both methods survive agent restarts, but Daytona maintains a running VM while Modal serializes the filesystem to object storage.

Can I use the SSH backend with key-based authentication and connection multiplexing?

Yes, the SSH backend in tools/environments/ssh.py automatically configures connection multiplexing using OpenSSH's ControlMaster=auto setting with a persistent control socket stored in ~/.hermes/ssh/. This enables connection reuse across multiple command executions without re-authentication. The implementation uses standard SSH command construction, so it respects your ~/.ssh/config settings including IdentityFile configurations for key-based authentication, though you must configure the host and user via TERMINAL_SSH_HOST and TERMINAL_SSH_USER environment variables or configuration entries.

What happens if I interrupt a long-running command in the Modal backend?

The Modal backend handles interrupts through a background thread polling mechanism implemented in tools/environments/modal.py. When an interrupt signal is received, the environment terminates the command execution by calling self._inner.stop() on the wrapped SwerexModalEnvironment instance. This differs from the local backend, which uses a global tools.interrupt._interrupt_event, and the SSH backend, which sends a remote kill command through the control socket. The Modal implementation ensures cloud resources are properly released even when forcibly terminating commands.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →