Skip to content

Repository files navigation

@deorta-dev/remotly

Remote Deploy Toolkit — Automate deployment of Node.js / NestJS applications to remote servers via SSH.

npm install -g @deorta-dev/remotly
npx @deorta-dev/remotly deploy

Quick Start

# 1. Create configuration
remotly init
ini
# 2. Edit .remotly-config/global.yml with your server details

# 3. Deploy
remotly deploy                    # interactive
remotly deploy --app api          # headless
remotly deploy --parallel         # multiple apps in parallel

Documentation


CLI Commands

Command Description
remotly deploy Deploy applications to remote servers
remotly list List available applications
remotly init Create global deploy configuration
remotly init-app <name> Create per-app configuration
remotly status Check status of deployed services
remotly logs <app> Fetch logs from a deployed service
remotly versions <app> List available deployment versions
remotly rollback <app> Rollback to a previous version

deploy Options

Flag Description Default
-a, --app <name> App to deploy (repeatable) prompt
-c, --config <dir> Config directory ./.remotly-config
-p, --parallel Deploy in parallel false
--no-build Skip build step false
--dry-run Preview without executing false
--env-file <path> Load additional .env file

logs Options

Flag Description Default
-n, --lines <number> Number of log lines 50
-c, --config <dir> Config directory ./.remotly-config

rollback Options

Flag Description Default
-v, --version <version> Specific version to rollback to previous version
-c, --config <dir> Config directory ./.remotly-config

Exit Codes

Code Meaning
0 All deploys successful
1 At least one deploy failed
2 Configuration error
3 SSH connection error

Configuration

# .remotly-config/global.yml
global:
  transport:
    type: ssh
    host: "your-server.com"
    port: 22
    username: "deploy"
    privateKeyPath: "~/.ssh/id_rsa"

  nodeVersion: "20"
  installPath: "/opt/services/{appName}"

  # Service manager: "systemd" (default) or "pm2"
  serviceManager: "systemd"

  # Optional: pre-deploy commands
  preDeploy:
    - command: "mkdir -p /var/log/{appName}"
      description: "Create log directory"

  # Optional: system dependencies
  systemDependencies:
    check: true
    apt:
      - libnss3

  # Optional: post-deploy commands
  postDeploy:
    - command: "systemctl reload nginx"
      description: "Reload reverse proxy"

  # Optional: versioning (timestamped releases + rollback)
  versioning:
    enabled: true
    keepReleases: 5

  # Optional: extra files/directories to upload
  extraFiles:
    - source: "scripts/init.sh"
    - source: "config/"
      destination: "/etc/myapp/config/"

  env:
    NODE_ENV: "production"

apps:
  api:
    env:
      PORT: "3000"

Resolution order: Defaults → globalapps[appName] → per-app file → REMOTLY_* env → CLI flags

See CONFIG_SCHEMA.md for the complete parameter reference.


Programmatic API

import { deploy, deployMultiple, dryRun, scanApps, use } from '@deorta-dev/remotly';
import { firstValueFrom } from 'rxjs';

// Deploy a single app
const result = await firstValueFrom(deploy({ appName: 'api' }));
console.log(result.success); // true | false

// Deploy multiple
const results = await firstValueFrom(
  deployMultiple([
    { appName: 'api' },
    { appName: 'worker' },
  ], { parallel: true }),
);

// Preview
const plan = await firstValueFrom(dryRun({ appName: 'api' }));
console.log(plan.steps);

All async operations return RxJS Observables. The only exception is CLI command handlers which use firstValueFrom to bridge Observable → Promise.


Pipeline

# Step Phase Description
1 pre-deploy-commands build Execute pre-deploy commands (local/remote)
2 build build Execute build system
3 validate-output build Verify output directory
4 connect transfer SSH connection
5 ensure-node transfer Verify/install Node.js on remote
6 system-dependencies transfer Check/install system packages
7 prepare-dir transfer Create remote directories (or release dir with versioning)
8 transfer transfer Upload built files
9 upload-extra-files transfer Upload extra files/directories
10 symlink-current transfer Update current symlink (versioning only)
11 install install Remote npm install
12 service-install install Install service (systemd or PM2)
13 service-start start Start/restart service
14 verify verify Verify service is active
15 post-deploy-commands verify Execute post-deploy commands (local/remote)

Each phase has onBefore and onAfter hooks that plugins can intercept.


Plugins

import { use } from '@deorta-dev/remotly';
import { nxPlugin } from '@deorta-dev/remotly-plugin-nx';
import { systemdPlugin } from '@deorta-dev/remotly-plugin-systemd';
import { sshPlugin } from '@deorta-dev/remotly-plugin-ssh';

use(nxPlugin());
use(systemdPlugin());
use(sshPlugin());

Official Plugins

Package Purpose
@deorta-dev/remotly-plugin-nx NX scanner + builder
@deorta-dev/remotly-plugin-systemd systemd service manager
@deorta-dev/remotly-plugin-ssh SSH transport
@deorta-dev/remotly-plugin-pm2 PM2 service manager
@deorta-dev/remotly-plugin-docker Docker deployment

Creating a Plugin

import type { RemotlyPlugin } from '@deorta-dev/remotly';
import { of } from 'rxjs';

export const myPlugin = (): RemotlyPlugin => ({
  name: 'my-plugin',
  version: '1.0.0',
  hooks: {
    onAfterBuild: (ctx) => {
      ctx.log('info', `Build completed for ${ctx.appName}`);
      return of(void 0);
    },
  },
  transports: [
    {
      name: 'my-transport',
      transport: { /* ... */ },
    },
  ],
});

PM2 Support

Remotly supports PM2 as an alternative to systemd for process management. Set serviceManager: "pm2" in your config:

global:
  serviceManager: "pm2"

PM2 is auto-installed on the remote server if not present. The pipeline generates an ecosystem.config.js file.

Versioning & Rollback

Enable timestamped releases to support rollback:

global:
  versioning:
    enabled: true
    keepReleases: 5

When versioning is enabled, each deploy creates a timestamped directory under releases/{YYYYMMDDHHmmss}/, and current becomes a symlink to the active release. Old releases beyond keepReleases are automatically cleaned up.

# List all versions
remotly versions api

# Rollback to previous version
remotly rollback api

# Rollback to specific version
remotly rollback api -v 20240711143000

Extra Files

Upload additional files or directories with the deploy:

global:
  extraFiles:
    - source: "scripts/init.sh"              # relative project path
    - source: "config/"
      destination: "/etc/myapp/config/"      # remote destination (optional)

If destination is omitted, the file is uploaded relative to the target installation directory.

Secrets

Placeholders ${VAR_NAME} are resolved by chaining providers:

env:
  DATABASE_URL: "${DB_PASSWORD}"

Order: process.env.env file → plugin providers (AWS Secrets Manager, Vault, etc.)


Architecture

CLI layer (commander + inquirer + chalk)
    ↓
Core layer (Deployer + PipelineRunner)
    ↓
Scanner → Builder → Transport → ServiceManager → Secrets

Principles:

  • Functional programming — no classes, factory functions
  • RxJS Observables — no Promises in library code
  • No shared state — each deploy is independent
  • Plugin-based — everything is extensible

Development

npm run build          # tsup (ESM + CJS + DTS)
npm run dev            # watch mode
npm run typecheck      # tsc --noEmit
npm run lint           # eslint
npm test               # vitest
npm run test:coverage  # with coverage report

License

MIT — @deorta-dev

About

Remote Deploy Toolkit — Automate deployment of Node.js / NestJS applications to remote servers via SSH.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages