CLI Basics for Node
10 examples for curl, jq, ss, and log tailing on Linux servers running Node.js APIs - 7 basic and 3 intermediate.
Prerequisites
- SSH access to a staging or production VM/container host (read-only role for juniors).
curl,jq, andssinstalled (default on most Linux images).- Know your service port (
3000default) and health route (/health).
Basic Examples
1. Health Check with curl
curl -sf http://localhost:3000/health
curl -sf http://localhost:3000/ready-ssilent,-ffail on HTTP 4xx/5xx (non-zero exit)- Use in load balancer probes and deploy smoke scripts
readyshould fail when Postgres/Redis unreachable
# With timing for latency triage
curl -sf -w "time_total=%{time_total}\n" -o /dev/null http://localhost:3000/health2. POST JSON to an API
curl -sf -X POST http://localhost:3000/orders \
-H "Content-Type: application/json" \
-H "x-request-id: cli-smoke-$(date +%s)" \
-d '{"customerId":"550e8400-e29b-41d4-a716-446655440000","items":[{"sku":"550e8400-e29b-41d4-a716-446655440001","quantity":1}]}'- Always send
x-request-idfor log correlation - Pipe through
jqfor readable output:| jq .
3. Filter JSON Logs with jq
# Last 100 error lines from container stdout
kubectl logs deploy/orders-api --tail=500 | jq -c 'select(.level=="error")' | tail -20
# Trace one request
kubectl logs deploy/orders-api --since=10m | jq -c 'select(.requestId=="abc-123")'- Pino logs are one JSON object per line - perfect for
jq select(.statusCode >= 500)for HTTP errors in request_complete events
Related: pino - log field conventions
4. List Listening Ports with ss
ss -tlnp | grep node
ss -tlnp 'sport = :3000'ssreplaces deprecatednetstaton modern Linux- Confirm Node bound
0.0.0.0:3000, not127.0.0.1only in containers - Check TIME-WAIT accumulation under load:
ss -s
5. Tail systemd Journal for Node Service
sudo journalctl -u orders-api -f --since "15 min ago"
sudo journalctl -u orders-api -p err --since today-ffollow live logs on VMs without Kubernetes- Pair with
systemctl status orders-apifor restart loops
6. Follow Docker Container Logs
docker logs -f --tail 200 orders-api
docker logs orders-api 2>&1 | jq -c 'select(.level=="error")' | tail -10- Node apps log to stdout - Docker captures it
- Do not
docker execto read log files that do not exist
7. DNS and Upstream Check from Server
curl -sf https://api.stripe.com/v1/charges -H "Authorization: Bearer sk_test_xxx" -o /dev/null -w "%{http_code}\n"
getent hosts postgres.internal- Distinguish app bug vs network/DNS failure during incidents
- Run from same network namespace as the app pod when possible
Intermediate Examples
8. Smoke Script in Repo
#!/usr/bin/env bash
set -euo pipefail
BASE_URL="${BASE_URL:-http://localhost:3000}"
curl -sf "$BASE_URL/health" | jq -e '.status == "ok"'
curl -sf "$BASE_URL/ready" | jq -e '.status == "ok"'
echo "smoke OK"- Commit to
bin/smoke.sh; run post-deploy in CI or SSH set -euo pipefailfails fast on first error
Related: Linux CLI Best Practices - script hygiene
9. Compare Response Headers (Caching, Auth)
curl -sI http://localhost:3000/orders/1 | grep -iE 'cache-control|x-request-id|content-type'-IHEAD request for metadata only- Verify CDN/cache headers on GET routes
10. Load Spot-Check with curl Loop
for i in $(seq 1 20); do
curl -sf -o /dev/null -w "%{http_code} %{time_total}\n" http://localhost:3000/health
done- Quick manual check before declaring incident resolved
- For real load tests use
k6orautocannon, not bash loops in production
FAQs
jq not installed on minimal container?
Run from bastion or debug pod with jq. Alternatively node -e parse JSON, but jq is faster for ops.
curl works locally but not from laptop to prod?
Expected - production is behind VPN or private network. SSH to bastion or kubectl port-forward first.
Related
- Process Inspection - top, lsof
- Linux CLI Best Practices - portable summary
- Incident Triage Skill - incident commands
Stack versions: This page was written for Node.js 24.18.0 (Active LTS), npm 10+, TypeScript 5.6+, Express 5, Fastify 5, and NestJS 11.