Introduction
There's a moment every operations engineer knows well: it's 2 AM, something's broken, and you're frantically SSH-ing into servers trying to remember the exact sequence of commands to fix it. You've done this before, but was it systemctl restart first or the config update? And which servers exactly?
This is the problem Rundeck solves. It's an open-source runbook automation platform that lets you define, schedule, and execute operational procedures across your entire infrastructure—with proper access control, audit trails, and the peace of mind that comes from knowing the procedure will run exactly the same way every time.
I've been using Rundeck for years, and recently I decided to contribute back to the ecosystem by creating three tools that solve specific pain points I encountered: a production-ready Docker image, a plugin for copying files between nodes, and a GitHub Action for seamless CI/CD integration. Let me walk you through all of them.
What Makes Rundeck Special
Rundeck occupies a unique space in the DevOps toolchain. It's not a configuration management tool like Ansible or Puppet—though it integrates beautifully with them. It's not a CI/CD platform like Jenkins—though it can trigger and be triggered by pipelines. Rundeck is specifically designed for operational workflows.
Core Capabilities
- Job Definitions: Create multi-step workflows with conditionals, error handling, and node targeting
- Node Management: Maintain a centralized inventory of your infrastructure with custom attributes
- Access Control: Fine-grained RBAC so developers can restart their services without full SSH access
- Key Storage: Secure credential management for SSH keys, passwords, and API tokens
- Execution History: Complete audit trail of who ran what, when, and with what results
- Scheduling: Cron-like scheduling for recurring maintenance tasks
- REST API: Full API for integration with CI/CD pipelines, monitoring systems, and AI agents
Where It Shines
In my experience, Rundeck excels at:
- Incident Response: Pre-built runbooks that on-call engineers can execute confidently
- Self-Service Operations: Let developers restart services or clear caches without ops tickets
- Coordinated Procedures: Multi-node operations that need to happen in a specific order
- Compliance Tasks: Scheduled security scans, backup verifications, audit reports
- Deployment Orchestration: Coordinate deployments across environments with approval gates
REST API: Automation Beyond the UI
One of Rundeck's most powerful features is its comprehensive REST API. Every action you can perform in the web interface is available programmatically, making Rundeck a perfect backend for automated operations.
The API supports job execution with custom parameters, execution monitoring (check status, stream logs, abort running jobs), job management (create, update, delete, import/export), node inventory queries, key storage management, and system health checks.
Authentication
Rundeck supports multiple authentication methods for API access:
# Using API Token (recommended)
curl -H "X-Rundeck-Auth-Token: YOUR_TOKEN" \
https://rundeck.example.com/api/41/projects
# Run a job by ID
curl -X POST \
-H "X-Rundeck-Auth-Token: YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{"options": {"environment": "production", "version": "1.2.3"}}' \
https://rundeck.example.com/api/41/job/JOB_ID/run
CI/CD Pipelines
I created a dedicated GitHub Action to make CI/CD integration easier: rundeck-github-action.
- name: Deploy via Rundeck
uses: Walsen/rundeck-github-action@v1
with:
rundeck_url: https://rundeck.example.com
rundeck_token: ${{ secrets.RUNDECK_TOKEN }}
action: run_job
job_id: ${{ vars.DEPLOY_JOB_ID }}
job_options: '{"version": "${{ github.sha }}", "environment": "production"}'
wait_for_completion: true
timeout: 600
The action supports multiple operations:
| Action | Description |
run_job | Execute a Rundeck job with options |
get_job_info | Get job details |
list_jobs | List jobs in a project |
get_execution | Get execution details |
list_executions | List executions for a job or project |
abort_execution | Abort a running execution |
You can wait for job completion and get the execution status:
- name: Run deployment and wait
id: deploy
uses: Walsen/rundeck-github-action@v1
with:
rundeck_url: https://rundeck.example.com
rundeck_token: ${{ secrets.RUNDECK_TOKEN }}
action: run_job
job_id: ${{ vars.DEPLOY_JOB_ID }}
wait_for_completion: true
- name: Check result
run: |
echo "Status: ${{ steps.deploy.outputs.execution_status }}"
echo "URL: ${{ steps.deploy.outputs.execution_url }}"
Monitoring Integration
Have your monitoring system trigger remediation jobs automatically:
# PagerDuty webhook handler example
def handle_alert(alert):
if alert['type'] == 'high_memory':
requests.post(
f"{RUNDECK_URL}/api/41/job/{CLEAR_CACHE_JOB}/run",
headers={"X-Rundeck-Auth-Token": RUNDECK_TOKEN},
json={"options": {"server": alert['hostname']}}
)
AI Agents Integration
Rundeck's API makes it an ideal execution backend for AI-powered operations. Using the Strands Agents SDK from AWS, you can build intelligent agents that leverage Rundeck as their operational backbone.
from strands import Agent, tool
import requests
RUNDECK_URL = "https://rundeck.example.com"
RUNDECK_TOKEN = "your-api-token"
@tool
def list_rundeck_jobs(project: str) -> dict:
"""List available runbooks in a Rundeck project."""
response = requests.get(
f"{RUNDECK_URL}/api/41/project/{project}/jobs",
headers={"X-Rundeck-Auth-Token": RUNDECK_TOKEN}
)
return response.json()
@tool
def run_rundeck_job(job_id: str, options: dict = None) -> dict:
"""Execute a Rundeck job/runbook."""
response = requests.post(
f"{RUNDECK_URL}/api/41/job/{job_id}/run",
headers={"X-Rundeck-Auth-Token": RUNDECK_TOKEN, "Content-Type": "application/json"},
json={"options": options or {}}
)
return response.json()
@tool
def get_execution_status(execution_id: int) -> dict:
"""Check the status of a Rundeck job execution."""
response = requests.get(
f"{RUNDECK_URL}/api/41/execution/{execution_id}",
headers={"X-Rundeck-Auth-Token": RUNDECK_TOKEN}
)
return response.json()
ops_agent = Agent(
system_prompt="""You are an operations assistant with access to Rundeck runbooks.
When asked to perform operational tasks:
1. List available jobs to find the appropriate runbook
2. Execute the job with the correct parameters
3. Monitor the execution and report the results
Always confirm before executing destructive operations.""",
tools=[list_rundeck_jobs, run_rundeck_job, get_execution_status]
)
response = ops_agent("Clear the log files on the production cluster")
This pattern keeps humans in control—the AI can only execute pre-approved runbooks with proper access controls and audit trails. It's autonomous operations with guardrails.
Low-Code Integration with n8n
If you prefer a visual approach, n8n offers native Rundeck integration. You can build workflows that connect GitHub events to Rundeck job executions without writing code. Combined with n8n's 400+ integrations, you can create powerful automation chains.
Licensing: Open Source vs Enterprise
Rundeck follows an open-core model. The community edition is fully open source under the Apache 2.0 license, while PagerDuty (who acquired Rundeck) offers commercial versions with additional features.
Free forever, includes: workflow execution and job definitions, node management and key storage, access control (ACL-based), scheduling and job activity logs, community plugins, and REST API.
This is what my Docker image uses—perfect for small to medium teams and learning environments.
The enterprise offerings add:
| Feature | Description |
| High Availability | Clustered deployments with auto-takeover |
| SSO Integration | SAML, LDAP, OAuth support |
| Enterprise Plugins | ServiceNow, PagerDuty, Datadog, VMware integrations |
| Advanced Scheduling | Blackout calendars, schedule forecasting |
| Failed Job Resume | Resume from the failed step instead of restarting |
| Enterprise Support | SLA-backed support and account management |
Which Should You Choose?
For most use cases, start with open source. It's production-ready. Consider upgrading when you need HA, SSO, or enterprise integrations.
The Gap: Node-to-Node File Transfers
While building a configuration distribution workflow, I hit a limitation. Rundeck's built-in file copier moves files from the Rundeck server to target nodes. But I needed to copy files from one node to multiple other nodes—specifically, distributing generated configs from a central server to application nodes.
The workaround was clunky: download to Rundeck, then upload to each destination. For large files or many destinations, this becomes a bottleneck. So I built a plugin.
Rundeck Node-to-Node Plugin
The rundeck-node-to-node plugin adds a workflow step that copies files and directories between nodes using SSH/SFTP, fully integrated with Rundeck's node definitions and key storage.
Features
- Two Transfer Modes: Route through Rundeck (reliable) or direct node-to-node (fast)
- Parallel Transfers: Copy to multiple destinations simultaneously
- Directory Support: Recursive copy with preserved permissions and timestamps
- Key Storage Integration: Uses Rundeck's secure credential management
- Error Handling: Option to continue on partial failures
Transfer Modes
Via-Rundeck (Default): Files download to Rundeck once, then upload to all destinations. Works in any network topology.
Direct Mode: Source pushes directly to destinations via SCP. Faster, but requires source-to-destination SSH access.
Node Configuration
The plugin uses Rundeck's standard node attributes:
config-server:
hostname: 10.0.1.10
username: deploy
ssh-key-storage-path: keys/project/deploy-key
tags: config
app-server-01:
hostname: 10.0.1.20
username: deploy
ssh-key-storage-path: keys/project/deploy-key
tags: app,production
Plugin Options
| Option | Required | Default | Description |
| Source Node | Yes | - | Node name where files originate |
| Source Path | Yes | - | File or directory path on source |
| Destination Nodes | Yes | - | Comma-separated destination node names |
| Destination Path | Yes | - | Target path on destinations |
| Transfer Mode | No | via-rundeck | via-rundeck or direct |
| Parallel Transfers | No | true | Transfer to multiple nodes in parallel |
| Continue on Error | No | false | Don't fail if some destinations fail |
Installation
- Download the JAR from Releases
- Copy to Rundeck's
libext directory
- Restart Rundeck (or wait for auto-reload)
The plugin appears as a new workflow step type: "Node to Node File Copy".
Production-Ready Docker Image
Installing Rundeck traditionally means dealing with Java, databases, reverse proxies, and process management. To streamline this, I created a Docker image that bundles everything for production use.
The rundeck-image provides:
- Rundeck 5.18.0 (configurable version)
- Nginx reverse proxy for proper HTTP handling
- Supervisor for process management
- PostgreSQL support for production databases
- Node-to-Node plugin pre-installed
Quick Start
docker run -d -p 8080:8080 -e RUNDECK_GRAILS_URL=http://localhost:8080 ghcr.io/walsen/rundeck-image:latest
Production Setup with Docker Compose
version: '3.8'
services:
rundeck:
image: ghcr.io/walsen/rundeck-image:latest
ports:
- "8080:8080"
environment:
RUNDECK_GRAILS_URL: https://rundeck.example.com
RUNDECK_DATABASE_URL: jdbc:postgresql://db:5432/rundeck
RUNDECK_DATABASE_USERNAME: rundeck
RUNDECK_DATABASE_PASSWORD: ${DB_PASSWORD}
volumes:
- ./config/realm.properties:/etc/rundeck/realm.properties
- rundeck-data:/var/rundeck
depends_on:
- db
db:
image: postgres:15-alpine
environment:
POSTGRES_DB: rundeck
POSTGRES_USER: rundeck
POSTGRES_PASSWORD: ${DB_PASSWORD}
volumes:
- postgres-data:/var/lib/postgresql/data
volumes:
rundeck-data:
postgres-data:
Environment Variables
| Variable | Default | Description |
RUNDECK_GRAILS_URL | http://localhost:8080 | External URL (must match your setup) |
RUNDECK_DATABASE_URL | - | PostgreSQL JDBC connection string |
RUNDECK_DATABASE_USERNAME | - | Database user |
RUNDECK_DATABASE_PASSWORD | - | Database password |
User Authentication
Mount a realm.properties file for basic authentication:
admin:admin,user,admin
operator:operator123,user
readonly:viewer456,user
For production, integrate with LDAP or SSO—Rundeck supports both.
Custom Port Mapping
The image handles any external port. Just match RUNDECK_GRAILS_URL:
docker run -d -p 4440:8080 -e RUNDECK_GRAILS_URL=http://localhost:4440 ghcr.io/walsen/rundeck-image:latest
CI/CD Integration
Both repositories include GitHub Actions workflows:
rundeck-image:
- Builds on every push to main
- Publishes to GitHub Container Registry
- Tags releases with version numbers
rundeck-node-to-node:
- Builds and tests the plugin
- Creates release artifacts
- Publishes JAR files to GitHub Releases
Practical Example: Config Distribution Workflow
Let me show how these tools work together in a real scenario.
The Setup:
- 1 config server generates environment-specific configuration files
- 5 application servers need these configs
- Updates should happen without manual intervention
Job Configuration:
| Step | Node(s) | Action |
| 1 | config-server | /opt/scripts/generate-config.sh |
| 2 | config-server → app-01..05 | Node-to-Node copy /etc/myapp/config.yml |
| 3 | app-01..05 (sequential) | systemctl reload myapp |
The Result:
- One-click config updates
- Parallel distribution to all servers
- Rolling reload to avoid downtime
- Complete audit trail
- Anyone with permissions can run it
Testing the Plugin
The plugin repository includes a Docker-based test environment:
cd test/
docker-compose up -d
This spins up a Rundeck instance with the plugin, multiple test nodes for source/destination testing, and pre-configured SSH keys. See test/README.md for detailed testing instructions.
Deploying Rundeck on AWS
Best Option: Amazon ECS on Fargate
For most teams, ECS Fargate hits the sweet spot between cost and operational simplicity:
| Factor | ECS Fargate |
| Operational Effort | Low - no EC2 instances to manage |
| Cost | ~$30-50/month for small workloads |
| Integration | Native ALB, Secrets Manager, CloudWatch |
| Persistence | EFS for Rundeck data, RDS for database |
Budget Option: ECS with PostgreSQL Sidecar
Run PostgreSQL as a sidecar container instead of RDS to cut costs significantly:
| Factor | With RDS | With PostgreSQL Sidecar |
| Monthly Cost | $50-80 | $20-35 |
| Database Backups | Automatic | Manual (EFS snapshots) |
| High Availability | RDS Multi-AZ option | Single container |
For most Rundeck deployments, the sidecar approach works great—Rundeck isn't a high-transaction workload.
AWS Options Comparison
| Option | Monthly Cost | Ops Effort | Best For |
| EC2 (t3.small) | $15-20 | Medium | Budget-conscious, 24/7 workloads |
| Lightsail | $5-20 | Low | Learning, simple setups |
| ECS Fargate | $30-50 | Low | Production, low maintenance |
| EKS | $100+ | High | Already running Kubernetes |
Why Not the Others?
- EC2: More operational overhead (patching, monitoring), but cheaper for 24/7 workloads with Reserved Instances
- EKS: Overkill unless you already have a Kubernetes cluster—adds complexity and ~$70/month just for the control plane
- App Runner: Simpler but limited networking control, harder to reach private infrastructure
- Lightsail: Cheapest option but limited VPC integration for node access
My Recommendations
- Small team/learning: EC2 t3.small or Lightsail (~$10-20/month)
- Production with low ops effort: ECS Fargate + RDS (~$50-80/month)
- Enterprise/HA: ECS Fargate multi-task + RDS Multi-AZ (~$150+/month)
Conclusions
Rundeck transforms operational chaos into repeatable, auditable procedures. The Node-to-Node plugin enables efficient file distribution between nodes. The Docker image removes deployment friction. And the GitHub Action makes CI/CD integration seamless.
If you're drowning in operational toil, give Rundeck a try.
Links