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

> Compare Hermes Agent terminal backends: local, Docker, SSH, Daytona, Singularity, and Modal. Understand their trade-offs in isolation, persistence, and resource management for optimal selection.

- Repository: [Nous Research/hermes-agent](https://github.com/NousResearch/hermes-agent)
- Tags: comparison
- Published: 2026-03-09

---

**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`](https://github.com/NousResearch/hermes-agent/blob/main/tools/environments/base.py). The [`tools/terminal_tool.py`](https://github.com/NousResearch/hermes-agent/blob/main/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`](https://github.com/NousResearch/hermes-agent/blob/main/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`](https://github.com/NousResearch/hermes-agent/blob/main/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`](https://github.com/NousResearch/hermes-agent/blob/main/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`](https://github.com/NousResearch/hermes-agent/blob/main/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`](https://github.com/NousResearch/hermes-agent/blob/main/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.

## Modal Backend: Serverless Containers

The **modal** backend executes commands in Modal's serverless container environment through [`tools/environments/modal.py`](https://github.com/NousResearch/hermes-agent/blob/main/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`:

```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:

```python
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`](https://github.com/NousResearch/hermes-agent/blob/main/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`](https://github.com/NousResearch/hermes-agent/blob/main/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`](https://github.com/NousResearch/hermes-agent/blob/main/tools/environments/base.py), ensuring consistent behavior across [`tools/terminal_tool.py`](https://github.com/NousResearch/hermes-agent/blob/main/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`](https://github.com/NousResearch/hermes-agent/blob/main/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`](https://github.com/NousResearch/hermes-agent/blob/main/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`](https://github.com/NousResearch/hermes-agent/blob/main/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`](https://github.com/NousResearch/hermes-agent/blob/main/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`](https://github.com/NousResearch/hermes-agent/blob/main/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`](https://github.com/NousResearch/hermes-agent/blob/main/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.