Bash deployment CLI

Ship Node apps to your server. No ceremony.

ShipNode deploys backends and static frontends over SSH, builds releases, flips the current symlink atomically, reloads PM2, verifies health, and rolls back when a release misbehaves.

npm install -D @devalade/shipnode
Trace the deploy path
deploy@vps-ams-04 release healthy
$ shipnode init framework Express package manager pnpm app type backend config shipnode.config.ts created $ shipnode deploy rsync ./ -> /var/www/api/releases/20260428194612 install pnpm install --frozen-lockfile build pnpm run build symlink current -> releases/20260428194612 pm2 reload api --update-env health GET /health 200 OK 47ms deployed https://api.example.com
releases/20260428180603 current -> 20260428194612 shared/.env
Install command copied

Release path

Three commands with an audit trail.

ShipNode stays intentionally boring on the server: timestamped folders, a shared env file, an atomic symlink, PM2 for processes, and Caddy for HTTPS.

01

Detect

Reads package.json and lockfiles to infer Express, NestJS, Next.js, Astro, React, Vue, Svelte, npm, yarn, pnpm, or bun.

shipnode init
02

Prepare

Installs Node.js, PM2, Caddy, jq, and the matching package manager on Ubuntu or Debian. This is the one-time server pass.

shipnode setup
03

Release

Builds, syncs, creates a timestamped release, switches the current symlink, reloads PM2, and checks health before declaring victory.

shipnode deploy
/var/www/myapp zero downtime layout
current -> releases/20260428194612
releases/
  20260428180603/
  20260428194612/
shared/
  .env
  ecosystem.config.cjs
.shipnode/
  releases.json
  deploy.lock

Why it exists

No magic, just good server hygiene.

ShipNode is for developers who want a real server without rebuilding a deploy platform from shell history. It keeps the moving parts plain: SSH, rsync, releases, PM2, Caddy, health checks, and rollback.

Docker/Kubernetes

Great at scale, heavy when you just need a VPS release path.

PaaS platforms

Fast to start, but pricing and platform rules become part of your architecture.

Manual SSH

Transparent, but easy to forget a step under pressure.

ShipNode

Keeps the server visible and turns repeatable release hygiene into commands.

shipnode.config.ts production
import { shipnode } from '@devalade/shipnode';

export default shipnode
  .backend()
  .ssh({ host: '203.0.113.10', user: 'deploy' })
  .deployTo('/var/www/api')
  .pm2('api', { instances: 2 })
  .port(3000)
  .domain('api.example.com')
  .healthCheck('/health')
  .nodeVersion('22')
  .pkgManager('pnpm')
  .build();

Library surface

A deploy tool made of readable shell modules.

The CLI is split into focused modules for validation, prompts, package managers, templates, database setup, users, releases, and commands. That keeps the tool small enough to inspect.

01

Zero-downtime releases

Timestamped directories and atomic symlink switching keep active traffic on the previous build until the new one is verified.

02

Health-gated deploys

Configurable endpoint checks decide whether the release survives or rolls back automatically.

03

PM2 and Caddy templates

Generated configs can be ejected, edited, and kept across future deploys.

04

Server hardening

SSH, firewall, fail2ban, app users, and sudo policies can be managed from the same CLI surface.

05

Profiles and CI

Deploy production, staging, or a custom config, then generate GitHub Actions workflows when you are ready.

06

Database and Redis setup

Optional PostgreSQL, MySQL, SQLite, and Redis provisioning prepares services on the target server.

Command map

Small verbs, practical control.

ShipNode does not hide the server. It gives you memorable commands for the tasks that normally scatter across SSH sessions, copied snippets, and half-finished notes.

Bootstrap

shipnode init Create shipnode.config.ts with framework-aware defaults
shipnode setup Install Node.js, PM2, Caddy, and package managers
shipnode doctor Run local and remote pre-flight diagnostics

Ship

shipnode deploy Build, sync, release, health-check
shipnode deploy --config shipnode.staging.config.ts Deploy a non-default environment
shipnode rollback --steps 2 Move current back two releases
shipnode unlock Clear a stale deploy lock

Operate

shipnode status Inspect PM2 and release state
shipnode logs Stream process logs
shipnode metrics Open the PM2 monitoring dashboard
shipnode restart Restart the application
shipnode stop Stop the application
shipnode run "<cmd>" Run a one-off command on the server

Config & env

shipnode env Upload local .env to the server
shipnode config show Print the resolved configuration
shipnode config validate Validate the configuration file
shipnode eject Eject PM2/Caddy templates for customization

Security & team

shipnode harden Apply SSH, firewall, and fail2ban hardening
shipnode doctor --security Run a security audit on the server
shipnode user sync Sync .shipnode/users.yml to the server
shipnode user list List non-system users on the server

Backup & edge

shipnode backup setup Install backup script and systemd timer
shipnode backup run Run a backup immediately to S3
shipnode cloudflare init Provision a Cloudflare Tunnel for the app
shipnode cloudflare status Show cloudflared and tunnel status

CI & self-update

shipnode ci github Generate a GitHub Actions deploy workflow
shipnode ci env-sync Sync .env into GitHub repository secrets
shipnode upgrade Upgrade shipnode to the latest version

Detection matrix

Bring the stack you already picked.

ShipNode reads the local project, picks the right install/build/start commands, and lets config override the rare case where auto-detection is not enough.

Backends

ExpressNestJSFastifyKoaHonoAdonisJS

Full-stack

Next.jsNuxtRemixAstro

Frontends

ReactVueSvelteAngularSolidJS

Package managers

npmyarnpnpmbun

Start shipping

Install once. Deploy from the project folder.

npm install -D @devalade/shipnode