Homeβ€Ί JavaScriptβ€Ί Nodemon: Stop Restarting Node.js by Hand Every Single Time

Nodemon: Stop Restarting Node.js by Hand Every Single Time

Where developers are forged. Β· Structured learning Β· Free forever.
πŸ“ Part of: Node.js β†’ Topic 17 of 17
Nodemon auto-restarts your Node.
πŸ§‘β€πŸ’» Beginner-friendly β€” no prior JavaScript experience needed
In this tutorial, you'll learn:
  • Nodemon belongs in devDependencies only. The moment it appears in a production dependency list or a production process manager, something has gone wrong with your deployment pipeline.
  • The infinite restart loop β€” where Nodemon restarts because the running app writes to a watched directory β€” is the most common Nodemon-related incident on development teams. Add your logs/, dist/, and coverage/ directories to the 'ignore' array in nodemon.json before you ever hit this.
  • Reach for nodemon.json the moment your project has more than one developer or more than two configuration flags. Inline flags in npm scripts don't scale, can't be documented inline, and create config drift between team members.
✦ Plain-English analogy ✦ Real code with output ✦ Interview questions
⚑ Quick Answer
Imagine you're editing a Google Doc and every time you change a word, someone has to manually walk over, close the document, and reopen it before you can see the update. That's exactly what developing a Node.js app without Nodemon feels like β€” you change one line, then you Ctrl+C the server, retype 'node server.js', and wait. Nodemon is the person standing over your shoulder who sees you saved the file and restarts the server automatically before you've even lifted your hands off the keyboard. You write code, flip to the browser, and your change is already live.

I watched a junior dev spend forty-five minutes debugging what turned out to be a change he'd made that was never actually running β€” because he'd forgotten to restart his server after saving the file. Not once. Three times in the same afternoon. That's not a character flaw; it's a workflow problem, and Nodemon exists to eliminate it entirely.

Node.js doesn't watch your files. When you start a Node.js server with 'node app.js', it loads your code once into memory and runs it. The moment you change a file and save it, Node.js has absolutely no idea. It's still running the old code. The only way to pick up your changes is to kill the process and restart it manually. In a fast feedback loop where you're editing, checking, editing, checking β€” that's a tax you pay hundreds of times a day. It sounds minor until you've done it for three hours straight and your muscle memory is shot and you've introduced a bug because you were staring at stale output.

After reading this, you'll be able to install Nodemon, wire it into any Node.js project, configure it to watch exactly the files you care about, ignore the ones you don't, and set it up so that every developer who clones your repo gets the same workflow automatically. You'll also know the three configuration mistakes that cause Nodemon to restart infinitely or miss changes entirely β€” both of which I've seen derail entire dev sessions.

Why Manual Restarts Kill Your Flow (And What Node.js Actually Does)

Before you can appreciate what Nodemon does, you need to understand why Node.js doesn't auto-restart by itself β€” and it's not an oversight, it's a deliberate design decision.

When you run 'node server.js', Node reads that file, compiles it to bytecode, loads it into memory, and starts executing. From that point on, Node is a running process. It owns a port, it holds database connections, it has state in memory. It doesn't scan your file system for changes because that's not its job. Its job is to serve requests as fast as possible. Polling the disk constantly would waste CPU cycles and slow everything down. So Node.js makes a deal with you: 'I'll be blazing fast, but you handle restarts.'

In development, that deal is terrible. You're changing files every two minutes. Every change requires a Ctrl+C and 'node server.js' again. Worse, you'll forget. You'll save a file, test your endpoint, get the old behaviour, spend ten minutes convinced your logic is wrong, and then finally notice you never restarted. I've seen this exact scenario cause someone to revert a perfectly correct fix because they thought it 'didn't work.'

Nodemon solves this by wrapping the Node.js process. It watches your project files for changes, and when it detects one, it kills the current Node.js process and starts a new one automatically. Your server is always running the latest version of your code. You save, you flip to Postman or your browser, your change is there.

BasicExpressServer.js Β· JAVASCRIPT
123456789101112131415161718192021222324252627282930313233343536373839404142
// io.thecodeforge β€” JavaScript tutorial

// A minimal Express server β€” the kind you'd have at the very start
// of building a REST API for, say, a product catalogue service.
// Without Nodemon, every change to this file means manually restarting.

const express = require('express');

const app = express();
const PORT = 3000;

// Parse incoming JSON request bodies (needed for POST/PUT endpoints)
app.use(express.json());

// A simple health-check route β€” the first thing any real API should have.
// Load balancers and monitoring tools ping this to know the service is alive.
app.get('/health', (req, res) => {
  res.status(200).json({
    status: 'ok',
    timestamp: new Date().toISOString()
  });
});

// A placeholder products route β€” imagine this hitting a database next.
app.get('/products', (req, res) => {
  res.status(200).json({
    products: [
      { id: 1, name: 'Wireless Keyboard', price: 79.99 },
      { id: 2, name: 'USB-C Hub',        price: 49.99 }
    ]
  });
});

// Start the server and bind it to the port.
// Without Nodemon: change ANYTHING above, and this output is lying to you
// until you manually kill and restart the process.
app.listen(PORT, () => {
  console.log(`Product API running on http://localhost:${PORT}`);
  console.log('Tip: You are running the code that was loaded at START TIME.');
  console.log('Changes you make now will NOT appear until you restart.');
});
β–Ά Output
Product API running on http://localhost:3000
Tip: You are running the code that was loaded at START TIME.
Changes you make now will NOT appear until you restart.
⚠️
The Invisible Bug Trap:The most common beginner mistake isn't a syntax error β€” it's testing against a running server that's still executing code from three saves ago. If you ever think 'this should be working but it's not,' the very first question is: did you restart? Nodemon makes this question irrelevant.

Installing Nodemon and Running Your First Auto-Restarting Server

Here's exactly how to get Nodemon running from nothing. Two decisions upfront: global install vs. local install. I'll tell you the right answer and explain why the other one causes problems on a team.

Global install means you run 'npm install -g nodemon' and the 'nodemon' command becomes available everywhere on your machine. Sounds convenient. The problem is that your teammates might have a different version, or a CI/CD pipeline won't have it at all. Six months later someone clones your repo, runs the start script, gets 'nodemon: command not found', and wastes half an hour figuring out why. Don't do global installs for project tooling.

Local install β€” 'npm install --save-dev nodemon' β€” puts Nodemon in your project's node_modules folder and records it in package.json under devDependencies. Anyone who clones the repo and runs 'npm install' gets the exact same version of Nodemon automatically. This is the only approach that works on a team or in CI. The '--save-dev' flag is critical: Nodemon is a development tool, not something that should ever run in production. Separating dev from production dependencies keeps your production Docker image lean and your deployment safe.

Once installed locally, you can't just type 'nodemon server.js' in the terminal β€” your shell doesn't know where to find it. You access it through npm scripts in package.json, which automatically add your local node_modules/.bin to the PATH when running scripts.

PackageJsonSetup.json Β· JAVASCRIPT
12345678910111213141516171819202122232425262728293031323334353637383940
// io.thecodeforge β€” JavaScript tutorial

// This is your package.json β€” the configuration file at the root
// of every Node.js project. Think of it as your project's ID card
// and instruction manual combined.

// After running: npm install --save-dev nodemon
// Your package.json should look something like this:

{
  "name": "product-catalogue-api",
  "version": "1.0.0",
  "description": "REST API for product catalogue management",

  "scripts": {
    // 'start' is the production command β€” plain node, no Nodemon.
    // Never run Nodemon in production. It restarts on file changes,
    // which is the last thing you want on a live server.
    "start": "node server.js",

    // 'dev' is what every developer runs locally.
    // npm run dev => npm finds nodemon in node_modules/.bin automatically.
    // The --rs flag lets you manually type 'rs' + Enter in the terminal
    // to force a restart without changing any file (handy for env var changes).
    "dev": "nodemon server.js"
  },

  "dependencies": {
    // express belongs here β€” it runs in production
    "express": "^4.18.2"
  },

  "devDependencies": {
    // nodemon belongs HERE, not in dependencies.
    // --save-dev puts it here automatically.
    // This means 'npm install --production' skips it entirely.
    "nodemon": "^3.0.1"
  }
}
β–Ά Output
// After running: npm run dev
// You will see output similar to this in your terminal:

[nodemon] 3.0.1
[nodemon] to restart at any time, enter `rs`
[nodemon] watching path(s): *.*
[nodemon] watching extensions: js,mjs,cjs,json
[nodemon] starting `node server.js`
Product API running on http://localhost:3000

// Now edit ANY .js or .json file and save. You will immediately see:
[nodemon] restarting due to changes...
[nodemon] starting `node server.js`
Product API running on http://localhost:3000
⚠️
Senior Shortcut:Always have a separate 'start' and 'dev' script in package.json. 'start' runs plain node for production, 'dev' runs nodemon for local development. If someone accidentally runs 'npm run dev' on a production server, Nodemon watching for file changes on a live machine is a security and stability nightmare β€” plus, any deployment tool that touches the filesystem would trigger constant restarts.

Configuring Nodemon: Watch the Right Files, Ignore the Rest

Out of the box, Nodemon watches all .js, .mjs, .cjs, and .json files in your project directory and restarts on any change. For a tiny project that's fine. For anything real, it'll drive you insane.

Here's the real problem: Nodemon doesn't know the difference between your source code and generated files, log files, or temporary files. If your app writes to a log file on every request β€” which is completely normal β€” and that log file is inside your project directory, Nodemon sees the write, thinks your code changed, and restarts. Which triggers another request. Which writes to the log. Which triggers another restart. I've seen this infinite restart loop on three separate teams. It eats CPU, it makes your logs unreadable, and it looks like the world's most confusing bug because the server is constantly restarting for no visible reason.

The fix is a nodemon.json configuration file. It sits at the root of your project alongside package.json and gives you precise control over what Nodemon watches, what it ignores, and what file extensions trigger a restart. Every serious Node.js project should have one. It makes the development experience consistent for every person on the team β€” nobody's getting phantom restarts because of local tooling differences.

nodemon.json Β· JAVASCRIPT
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647
// io.thecodeforge β€” JavaScript tutorial

// nodemon.json β€” place this at your project root.
// Nodemon reads this automatically when it starts. No flags needed.

{
  // 'watch' tells Nodemon exactly which directories to monitor.
  // Only changes inside these folders will trigger a restart.
  // Here we're only watching our source code folder, not the whole project.
  "watch": ["src", "config"],

  // 'ext' lists the file extensions that should trigger a restart.
  // We add 'env' because environment config changes need a restart.
  // We DON'T add 'log', 'tmp', or 'json' from generated folders.
  "ext": "js,json,env",

  // 'ignore' is your safety net β€” anything Nodemon should never restart for.
  // This is where you stop the infinite restart loop before it starts.
  "ignore": [
    "src/**/*.test.js",   // Don't restart when test files change β€” tests run separately
    "src/**/*.spec.js",   // Same for spec files
    "logs/*",             // Log files change on every request β€” NEVER watch these
    "node_modules/*",     // npm install changes these β€” never restart for this
    "coverage/*",         // Test coverage reports are generated, not source code
    "dist/*",             // Built/compiled output β€” not source code
    ".git/*"              // Git internals β€” definitely not source code
  ],

  // 'delay' adds a debounce in milliseconds before Nodemon restarts.
  // Without this, saving multiple files quickly (e.g. a refactor touching 5 files)
  // triggers 5 rapid restarts in a row instead of one clean restart.
  // 500ms is the sweet spot β€” long enough to batch saves, short enough to feel instant.
  "delay": 500,

  // 'env' injects environment variables specifically for the dev session.
  // This means you don't need a separate .env loader for local development basics.
  // For production, these come from your actual environment β€” NOT from here.
  "env": {
    "NODE_ENV": "development",
    "PORT": "3000"
  },

  // 'verbose' set to true prints exactly which file changed and why Nodemon restarted.
  // Turn this on when debugging a phantom restart β€” it'll name the culprit file immediately.
  "verbose": false
}
β–Ά Output
// With this nodemon.json in place, running 'npm run dev' produces:

[nodemon] 3.0.1
[nodemon] to restart at any time, enter `rs`
[nodemon] watching path(s): src/**/* config/**/*
[nodemon] watching extensions: js,json,env
[nodemon] starting `node server.js`
Product API running on http://localhost:3000

// Edit src/routes/products.js and save β€” you see:
[nodemon] restarting due to changes...
[nodemon] starting `node server.js`
Product API running on http://localhost:3000

// Write to logs/app.log β€” Nodemon stays silent. No restart. Perfect.
⚠️
The Infinite Restart Loop:If your app writes to any file inside a watched directory β€” logs, generated files, cache files β€” Nodemon will restart every time that file is written, which causes the app to start again, write to the file again, and restart again forever. Symptom: '[nodemon] restarting due to changes...' appearing continuously with no file saves on your end. Fix: add the offending directory to the 'ignore' array in nodemon.json. Run with 'verbose: true' first to see exactly which file is causing it.

Nodemon With ES Modules, TypeScript, and Multi-Process Apps

Basic Nodemon for a CommonJS Express app is straightforward. But modern Node.js projects use ES Modules ('import'/'export' instead of 'require'), TypeScript, or even multiple processes. Each one needs a small adjustment or you'll hit a wall and not know why.

ES Modules in Node.js require either a .mjs extension or '"type": "module"' in your package.json. Nodemon itself handles this fine β€” the restart mechanism doesn't care about your module system. But the command you give Nodemon matters. If you're using '"type": "module"' and running Node 18+, plain 'nodemon server.js' still works. No change needed there.

TypeScript is the one that trips people up. Nodemon can't execute TypeScript directly β€” Node.js can't either. You need a TypeScript executor. The most common choice for development is 'ts-node'. You tell Nodemon to use ts-node as the executor instead of node, and it handles compilation on the fly. For larger projects, 'tsx' has become the faster alternative β€” it's built on esbuild and noticeably quicker for big codebases. I've seen teams on large TypeScript monorepos where ts-node's restart time was 8-12 seconds per change. Switching to tsx dropped that to under 2 seconds. That compounds over a full work day into real productivity.

For any of these setups, the exec key in nodemon.json is your control lever. It replaces 'node' with whatever executor you need.

nodemon.typescript.json Β· JAVASCRIPT
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849
// io.thecodeforge β€” JavaScript tutorial

// nodemon.json configured for a TypeScript Node.js project.
// This is the setup you'd use for a TypeScript Express API β€”
// the kind of thing you'd find in a fintech or SaaS backend.

{
  // 'watch' stays the same β€” source code directories only
  "watch": ["src", "config"],

  // Add 'ts' to the extension list β€” Nodemon now triggers on .ts file changes too
  "ext": "ts,js,json",

  "ignore": [
    "src/**/*.test.ts",
    "src/**/*.spec.ts",
    "dist/*",           // TypeScript compiles TO dist/ β€” never watch compiled output
    "node_modules/*",
    "logs/*"
  ],

  // 'exec' is the key that replaces 'node' with a different runtime/executor.
  // Here we use 'tsx' β€” dramatically faster than ts-node for large codebases.
  // Install it with: npm install --save-dev tsx
  // For ts-node instead: "exec": "ts-node src/server.ts"
  "exec": "tsx src/server.ts",

  "delay": 500,

  "env": {
    "NODE_ENV": "development",
    "PORT": "3000"
  }
}

// ─────────────────────────────────────────────────────────
// Your package.json scripts section with TypeScript:
// ─────────────────────────────────────────────────────────
// {
//   "scripts": {
//     "start": "node dist/server.js",        // Production: runs compiled JS
//     "build": "tsc",                         // Compile TS to JS for production
//     "dev":   "nodemon"                      // Dev: nodemon reads nodemon.json automatically
//   }
// }
//
// Note: 'nodemon' with no arguments reads nodemon.json and uses the 'exec' command.
// This is cleaner than putting everything in the npm script as flags.
β–Ά Output
[nodemon] 3.0.1
[nodemon] to restart at any time, enter `rs`
[nodemon] watching path(s): src/**/* config/**/*
[nodemon] watching extensions: ts,js,json
[nodemon] starting `tsx src/server.ts`
Product API (TypeScript) running on http://localhost:3000

// Edit src/routes/products.ts and save:
[nodemon] restarting due to changes...
[nodemon] starting `tsx src/server.ts`
Product API (TypeScript) running on http://localhost:3000
πŸ”₯
Interview Gold:Nodemon is a development-only tool. In production, Node.js process management belongs to PM2 (which handles clustering, zero-downtime restarts, and process resurrection on crash) or to your container orchestrator like Kubernetes. If an interviewer asks 'how do you handle Node.js process management in production?' and you say 'Nodemon,' you've just told them you've never shipped a real production service.
Feature / AspectNodemonNode.js --watch (built-in, Node 18+)
Installation requiredYes β€” npm install --save-dev nodemonNo β€” built into Node.js 18+
Configuration file supportFull nodemon.json with ignore, delay, exec, envLimited β€” flags only, no dedicated config file
Custom executor (e.g. tsx, ts-node)Yes β€” via 'exec' key in nodemon.jsonNo β€” only runs node itself
File extension filteringYes β€” 'ext' key, granular controlBasic β€” --watch-path flag only
Ignore patternsGlob patterns in 'ignore' arrayNot supported as of Node 20
Restart debounce / delayYes β€” 'delay' key in millisecondsNo β€” restarts immediately on every change
Manual restart command ('rs')Yes β€” type 'rs' in terminal to force restartNo
TypeScript / ESM project supportYes β€” via custom 'exec' commandOnly plain .js and .mjs files
Maturity / community10+ years, battle-tested, 25M+ weekly downloadsExperimental in Node 18, stable in Node 22 but limited
Best forAny real project, especially TypeScript or complex setupsQuick one-off scripts, zero-dependency environments

🎯 Key Takeaways

  • Nodemon belongs in devDependencies only. The moment it appears in a production dependency list or a production process manager, something has gone wrong with your deployment pipeline.
  • The infinite restart loop β€” where Nodemon restarts because the running app writes to a watched directory β€” is the most common Nodemon-related incident on development teams. Add your logs/, dist/, and coverage/ directories to the 'ignore' array in nodemon.json before you ever hit this.
  • Reach for nodemon.json the moment your project has more than one developer or more than two configuration flags. Inline flags in npm scripts don't scale, can't be documented inline, and create config drift between team members.
  • Nodemon is a development convenience tool, not a production process manager. If you're thinking about restart behaviour, crash recovery, or zero-downtime deploys in production β€” you want PM2, systemd, or your container orchestrator. Nodemon has never been designed for that job and won't do it reliably.

⚠ Common Mistakes to Avoid

  • βœ•Mistake 1: Installing Nodemon as a regular dependency with 'npm install nodemon' instead of 'npm install --save-dev nodemon' β€” Nodemon ends up in production Docker images, inflating container size and introducing file-watching overhead on live servers where it should never run β€” Fix: move it to devDependencies manually in package.json, or reinstall with the --save-dev flag and remove it from dependencies.
  • βœ•Mistake 2: Watching a directory that the running app writes to β€” such as a logs/ folder inside src/ β€” causing '[nodemon] restarting due to changes...' to loop infinitely with no user-triggered saves β€” Fix: add the log directory to the 'ignore' array in nodemon.json, and run Nodemon with 'verbose: true' temporarily to identify exactly which file is causing the phantom restarts.
  • βœ•Mistake 3: Putting Nodemon configuration as CLI flags in package.json scripts ('nodemon --watch src --ext ts,js --delay 500 server.js') instead of a nodemon.json file β€” the script becomes unreadable, teammates add conflicting flags, and the configuration isn't version-controlled cleanly β€” Fix: move all flags to a nodemon.json file at the project root and run just 'nodemon' in the npm script; it reads the config file automatically.
  • βœ•Mistake 4: Using Nodemon in production β€” either accidentally via a 'start' script that calls nodemon, or because a developer copied the 'dev' script β€” any file change on the production server (log rotation, deployment file writes, config updates) triggers a restart mid-request, causing dropped connections and 503 errors β€” Fix: ensure 'start' in package.json always uses plain 'node', and configure your production process manager (PM2, systemd, or Kubernetes) to use the start script, never the dev script.

Interview Questions on This Topic

  • QNodemon watches for file changes and restarts your Node.js process β€” but what's the underlying mechanism it uses to detect changes, and what are the implications of that mechanism on network file systems like NFS or Docker volume mounts on Windows?
  • QWhen would you choose Nodemon over Node.js's built-in --watch flag introduced in Node 18, and at what point would you move away from both in favour of a tool like PM2 or a containerised hot-reload setup?
  • QIf a team reports that their Node.js dev server is restarting every 2-3 seconds with no file saves happening, and Nodemon is showing '[nodemon] restarting due to changes...' repeatedly β€” walk me through exactly how you'd diagnose and fix this in under five minutes.

Frequently Asked Questions

Does nodemon work with TypeScript projects?

Yes, but you need to pair it with a TypeScript executor because Node.js can't run .ts files directly. Add 'tsx' or 'ts-node' to your devDependencies, then set the 'exec' key in your nodemon.json to point at your TypeScript entry file β€” for example, '"exec": "tsx src/server.ts"'. Nodemon handles the file watching; tsx or ts-node handles the TypeScript compilation on the fly. For large codebases, tsx is significantly faster than ts-node because it uses esbuild under the hood.

What's the difference between nodemon and node --watch?

Node --watch is built into Node.js 18+ and requires no installation, while Nodemon is a separate package with far more configuration options. Use node --watch for simple scripts where you have no custom executor, no ignore patterns, and no TypeScript involved. Use Nodemon for any real project β€” especially TypeScript projects, projects with log files that would cause phantom restarts, or any team environment where consistent configuration via nodemon.json matters.

How do I stop nodemon from restarting when I don't want it to?

You have two options. To prevent restarts for specific files or directories, add them to the 'ignore' array in nodemon.json using glob patterns β€” for example, 'logs/' or 'src//.test.js'. To temporarily pause and resume watching without stopping the process, press Ctrl+C once (not twice) β€” though this actually stops it. The better approach is fixing the ignore config so that files you don't care about are never watched in the first place. Also set a 'delay' of 500ms to debounce rapid multi-file saves into a single restart.

Is it safe to run nodemon in a Docker container during development?

It works, but file change detection is the landmine. Nodemon uses filesystem events (via chokidar) to detect changes. On Linux-to-Linux Docker setups this works fine. On Docker for Mac or Docker for Windows with volume mounts, the host OS filesystem events don't propagate into the container reliably β€” meaning Nodemon never sees your file changes and never restarts. The fix is to enable Nodemon's polling mode, which checks for changes on a timer instead of relying on filesystem events: set '"legacyWatch": true' in nodemon.json. It's more CPU-intensive but it works across all volume mount types. I've seen entire teams lose a morning to this on first Docker setup.

πŸ”₯
Naren Founder & Author

Developer and founder of TheCodeForge. I built this site because I was tired of tutorials that explain what to type without explaining why it works. Every article here is written to make concepts actually click.

← PreviousNVM β€” Node Version Manager: Install and Switch Node Versions
Forged with πŸ”₯ at TheCodeForge.io β€” Where Developers Are Forged