Case Study

PATTY CLI

A zero-agent SSH deploy tool that deploys apps to Linux servers using tools you already know — ssh, rsync, Docker, Caddy, and shell commands.

Role Product Engineer / Creator
Stack Node.js, SSH, Docker, Caddy
Status Active

The Problem

Deploying applications to a Linux server should be simple. You SSH in, copy your files, and start your app. But in practice, every team reinvents the same deploy pipeline:

  • CI scripts that grow into fragile bash spaghetti over time.
  • Server agents that need installing, updating, and monitoring — another service to keep running.
  • Each project rolling its own deploy config format, its own conventions, its own set of footguns.
  • No consistent way to check server health, view logs, run commands, or create backups across projects.

The core insight: all the pieces already exist on any Linux server — SSH, rsync, Docker, systemd, Caddy. The missing piece is a thin CLI that orchestrates them without adding another daemon.

The Solution

PATTY CLI is a Node.js deploy tool with zero server-side installation. It reads a single .deploy/config.json file and uses SSH to connect, rsync to copy files, and Docker Compose or Caddy to serve the app. No agent, no daemon, no recurring server cost.

One-File Config

A single .deploy/config.json defines environments, targets, domains, env files, and app settings. Generated with patty init, edited by hand when needed.

Multi-Environment Deploys

Dev, staging, and production from the same config. Each environment gets its own server, domain, env file, and Docker Compose project name.

Git-Aware Deploys

Deploys from a git archive instead of a dirty working directory. Rollback to any commit with patty rollback production --commit 8f4a31c.

Server Diagnostics

patty status runs a 50+ point security and health check — SSH config, firewall, fail2ban, Docker safety, disk, swap, memory — with a score from 0 to 100.

Auto-Fix Mode

patty status --fix applies safe automatic fixes: enables the firewall, installs fail2ban, configures unattended upgrades, sets up swap, hardens sysctl.

Built-in Observability

One command starts a Grafana stack on the server. patty obs open production sets up metrics, logs, and dashboards without manual configuration.

Container Management

List project containers, run commands inside them, exec into services. patty ps production and patty exec production npm run migrate.

Backups & Restore

Automated database dumps (MySQL, Postgres, MongoDB, Redis) and file backups. patty backup production creates them server-side and downloads them locally.

Design Decisions

No Server Agent

Everything runs over SSH. The server doesn't know PATTY exists. This means zero installation on the server, zero background processes to monitor, and zero security surface beyond what SSH already provides.

Node.js Over Go or Rust

PATTY orchestrates shell commands — it doesn't need raw speed. Node.js makes async subprocess management natural, and the npm ecosystem provides excellent CLI tools (commander, chalk, inquirer, ora) without building everything from scratch.

RSync Over Git Pull

Rsync is faster for small changes, works without a git repo on the server, and transparently handles binary files, uploads, and large directories. Git deploys are available as an opt-in for teams that prefer commit-based rollbacks.

Config Over Convention

A single JSON config file is explicit, checkable into version control, and easy to generate programmatically. No magic paths, no assumed project structures, no "it works on my machine."

Challenges

SSMUX and Connection Reliability

Long-running deploy operations over SSH can stall on flaky connections. I built multiplexed SSH control sockets (~/.patty/mux) that reuse a single TCP connection across multiple commands, reducing latency and eliminating connection buildup.

Detecting App Shapes

PATTY auto-detects whether a project is Docker Compose, a Dockerfile project, a Node app, or a static site — each with different deploy logic. The heuristic had to balance accuracy against simplicity: looking for docker-compose.yml, then Dockerfile, then package.json, then index.html.

Environment-Specific Configs

Docker Compose projects use the same docker-compose.yml for all environments but need different ports, env files, and volume mounts per environment. The generated per-environment Compose overrides handle this without duplicating the base config.

SSH Key and Passphrase Handling

Different servers use different keys, some with passphrases. PATTY supports --identity-file, --ask-password, and the SSH agent, covering everything from fresh VPS bootstrap to CI/CD pipeline keys.

Outcome

PATTY CLI is the deploy tool I wished I had across every project I've built. The key wins:

  • Zero server-side installation — bootstrap a new VPS and deploy in under 5 minutes
  • Consistent deploy workflow across projects regardless of stack
  • Built-in security posture checks that catch common misconfigurations
  • Database backups, restores, and file uploads as first-class commands, not afterthoughts
  • Observability stack that sets up in one command instead of hours of manual config

What I Learned

  • Orchestrating existing tools (SSH, rsync, Docker) is often better than replacing them. Each tool is already well-tested, well-documented, and optimized by years of development.
  • A good CLI has a progressive disclosure curve — simple defaults for beginners, flags for power users.
  • Server-side state is the hard part. Tracking what's deployed, what version, and on which server requires a local state file because you can't trust the server to always be reachable.
  • The most valuable feature isn't deploy speed — it's confidence. Status checks, rollbacks, and backups give the operator confidence to ship without fear.

Interested in this kind of work?

I build tools that make infrastructure boring — which is the highest compliment you can give a deploy system.