I started this experiment with a simple idea:

Since I already had ILMU working in OpenClaw, maybe I could also use the same ILMU API key with Hermes.

On paper, it sounded logical.

My intended setup was:

text
Coolify
→ Hermes
→ ILMU API
→ ilmu-nemo-nano / nemo-super

I thought this would be a straightforward configuration exercise.

It was not.

But the learning was valuable.

---

Why I Tried ILMU First

I already had an ILMU key from my OpenClaw setup. Since OpenClaw was part of my AI experimentation stack, it felt natural to try ILMU as the backend model provider for Hermes as well.

The logic was simple:

text
If OpenClaw can use ILMU,
maybe Hermes can use ILMU too.

So I created a Hermes service in Coolify using Docker Compose.

My Hermes container started.

The health check worked.

The /v1/models endpoint worked.

At that point, I thought I was close.

But I was only close to getting Hermes running — not close to getting Hermes + ILMU working.

That distinction became important.

---

My Starting Point

At the time, my stack already included:

text
Coolify
Traefik
OpenClaw
n8n
Postiz
Hermes
ILMU

I wanted Hermes to become another agent layer in the stack.

The bigger idea was:

text
Mobile / browser interface
→ Hermes
→ model provider
→ future tools like Todoist, Perplexity, Higgsfield, MCP

But before getting to the exciting part, I first needed to get the basic model connection working.

---

Creating Hermes as a Coolify Resource

In Coolify, I created Hermes as a Docker Compose resource:

text
Coolify
→ Project
→ New Resource
→ Docker Compose

I used Docker Compose because Hermes needs more than just a simple container image.

It needs:

text
image
command
port
environment variables
API keys
persistent storage
model settings

The image that worked for me was:

yaml
image: 'nousresearch/hermes-agent:latest'

The command was:

yaml
command:
  - gateway
  - run

The Hermes API server runs on:

text
8642

So the port mapping was:

yaml
ports:
  - '8642:8642'

---

The First Compose Direction

The early compose structure looked roughly like this:

yaml
version: '3.9'

services:
  hermes:
    image: 'nousresearch/hermes-agent:latest'
    restart: unless-stopped
    command:
      - gateway
      - run

    ports:
      - '8642:8642'

    environment:
      HERMES_UID: '10000'
      HERMES_GID: '10000'

      API_SERVER_ENABLED: 'true'
      API_SERVER_HOST: '0.0.0.0'
      API_SERVER_PORT: '8642'
      API_SERVER_KEY: '{{environment.HERMES_API_SERVER_KEY}}'
      API_SERVER_MODEL_NAME: 'hermes-agent'

      OPENAI_API_KEY: '{{environment.ILMUNANO_API_KEY}}'
      ILMU_API_KEY: '{{environment.ILMUNANO_API_KEY}}'
      ILMUNANO_API_KEY: '{{environment.ILMUNANO_API_KEY}}'

      OPENAI_BASE_URL: 'https://api.ilmu.ai/v1'
      OPENAI_API_BASE: 'https://api.ilmu.ai/v1'

      HERMES_MODEL: 'ilmu-nemo-nano'
      HERMES_INFERENCE_MODEL: 'ilmu-nemo-nano'

    volumes:
      - 'hermes_data:/opt/data'

volumes:
  hermes_data:

The intention was clear:

text
I call Hermes using HERMES_API_SERVER_KEY.
Hermes calls ILMU using ILMUNANO_API_KEY.

---

Shared Variables in Coolify

I created these shared variables in Coolify:

env
HERMES_API_SERVER_KEY=your_hermes_server_key
ILMUNANO_API_KEY=your_ilmu_key

I learned quickly that variable names are case-sensitive.

This matters:

text
ILMUNANO_API_KEY

is not the same as:

text
ilmunano_api_key

One letter or casing mismatch can break the whole setup.

I also learned not to create API server keys with special characters like:

text
& # $ ? !

Those characters can confuse shell commands or Docker Compose.

For generated server keys, a safer format is:

bash
openssl rand -hex 32

That gives a clean random key that is easier to use across shell, YAML, and Docker.

---

What Worked

The Hermes installation itself worked.

I was able to confirm:

text
Hermes container started
Hermes gateway started
/health returned OK
/v1/models returned hermes-agent
Hermes API server key worked

The health check worked:

bash
curl -i --max-time 10 http://127.0.0.1:8642/health

A successful response looked like:

text
HTTP/1.1 200 OK
{"status": "ok", "platform": "hermes-agent"}

Then /v1/models also worked:

bash
curl -i --max-time 10 \
  -H 'Authorization: Bearer YOUR_HERMES_API_SERVER_KEY' \
  http://127.0.0.1:8642/v1/models

That returned hermes-agent.

So at this point, I knew:

text
Coolify was working
Hermes container was working
Hermes API server was working
HERMES_API_SERVER_KEY was working

This was an important distinction.

The installation worked.

The ILMU integration did not.

---

Where It Failed

The failure happened when Hermes tried to call ILMU.

The error I kept seeing was:

text
Missing Authentication header

This was the key clue.

It meant:

text
My request reached Hermes.
Hermes accepted my API key.
But when Hermes called ILMU, ILMU did not receive the authentication header.

So the failing part was not:

text
Me → Hermes

The failing part was:

text
Hermes → ILMU

That distinction helped me avoid blaming the wrong layer.

---

What I Checked

I checked whether the ILMU key was inside the Hermes container.

It was.

The environment variable existed.

The key length was present.

So this was not simply a case of “I forgot to add the API key.”

The problem seemed to be that Hermes was not passing the key to ILMU in the way ILMU expected.

I tried mapping the same key under different names:

text
OPENAI_API_KEY
ILMU_API_KEY
ILMUNANO_API_KEY

I also pointed Hermes to:

text
https://api.ilmu.ai/v1

But the chat completion call still failed.

---

The Key Confusion I Made

One of my early mistakes was confusing the different keys.

There were two different types of keys involved:

| Key | Purpose | |---|---| | HERMES_API_SERVER_KEY | Used by me to access Hermes | | ILMUNANO_API_KEY | Used by Hermes to call ILMU |

When calling Hermes, I should use:

text
HERMES_API_SERVER_KEY

When Hermes calls ILMU, Hermes should use:

text
ILMUNANO_API_KEY

At one point, I used the wrong key in the wrong place and got:

text
401 Unauthorized
Invalid API key

The lesson:

Not all API keys are the same. The key that protects the agent server is not the same as the key that calls the model provider.

---

The Real Learning from the ILMU Attempt

The biggest learning was this:

A key that works in one tool does not automatically work the same way in another tool.

OpenClaw may know exactly how to talk to ILMU.

Hermes may treat ILMU as a generic OpenAI-compatible endpoint.

That sounds simple, but integration depends on details:

text
Which header is expected?
Which model name is accepted?
Is the key scoped to certain plans?
Does the provider require a special route?
Does the tool have provider-specific logic?

So I learned not to assume:

text
OpenClaw → ILMU works
therefore
Hermes → ILMU works

That assumption was wrong.

---

What I Would Do Differently

If I were doing this again, I would test ILMU directly first before putting Hermes in the middle.

The better troubleshooting sequence would be:

text
1. Test ILMU directly with curl
2. Confirm the model responds
3. Confirm the correct model name
4. Confirm the correct auth format
5. Then configure Hermes
6. Then test Hermes → ILMU

I jumped too quickly into the full chain.

That made debugging harder.

---

My Current Conclusion on ILMU + Hermes

I did not get ILMU working reliably as the Hermes backend.

But I did learn something useful.

My current architecture view is:

text
OpenClaw → ILMU
Hermes → OpenRouter

At least for now.

That is not a failure.

It is a cleaner separation.

The lesson is:

Use each provider where it is stable first. Do not force every provider into every tool too early.

ILMU remains useful for my OpenClaw experimentation.

But for Hermes, I decided to switch to OpenRouter because it gave me better model visibility, easier testing, and clearer routing diagnostics.

That transition became the second part of the journey.