Install Hermes Agent with Docker, OpenAI Codex and Signal on Ubuntu

Install Hermes Agent with Docker, OpenAI Codex and Signal on Ubuntu

Running an AI assistant on your own server opens up interesting possibilities. Instead of interacting with AI exclusively through a browser, you can connect it to messaging applications, local development tools, and automated workflows.

In this tutorial, I’ll explain how I deployed Nous Research Hermes Agent using Docker Compose on Ubuntu, configured authentication through OpenAI Codex, enabled its web dashboard, and connected it to my existing Signal account.

The final result is a self-hosted AI agent that I can communicate with directly from my phone using Signal’s Note to Self conversation.

I’ll also cover the problems I encountered during installation and how I resolved them.

1. What Is Hermes Agent?

Hermes Agent is an open-source AI agent developed by Nous Research.

Unlike a conventional chatbot, an agent can use tools and execute multi-step tasks. Depending on its configuration and permissions, it can interact with the filesystem, run shell commands, work with source code, and use external services.

For this installation, I used:

  • Ubuntu Linux as the host operating system.
  • Docker and Docker Compose to run the services.
  • Hermes Agent as the AI agent and messaging gateway.
  • OpenAI Codex authentication to access an AI model.
  • Hermes Dashboard for browser-based administration.
  • signal-cli to integrate with Signal Messenger.

The main advantage is that Hermes runs continuously on my Ubuntu machine while I can communicate with it remotely through Signal.

2. Solution Architecture

The setup consists of two Docker containers running on the same Docker Compose network.

 Android Phone
                  Signal Messenger
                         |
                         | Encrypted Signal messages
                         |
                  Signal Network
                         |
                         v
              +----------------------+
              | signal-cli           |
              | Native HTTP daemon   |
              | Port 8080            |
              +----------------------+
                         |
                         | Docker internal network
                         |
                         v
              +----------------------+
              | Hermes Agent         |
              | Gateway + AI Tools   |
              | Dashboard: 9119      |
              +----------------------+
                         |
                         v
                 OpenAI Codex
                 AI model access

The signal-cli service handles Signal communication, while Hermes processes incoming messages and generates responses.

The Signal HTTP port is not published to the host network. Only the Hermes Dashboard is exposed on the configured host interface.

3. Prerequisites

Before starting, make sure you have:

  • Ubuntu with Docker Engine and Docker Compose installed.
  • A working Signal account on your phone.
  • Access to OpenAI Codex authentication.
  • A terminal with permission to run Docker commands.
  • A persistent directory for storing Hermes configuration.

Verify Docker:

docker --version
docker compose version

Create a working directory:

mkdir -p ~/PROJECTS/hermes-docker
cd ~/PROJECTS/hermes-docker

4. Configure Docker Compose

The deployment uses two services: hermes and signal.

The following is a consolidated example based on my working installation. The exact Hermes startup settings should be checked against the image version being deployed.

Create compose.yaml:

services:
  hermes:
    image: nousresearch/hermes-agent:latest
    container_name: hermes
    restart: unless-stopped
    stdin_open: true
    tty: true
    environment:
      HERMES_DASHBOARD: "1"
    volumes:
      - ./data:/opt/data
    ports:
      - "192.168.1.104:9119:9119"

  signal:
    image: registry.gitlab.com/packaging/signal-cli/signal-cli-native:latest
    container_name: hermes-signal
    restart: unless-stopped
    command:
      - daemon
      - --http
      - 0.0.0.0:8080
    volumes:
      - ./signal-native-data:/var/lib/signal-cli
    tmpfs:
      - /tmp:exec

Replace 192.168.1.104 with your Ubuntu server’s LAN address.

The data directory stores Hermes configuration, while signal-native-data stores Signal’s account data.

For production deployments, consider pinning tested image versions rather than using latest.

Start Hermes:

docker compose up -d hermes

Check its status:

docker compose ps
docker compose logs --tail=50 hermes

5. Configure OpenAI Codex Authentication

Hermes needs access to an AI model.

In my installation, I used the OpenAI Codex authentication flow associated with my ChatGPT account.

Start an interactive Hermes configuration session:

docker compose run --rm --no-deps hermes

Follow the available authentication/setup flow for the installed Hermes version and select the OpenAI Codex provider.

The device authentication process may require opening a URL in a browser and authorizing the application.

Once authentication is complete, test the agent:

docker compose run --rm --no-deps hermes \
  chat -q "Reply with: Hermes is online."

In my installation, Hermes returned:

Hermes is online.

This confirmed that the agent could successfully authenticate and generate a response.

6. Enable the Hermes Dashboard

Hermes provides a web dashboard for managing configuration and messaging channels.

In Docker Compose, enable it with:

environment:
  HERMES_DASHBOARD: "1"

The dashboard uses port 9119.

However, I encountered an issue: Hermes refused to listen on 0.0.0.0 without an authentication provider.

The solution was to configure dashboard Basic Authentication in Hermes.

The configuration contains a dashboard section similar to:

dashboard:
  basic_auth:
    username: YOUR_USERNAME
    password_hash: "scrypt$..."

Use the Hermes configuration commands to generate and store a supported password hash. Do not store a plaintext password in the article or expose the hash publicly.

An important troubleshooting detail: my initial Compose configuration also contained dashboard authentication environment variables. These conflicted with the authentication configuration stored in Hermes.

Removing the conflicting variables and recreating the container resolved the problem.

docker compose up -d --force-recreate hermes

I could then open:

http://192.168.1.104:9119

and access the dashboard.

For access over SSH without exposing the dashboard to the LAN, an alternative is to bind the dashboard to 127.0.0.1 and forward the port:

ssh -N -L 9119:127.0.0.1:9119 \
  YOUR_USER@YOUR_SERVER_IP

Then open http://127.0.0.1:9119 on the client computer.

7. Install the Signal HTTP Daemon

This was the most interesting part of the installation.

My goal was to use my existing Signal account, rather than registering a separate phone number for the AI agent.

The solution is to link signal-cli as an additional Signal device.

Choosing the correct Docker image

Initially, I tried:

bbernhard/signal-cli-rest-api:latest

The container started successfully, but Hermes could not connect to it.

Testing its HTTP API revealed:

/v1/about       HTTP 200
/api/v1/check   HTTP 404

After inspecting Hermes’ Signal plugin, I discovered that it expects these endpoints:

EndpointPurpose
/api/v1/checkConnection check
/api/v1/eventsReceive Signal events
/api/v1/rpcExecute Signal operations

The REST API wrapper did not expose the expected interface.

I replaced it with:

registry.gitlab.com/packaging/signal-cli/signal-cli-native:latest

This image runs the native signal-cli daemon and supports the HTTP interface expected by Hermes.

Configure permissions

The native image runs as:

uid=101(signal-cli)
gid=101(signal-cli)

However, Docker initially created the persistent data directory as root:root.

This caused repeated errors:

Failed to read local accounts list

I fixed the directory ownership:

sudo chown -R 101:101 signal-native-data
sudo chmod -R u+rwX signal-native-data

Then started the service:

docker compose up -d signal

The logs confirmed that the daemon started:

INFO DaemonCommand - Starting daemon in multi-account mode
INFO HttpServerHandler - Started HTTP server on /0.0.0.0:8080

The daemon warned that HTTP authentication was disabled. Therefore, port 8080 should remain accessible only through the trusted internal Docker network, not publicly exposed.

8. Verify Connectivity Between Hermes and Signal

Before linking a Signal account, verify that Hermes can reach the daemon.

Execute this Python command inside the Hermes container:

docker compose exec hermes python -c '
import urllib.request

url = "http://signal:8080/api/v1/check"

with urllib.request.urlopen(url, timeout=10) as response:
    print("HTTP:", response.status)
'

My result:

HTTP: 200

This was an important milestone: Hermes could now communicate with the correct Signal HTTP API.

9. Link Your Existing Signal Account

Instead of registering another Signal account, I linked the daemon as a companion device.

Run:

docker compose exec signal \
  signal-cli link -n HermesAgent

The command generates a temporary URI beginning with:

sgnl://linkdevice?uuid=...

This URI can be converted into a QR code.

On Ubuntu, install:

sudo apt install qrencode

Then generate the QR code using the full URI:

qrencode -t ANSIUTF8 'YOUR_SIGNAL_LINK_URI'

On your phone:

  1. Open Signal.
  2. Navigate to Settings → Linked Devices.
  3. Select the option to link a new device.
  4. Scan the generated QR code.
  5. Approve the new device.

After linking, check the registered accounts:

docker compose exec signal signal-cli listAccounts

Your Signal account should appear in the output.

10. Configure Signal in Hermes Dashboard

Open Hermes Dashboard and navigate to:

Channels → Signal

Configure the following settings:

SettingValue
signal-cli HTTP URLhttp://signal:8080
Signal AccountYour Signal phone number
Allowed UsersYour Signal phone number
Allow All UsersDisabled

Save and enable the Signal channel.

Restart Hermes if required:

docker compose restart hermes

Because both services share the same Docker Compose network, Hermes can resolve signal using Docker’s internal DNS.

You do not need to publish Signal’s HTTP port on the Ubuntu host.

11. Test the Complete Integration

Now it’s time to verify the full communication path.

Open Signal on your phone and navigate to Note to Self.

Send:

Hello Hermes, are you online?

In my setup, Hermes successfully responded through Signal.

This confirmed the complete integration:

  1. Signal received the message.
  2. signal-cli made it available to Hermes.
  3. Hermes processed the request using its configured AI provider.
  4. The response was sent back through Signal.

You can also inspect Hermes logs:

docker compose logs --tail=80 hermes

Or filter Signal-related messages:

docker compose logs --tail=80 hermes | grep -i signal

12. Troubleshooting Summary

Here are the most important problems I encountered.

ProblemCauseSolution
Dashboard refuses to startMissing authentication providerConfigure dashboard Basic Auth
Dashboard authentication failsConflicting environment variablesRemove conflicting variables and recreate Hermes
Signal shows unhealthyData directory permission errorsCorrect volume ownership
Signal returns HTTP 404Incompatible REST API wrapperUse native signal-cli HTTP daemon
Signal repeatedly restartsUID/GID mismatchSet directory owner to 101:101
Hermes cannot reach SignalIncorrect URL or daemon unavailableUse http://signal:8080 and test /api/v1/check

These checks helped isolate problems one layer at a time: Docker container status, filesystem permissions, HTTP connectivity, API compatibility, and finally Signal account linking.

13. Security Considerations

A self-hosted AI agent can potentially execute commands and access local resources. It is important to limit its permissions.

For this setup, I recommend:

  • Keep the Signal HTTP API private to the Docker network.
  • Require authentication for Hermes Dashboard.
  • Restrict Signal’s Allowed Users list to trusted accounts.
  • Protect persistent data directories and authentication credentials.
  • Avoid granting unnecessary host filesystem access.
  • Do not mount the Docker socket unless there is a specific, understood need.
  • Review agent tool permissions before allowing shell execution or access to sensitive repositories.
  • Back up persistent configuration securely.
  • Keep the Docker images updated after testing new releases.

14. What’s Next?

Now that Hermes is accessible through Signal, there are several interesting possibilities.

As a software test automation engineer, I’m particularly interested in integrating Hermes with development and QA workflows.

For example, an AI agent could potentially:

  • Execute Python and Pytest test suites.
  • Analyze CI/CD failures.
  • Summarize test results.
  • Inspect application logs.
  • Work with Git repositories.
  • Generate reports and send summaries to Signal.

These capabilities require additional tool configuration and appropriate access controls; they are not automatically enabled by the Signal integration.

A possible next project is to let Hermes execute a selected Pytest suite and return a concise test execution summary directly to my phone.

Conclusion

Deploying Hermes Agent with Docker and integrating it with Signal provides a convenient way to access a self-hosted AI assistant.

The most challenging part of my setup was not Docker itself, but getting the correct Signal HTTP interface and filesystem permissions configured.

Once those issues were resolved, the integration worked as expected.

The result is a persistent AI agent running on Ubuntu, accessible through a web dashboard and directly from Signal Messenger.

This creates a foundation for more advanced automation, particularly in software development, testing, and infrastructure management.


Tested on Ubuntu with Docker Compose, Hermes Agent, and native signal-cli in November 2026.

Leave a Reply