@node-software-ts/server-cli
v1.5.0
Published
Deploy and manage Next.js, Nuxt.js, Laravel (Supervisor) and Socket apps on a Linux server.
Maintainers
Readme
server-cli
Deploy and manage Next.js, Nuxt.js, Laravel (Supervisor), and Socket.IO apps on a Linux/cPanel server using PM2 and supervisord.
npm i -g @node-software-ts/server-cli
server --versionHow it works
Each deploy command runs a fixed pipeline:
- Port check — fails fast if the port is already bound by a different app
- Install —
npm install(ornpm run buildfor Next.js) - Artifacts — writes
.htaccess, injects.envkeys, syncs config files chown— sets ownership to the project user (skippable with--no-chown)- process.json — upserts the PM2 entry, sorts all apps by port (descending)
- PM2 —
pm2 starton first deploy,pm2 restarton subsequent deploys - pm2 save — persists the process list across reboots
- Health check — HTTP or Socket.IO probe to confirm the app is responding
All steps log their number ([1/13]), so you always know where you are.
Commands
server nuxt <dir> <port>
Deploys a Nuxt 4 / Nitro app (built output must exist at .output/server/index.mjs).
server nuxt /home/practice/public_html 4988What it does:
- Strips foreign
@img/sharpbinaries frompackage-lock.json(cross-platform safety) - Removes and reinstalls
.output/server/node_modules - Writes a reverse-proxy
.htaccesspointing to the port - Clears nginx cache (
whmapi1 nginxmanager_clear_cache) - Locks
.htaccessagainst accidental overwrite (chattr +ia) - Registers / restarts the PM2 cluster process
server next <dir> <port>
Deploys a Next.js app (SSR or static export).
server next /home/practice/provider.practice.4hoste.com 4990What it does:
- Detects whether the project is a static export (
out/directory oroutput: 'export'in config)- SSR: starts
next startvia PM2 cluster - Static: copies
static-server.cjsand serves theout/directory
- SSR: starts
- Cleans and reinstalls
node_modules - Runs
npm run build - Writes
.htaccessand clears nginx cache
server socket <dir> <port>
Deploys a Socket.IO v4 Node.js server.
server socket /home/practice/dashboard.practice.4hoste.com 4995What it does:
- Clones
https://github.com/MohamedEMahmoud/node.gitinto<dir>/node/(skips if already present, removes.gitafter clone) - Runs
npm installinsidenode/ - Injects managed
.envkeys into the project root.env:APP_URL=https://<domain>NODE_HOST=<domain>·NODE_PORT=<port>·NODE_MODE=liveKEY,CERT,CA→ cPanel SSL cert paths for the domainSTORAGE,IMAGES,ROOMS→ filled only if missing (user values preserved)
- Adds
/node/node_modulesto.gitignore - Rewrites domain tokens in all
node/*.mddocs - Updates
node/SOCKET-EVENTS.mdwith current domain and port - Syncs any hardcoded port references in config files
- Starts PM2 with
instances: 1,exec_mode: cluster - Runs a Socket.IO handshake health check:
https://<domain>:<port>/socket.io/?EIO=4&transport=polling
server supervisor <dir>
Registers a Laravel project with supervisord for PHP queue workers.
server supervisor /home/practice/dashboard.practice.4hoste.comWhat it does:
- Reads
artisan+composer.jsonto confirm it's a Laravel project - Writes a
[program:laravel-<user>-<slug>]block to/etc/supervisord.conf - Reloads supervisord
- Waits up to 10 seconds for
laravel-worker.logto appear (confirms the worker started)
server auto [<type>] <dir>
Automatically picks the first free port from the firewall range and deploys.
server next auto /home/practice/provider.practice.4hoste.com
server nuxt auto /home/practice/public_html
server socket auto /home/practice/dashboard.practice.4hoste.com
server auto /home/practice/public_html # type auto-detectedPort selection logic:
- Reads all existing ports from
process.json - Finds the highest app port in the
1024–29999band - Scans sequentially from
highest + 1for the first unbound port - Falls back to firewall-declared ranges (
firewall-cmd→iptables→ufw) if no existing apps
server change [--domain <d>] [--port <p>] [<dir>]
Changes the domain or port of a live deployment without a full redeploy.
# Change port (auto-detect app from CWD)
cd /home/practice/public_html
server change --port=4989
# Change socket domain + port (also rewrites .env and SOCKET-EVENTS.md)
server socket change --domain=dashboard.practice.4hoste.com --port=4995 \
/home/practice/dashboard.practice.4hoste.comWhat it does:
- Looks up the PM2 entry in
process.jsonby project directory - Updates
PORTin the entry and rewritesprocess.json(sorted descending by port) - For socket type with
--domain: re-runsinjectSocketEnvBlockandrewriteSocketEventsDoc - Restarts the PM2 process (
pm2 restartorpm2 start) - Runs
pm2 save - Runs health check if domain is known
server restart [<dir|port|name>]
Restarts a running app. Runs a health check after the restart.
server restart /home/practice/public_html # by directory
server restart 4988 # by port
server restart practice-nuxt-practice.4hoste.com # by name
server restart all # restart everythingserver logs [<name>] [--lines <n>]
Tails PM2 logs. When called from a deployed project directory, the app name is auto-detected.
server logs # auto-detect from CWD
server logs practice-nuxt-practice.4hoste.com
server logs practice-nuxt-practice.4hoste.com --lines=100server stop <name|port>
Stops a PM2 process (keeps it in process.json).
server stop practice-nuxt-practice.4hoste.com
server stop 4988server delete <name|port>
Deletes a PM2 process and removes it from process.json.
server delete practice-nuxt-practice.4hoste.com
server delete 4988server status / server list
Lists all currently registered PM2 app names.
server list
server statusserver doctor
Checks the environment: Node.js, PM2, supervisord, whmapi1, process.json read/write, and whether running as root.
server doctorserver init [--yes]
Creates or updates ~/.server-cli/config.json with global defaults interactively.
Use --yes to skip all prompts.
server init
server init --yesserver self-update
Updates the CLI itself to the latest published version.
server self-update
server self-update --dry-run # preview onlyAuto-detection
When you don't specify a type, the CLI detects it from the project files:
| Detection order | Files checked | Detected type |
|----------------|---------------|---------------|
| 1 | .output/server/index.mjs or nuxt.config.* | nuxt |
| 2 | .next/ or next.config.* | next |
| 3 | node/app.js + socket.io in node/package.json | socket |
| 4 | artisan + composer.json | supervisor |
User is extracted from the path (/home/<user>/...), then --user, then $USER.
Domain is resolved in order:
--domainflag- cPanel userdata:
/var/cpanel/userdata/<user>/<domain>wheredocumentrootmatches the project dir - Directory basename (if it looks like a domain)
main_domainfrom/var/cpanel/userdata/<user>/mainserver_namefrom nginx.conffiles
PM2 name pattern: <user>-<type>-<domain> (or <user>-<type> if no domain).
Override with --name or name: in server.yaml.
Config files
Global config — ~/.server-cli/config.json
Created by server init. Controls PM2 process file path, supervisord config, and health check tuning.
{
"processJsonPath": "/root/Scripts/process.json",
"supervisordConfPath": "/etc/supervisord.conf",
"defaultUser": "root",
"healthcheck": {
"retries": 3,
"delayMs": 2000,
"timeoutMs": 5000
}
}Override the config path with --config=/path/to/config.json.
Per-project config — server.yaml
Place at the project root to set persistent defaults:
name: practice-nuxt-main # PM2 app name
type: nuxt # next | nuxt | socket | supervisor
port: 4988 # default port
env: # extra env vars merged into PM2 entry
NODE_ENV: productionPrecedence: built-in defaults → ~/.server-cli/config.json → server.yaml → CLI flags
Global flags
| Flag | Short | Effect |
|------|-------|--------|
| --dry-run | | Print every action, execute nothing. process.json is not written. |
| --verbose | -v | Stream child process output (npm, pm2) directly to terminal |
| --yes | -y | Non-interactive mode — accept all defaults |
| --no-healthcheck | | Skip HTTP/socket health check after deploy |
| --no-chown | | Skip chown -R user:user dir step |
| --user <u> | | Override Linux user (auto-detected from path) |
| --name <n> | | Override PM2 app name |
| --domain <d> | | Override domain (used for health check and socket SSL paths) |
| --config <path> | | Use a custom global config JSON |
All flags accept both --flag value (space) and --flag=value (equals) syntax.
process.json
The shared PM2 ecosystem file (default: /root/Scripts/process.json).
- Every deploy upserts the entry for the project (matched by
cwd) - After every write the file is sorted by PORT descending
--dry-runnever writes to this file
Health check
After every deploy and restart, the CLI probes the app:
- next / nuxt: HTTP GET
http://<domain>/— passes on 2xx or 3xx - socket: Socket.IO handshake
https://<domain>:<port>/socket.io/?EIO=4&transport=polling— passes when response body starts with0
Skip with --no-healthcheck. Tune retries/timeout in ~/.server-cli/config.json.
Exit codes
| Code | Meaning | Common fix |
|------|---------|------------|
| 0 | Success | — |
| 10 | Bad arguments | Check command syntax |
| 20 | Missing or empty directory | Ensure build artifacts exist |
| 30 | Port in use / no free port | Use server stop <port> or pick a different port |
| 40 | Unknown project type | Pass type explicitly: server next ... |
| 50 | Build failed | Check npm run build output with --verbose |
| 60 | PM2 or permission error | Ensure pm2 is installed; check file permissions |
| 70 | Supervisor failure | Check supervisord is installed and running |
| 80 | Health check failed | App started but not responding; check server logs |
| 90 | Deploy lock held | Another deploy is running for the same app |
License
MIT
