
In the world of DevOps, the distance between a web dashboard and the terminal can feel like a chasm. Often, we find ourselves manually SSH-ing into servers to trigger deployment scripts, restart services, or check container health. Bash Runner was born out of a desire to bridge this gap—securely, efficiently, and in real-time.
Bash Runner is a hybrid orchestration engine that allows you to execute remote shell commands and deployment scripts through a secure web interface.
When building an orchestration tool, you face a dilemma. REST is great for simple, "set-and-forget" commands. You send a request, the server executes, and you get a response. However, for long-running scripts (like building a Docker image), a standard HTTP request might timeout, and you lose visibility into the progress.
WebSockets provide the real-time stream we need, but they are often more complex to implement for simple integrations.
In Bash Runner, I implemented a Hybrid Endpoint architecture. A single interface handles both:
restart-nginx.Security is paramount when you're exposing a shell to the web. You can't just allow a user to run rm -rf /.
Bash Runner uses a Strict Command Registry system. Every executable command must be pre-defined in a configuration file. The web API only accepts commandIds, never raw shell strings.
{
"commands": {
"deploy-frontend": {
"script": "./scripts/deploy.sh",
"cwd": "/var/www/my-app",
"timeout": 300000,
"requiresSudo": true
}
}
}
This "whitelist-only" approach ensures that even if an API key is compromised, the attacker is limited to the pre-approved deployment actions you've defined.
One of the most satisfying parts of Bash Runner is watching a build process in real-time. By bridging Node.js child_process.spawn with a WebSocket server, we can pipe every line of console output directly to the frontend.
const child = spawn(command.script, command.args, { cwd: command.cwd });
child.stdout.on('data', (data) => {
socket.emit('output', { type: 'stdout', data: data.toString() });
});
child.stderr.on('data', (data) => {
socket.emit('output', { type: 'stderr', data: data.toString() });
});
This transforms a "black box" deployment into a transparent, observable workflow.
To protect the command plane, Bash Runner implements multiple layers of defense:
Bash Runner isn't just a utility; it's a testament to the power of Node.js in the DevOps ecosystem. By combining the stability of REST with the real-time nature of WebSockets, we've created a tool that makes infrastructure management feel fluid and modern.
The terminal is no longer a separate world—it's just one API call away.
Interested in secure orchestration? Check out the Bash Runner project details in my Portfolio.