Automating Deployment Pipelines with Hermes Agent
From Writing git-ops and docker-build Skills to Cron Scheduling
Have you ever gotten lost in YAML hell while setting up a Jenkins pipeline? I have — and quite deeply at that. Aligning indentation at every stage, double-checking environment variable syntax, and just when the pipeline finally runs, something else breaks the next day. Back then I thought, "What if an AI agent could understand this pipeline and run it on its own?" By 2026, that's become reality. Hermes Agent is an open-source autonomous agent framework built by NousResearch. Its core strength isn't just receiving and executing commands — it's a structure that learns on its own and gets progressively better at the same tasks over time.
This article covers the entire process of writing custom Skills like git-ops and docker-build, connecting them to cron jobs, and running an unattended nightly build-and-deploy pipeline. By the end of this article, you'll have a deployment pipeline that runs on its own starting tonight. For those new to certain concepts, brief explanations are included along the way so you can follow along even without DevOps experience.
Core Concepts
Before You Begin: Installation and Initialization
Hermes Agent can be installed via pip.
pip install hermes-agent
# Initialization — an interactive prompt will appear asking for your LLM API key
hermes init
# Verify installation
hermes --version # v0.14.0Running hermes init lets you choose which LLM API (OpenAI, Anthropic, OpenRouter, etc.) to use and register your key. Once configured, it's used automatically in all subsequent commands.
Hermes only works with models that support a minimum of 64K token context. Using large-context models like Claude Sonnet or GPT-4o is recommended.
The Structure of the Skills System
Skills are modular units that extend the agent's capabilities. Think of each file as defining a single tool. They are written by placing a SKILL.md file inside the ~/.hermes/skills/<category>/<skill-name>/ directory.
~/.hermes/skills/
devops/
git-ops/
SKILL.md # Required: core instructions
scripts/ # Helper scripts (bash, python)
references/ # Reference documents
templates/ # Output format templates
docker-build/
SKILL.md
scripts/
build.shSKILL.md is divided into two parts. The YAML frontmatter at the top contains metadata such as the name, version, and required environment variables. The body markdown describes the procedures the agent should follow and how to verify results.
The agentskills.io standard: This is the de facto open standard adopted by Hermes. It was established as NousResearch led the effort and platforms like Claude Code and Cursor adopted the same specification. Skills written to this standard can be ported directly across compatible platforms, making a Skill written once into "portable agent code" that can be reused across multiple agent environments.
The When to Use / Procedure / Verification structure in the body is the recommended format. These three sections aren't strictly required, but the community has validated that Skills following this structure have a significantly higher success rate for agent intent recognition — so it's generally advisable to follow it.
How the Cron System Works
Hermes's gateway daemon periodically polls the scheduler and runs scheduled tasks in isolated agent sessions. Each cron job consists of four components.
| Component | Role |
|---|---|
--prompt |
Task instructions delivered to the agent |
--schedule |
Standard cron expression or natural language schedule |
--skill |
List of Skills to load (multiple can be specified) |
--deliver |
Result delivery channel (Slack, email, etc.) |
Since v0.10, natural language scheduling is also supported. A single command like "Push changes to GitHub every night at midnight" can simultaneously create a Skill and a cron job, which is especially convenient for those unfamiliar with cron expressions.
Connecting Data Flow Between Jobs with context_from
This was something I found confusing at first, but the context_from parameter lets you pipe the output of one cron job into the context of the next — without an external orchestrator like Jenkins or GitHub Actions. The output text from the previous job is injected directly into the context window of the next job. This is how a subsequent job can read the test result text and determine whether things "passed."
Practical Application
Examples 1 through 4 have cumulative dependencies. You need to write the git-ops (Example 1) and docker-build (Example 2) Skills first before you can set up the Cron connections (Example 3) and multi-stage pipeline (Example 4).
Example 1: Writing the git-ops Skill
This is the most fundamental DevOps Skill. It can be used for nightly auto-commits and pushes, or for remote repository backups. Honestly, just getting this one Skill right gives you the peace of mind that "my codebase is automatically saved every night."
---
name: git-ops
description: Automate Git repository sync, commits, and pushes
version: "1.2"
required_environment_variables:
- name: GITHUB_TOKEN
prompt: "Enter your GitHub Personal Access Token"
required_for: [push]
---
## When to Use
When you need to sync a remote repository, run nightly backups, or automate commits.
## Procedure
1. Check for changes with `git status`; exit if there are none
2. Stage changed files: `git add -A`
3. Read the change summary from `git diff --staged --stat` and
generate a commit message in the format "auto: [summary of changed files]", then commit
4. Push to the remote repository using `GITHUB_TOKEN`:
`git push https://$GITHUB_TOKEN@github.com/<owner>/<repo>.git`
## Verification
Verify the latest commit with `git log -1` and check sync status with the remote branchThe required_environment_variables field matters for a specific reason. Separating secrets into environment variables prevents tokens from being exposed directly in the SKILL.md body. It's the most basic safeguard against the accident of a token being leaked the moment SKILL.md is pushed to Git.
You can generate a GITHUB_TOKEN from GitHub → Settings → Developer settings → Personal access tokens. Checking only the repo permission is sufficient.
Example 2: Writing the docker-build Skill
Building on the git-ops Skill we created earlier, this Skill handles building, tagging, and pushing to a registry all at once. For reference, a Docker registry is a repository that stores and distributes built images (services like Docker Hub and AWS ECR fall into this category). This is a situation you encounter frequently in practice, and tracking down where a failed build broke can be difficult, so specifying in the procedure that failure logs be written to references/build-errors.md with timestamps makes debugging much easier.
---
name: docker-build
description: Automate Docker image building, tagging, and registry push
version: "1.0"
required_environment_variables:
- name: DOCKER_REGISTRY_TOKEN
prompt: "Enter your Docker registry token"
required_for: [push]
- name: IMAGE_TAG
prompt: "Image tag (e.g., latest, v1.2.3)"
required_for: [build, push]
---
## When to Use
During the build stage of a CI/CD pipeline, or when creating images before staging deployment.
## Procedure
1. Run `scripts/build.sh` to build the image
2. On success, authenticate with `$DOCKER_REGISTRY_TOKEN` and push to the registry
3. On failure, record the error log with a timestamp in `references/build-errors.md`
## Verification
Verify image layers with `docker inspect <image>`Keeping helper scripts in a separate scripts/ directory is recommended. It prevents the SKILL.md body from becoming cluttered and allows scripts to be tested independently. A minimal skeleton looks like this.
#!/usr/bin/env bash
# scripts/build.sh
set -e
IMAGE_NAME="${IMAGE_NAME:-myapp}"
IMAGE_TAG="${IMAGE_TAG:-latest}"
REGISTRY="${REGISTRY:-registry.example.com}"
docker build -t "$REGISTRY/$IMAGE_NAME:$IMAGE_TAG" .
echo "Build succeeded: $REGISTRY/$IMAGE_NAME:$IMAGE_TAG"Values like IMAGE_NAME and REGISTRY can be modified to suit your environment.
Example 3: Connecting Skills to a Cron Job
With both Skills written, you can now tie them together in a nightly cron job. It's handled with a single CLI command.
hermes cron add \
--schedule "0 2 * * *" \
--skill git-ops \
--skill docker-build \
--prompt "Build the staging branch, push it to the registry, and send the deployment result to Slack" \
--deliver slack:#deploy-alerts0 2 * * * means every night at 2 AM. --skill can be specified multiple times, and the specified Skills are loaded into that cron session.
To use the --deliver slack:#deploy-alerts option, a Slack Webhook must be configured beforehand. Register it first with the hermes config slack-webhook https://hooks.slack.com/... command, after which you can use it in the form --deliver slack:#channel-name.
The workflow of coming into work in the morning and checking the accumulated deployment results in the Slack channel is more impressive than you'd expect. There was a moment the first time it worked where I thought, "This actually works?"
Example 4: Building a Multi-Stage Pipeline with context_from
In addition to the Skills we've already built, if you have separately written run-tests and deploy Skills, you can complete a test → build → deploy pipeline entirely within Hermes, without an external orchestrator. This was personally the most interesting part.
# Job 1: Run tests (at minute 0 of every hour)
name: test-runner
schedule: "0 * * * *"
skill: run-tests
prompt: "Run the test suite and generate a text report of the results"
# Job 2: Build if tests pass (at minute 15 of every hour, referencing test-runner output)
name: docker-build-job
schedule: "15 * * * *"
skill: docker-build
context_from: ["test-runner"]
prompt: "Check the test report and build the image only if all tests passed"
# Job 3: Deploy to staging if build succeeds (at minute 30 of every hour, referencing docker-build-job output)
# Staging server: a test environment for validation before production deployment
name: deploy-staging
schedule: "30 * * * *"
skill: deploy # Requires a separately written deploy Skill
context_from: ["docker-build-job"]
prompt: "Reference the built image information and deploy to the staging server"
context_from: A parameter that injects the output text of a previous cron job directly into the context window of the next job. Because Job B can read Job A's results and use them for conditional decisions, logic like "build only if tests pass" can be expressed as a natural language prompt.
The 15-minute intervals in the example above are chosen under the assumption that each job (tests, build) completes within 15 minutes. If your project has longer build times, it's recommended to use more generous intervals. If the next job starts before the previous one finishes, it will end up reading stale context from the prior run.
Pros and Cons Analysis
The biggest drawback I felt most acutely in practice was the cron prompt overhead. Since every execution consumes tokens from the prompt, I initially set tight intervals thinking "more frequent is better," and was caught off guard by the end-of-month bill. Please pay particular attention to that aspect.
Advantages
| Item | Detail |
|---|---|
| Self-learning | Automatically creates and improves SKILL.md on task success; handles the same task more efficiently over time |
| Portability | Compliance with the agentskills.io standard enables Skill sharing with Claude Code, Cursor, and others |
| Multi-backend | Supports 7 execution environments: Local, Docker, SSH, Modal, Vercel Sandbox, and more |
| Security | required_environment_variables prevents secrets from being exposed in model transcripts |
| Model flexibility | Any LLM API can be swapped in — OpenAI, Nous Portal, OpenRouter, etc. |
| Cost | The framework itself is free under MIT; approximately $20–50/month based on VPS + budget model usage |
Disadvantages and Caveats
| Item | Detail | Mitigation |
|---|---|---|
| Minimum context requirement | Models with fewer than 64K tokens are outright rejected | Use 64K+ models like Claude Sonnet or GPT-4o |
| Cron prompt overhead | Every run consumes tokens from the prompt; costs accumulate with high-frequency schedules | Use generous schedule intervals and keep prompts concise |
| Snapshot delay | Skills Hub curators run periodically, causing a lag before the latest Skills are reflected | Locally written custom Skills are reflected immediately, so solve this with custom development |
| Memory limits | Very long task histories can be compressed and lost (occurs in the LightRAG layer) | Save important results separately to Git commits or external storage |
| Ecosystem size | Approximately 118 Skills in the Skills Hub, smaller than other platforms | Refer to the awesome-hermes-agent community list; direct contributions are possible |
LightRAG: Hermes's default memory and knowledge graph backend. It's the component that allows the agent to remember and search previous task results. When memory limits are reached, compression occurs in this layer.
The Most Common Mistakes in Practice
-
Writing secrets directly in the
SKILL.mdbody — Tokens and passwords must always be separated intorequired_environment_variables. The moment a Markdown file is pushed to version control, the token is leaked. -
Writing the
Proceduresection too vaguely — Writing abstractly, like "deploy it," causes the agent to interpret the instruction differently each time. Specifying concrete commands and decision criteria step by step is far more reliable. -
Overlapping execution times in a
context_frompipeline — If Job 2 starts before Job 1 finishes, it will read stale context from the previous run. It's recommended to leave sufficient gaps between jobs.
Closing Thoughts
By combining Hermes Agent's Skills and Cron system, you can build a DevOps pipeline that, once designed, runs on its own and learns to operate more efficiently over time.
Three steps you can start right now:
-
Create
~/.hermes/skills/devops/git-ops/SKILL.md, copy the example code, and modify the repository path and branch name to match your setup. You can generate aGITHUB_TOKENfrom GitHub Settings → Developer settings → Personal access tokens. -
Register your first nightly cron job with the command
hermes cron add --schedule "0 2 * * *" --skill git-ops --prompt "Commit any changes and push to the remote repository". Checkinggit logthe next morning to see the auto-commits stacked up is more satisfying than you'd expect. -
Write the
docker-buildSkill and connect the two jobs withcontext_from. You'll get to experience a multi-stage pipeline that decides on its own whether to build based on whether tests passed.