Migrate to RunxBuild and earn up to $50 in hosting credit on your first deposit.

Calculate your savings
unxBuild

Angular Project Run Command: ng serve, ng build, and What Ships

Sean

Platform Writer

Sep 10, 2026
8 min read

To run an Angular project locally the command is ng serve, which compiles the application in memory, starts a development server on port 4200 and rebuilds whenever you save. To produce something you can actually deploy the command is ng build, which writes optimised files to a dist folder and never starts a server at all. Nearly all Angular deployment confusion is those two commands being treated as versions of the same thing.

Angular Project Run Command: ng serve, ng build, and What Ships

They are not. One is a development tool that never produces a deployable artefact, and the other produces the artefact and cannot serve it.

Table of contents

The commands you will actually use

The daily set is short.

ng serve                 # dev server on http://localhost:4200 with live reload
ng serve --open          # the same, and opens a browser
ng serve --port 4300     # when 4200 is taken
ng build                 # production build into dist/
ng build --watch         # rebuild on change, without a server
ng test                  # unit tests
ng generate component x  # scaffold a component
ng new my-app            # create a project
ng version               # CLI, framework and Node versions

In recent versions ng dev is an alias for ng serve, and ng s and ng b are the short forms.

One small thing worth knowing: since Angular 12, ng build produces a production build by default. The --prod flag that appears in older tutorials no longer exists, and passing it gives you an unknown-argument error that sends people looking for a problem that is not there.

npm start versus ng serve

Both work and they are not identical.

ng serve calls the Angular CLI directly, which requires the CLI to be resolvable — installed globally, or reachable through npx ng serve.

npm start runs whatever the start script in package.json says, which in a default Angular project is ng serve. That indirection is worth keeping, because it is where team-specific flags live:

"scripts": {
  "start": "ng serve --port 4300 --proxy-config proxy.conf.json",
  "build": "ng build",
  "test": "ng test"
}

With that in place, a new colleague runs npm start and gets the right port and the right proxy without being told. Prefer npm start in documentation and ng serve when you are deliberately overriding something.

The proxy configuration is the underrated part. During development the app is on 4200 and the API is somewhere else, which is a cross-origin request. A proxy file makes the dev server forward matching paths to the API, so the browser sees one origin and you never write a CORS workaround that has to be undone before release.

What ng build actually produces

This is the part that matters for deployment, and it changed in a way that trips people up.

Since Angular 17, the default output lives at dist/<project-name>/browser/. Older projects put it directly in dist/<project-name>/. Deployment configurations copied from an older tutorial point at the wrong folder and publish an empty directory or an unstyled page.

Check what you have rather than assuming:

ng build
ls dist/*/

If there is a browser folder, that is your publish directory. If the project uses server-side rendering there will be a server folder beside it, and that one is a Node application rather than static files — a different deployment shape entirely.

The useful flags:

ng build --configuration production   # explicit; the default anyway
ng build --base-href /app/            # when serving from a subpath
ng build --source-map                 # keep source maps for debugging
ng build --stats-json                 # for bundle analysis

--base-href is the one people meet the hard way. Deploying to a subdirectory without it produces a page that loads, finds no assets, and shows nothing.

Deploying the output: the one rule everybody misses

The contents of the browser folder are static files. Any static host can serve them, and that is genuinely the whole deployment for a standard Angular application.

Except for one thing, and it catches almost everyone.

Angular uses client-side routing. When someone visits /dashboard directly, or refreshes the page while there, the browser asks the server for /dashboard. There is no file at that path — there is only index.html and some JavaScript that would have handled the route if it had loaded. The server returns 404.

The fix is a rewrite rule sending any unmatched path to index.html, usually called SPA fallback or history-mode fallback. Every static host has some way to express it:

  • A rewrite rule to /index.html with a 200 status, on hosts with a rewrites configuration.
  • try_files $uri $uri/ /index.html; in an nginx location block.

The reason this is so commonly missed is that it never appears in development. ng serve handles the fallback for you, so the application works perfectly until the first refresh on a deployed URL.

A build that behaves the same everywhere

Three habits remove most environment-specific build failures.

Use npm ci rather than npm install in build pipelines. It installs exactly what the lockfile says and fails if the lockfile and package.json disagree, instead of quietly resolving to something newer.

Pin the Node version. Angular’s CLI is specific about supported Node versions and a build machine on a different major version can fail with errors that look like your code is broken. Put the version in package.json under engines and configure the same in your build environment.

Keep environment configuration out of the bundle where it matters. Angular’s environment files are compiled into the build, which is fine for an API base URL and wrong for anything secret. Everything in a browser bundle is public, no matter which file it came from.

And run ng build locally before assuming a deployment problem is the host’s. A build that fails on the server usually fails on your machine too, once the cached node_modules is out of the way.

How this fits the rest of the stack

The deployment shape for a standard Angular application is genuinely simple — a build command, a publish directory, and a fallback rule — which is why the cost conversation should be short too. The RunxBuild hosting calculator lists a static site, an API service, a database and bandwidth as separate lines, which is the honest way to price an Angular front end that talks to a backend.

RunxBuild builds static sites from a repository with SPA fallback available as a setting rather than a configuration file you hand-write, along with custom domains, headers, redirects and rewrites, and 120GB of bandwidth included before $0.10/GB. If the project uses server-side rendering, that half deploys as a Node service beside it with build logs, a live route and rollback.

Useful related references:

FAQ

What is the command to run an Angular project?

Use ng serve to start the development server on port 4200 with live reload, or npm start if the project defines a start script with its own flags. Add —open to launch a browser and —port to change the port when 4200 is already in use.

What is the difference between ng serve and ng build?

ng serve compiles in memory and runs a development server, producing no files you can deploy. ng build writes optimised output to the dist folder and starts nothing. You develop with the first and deploy the output of the second.

Where does ng build put the output?

In dist followed by the project name. Since Angular 17 the static files are inside a browser subfolder, and a server subfolder appears alongside it if server-side rendering is enabled. Older projects put the files directly under the project name, which is why copied deployment configurations often point at the wrong path.

Why does my Angular app 404 on refresh after deploying?

Because client-side routes have no matching file on the server. Refreshing at a route asks the server for a path that does not exist. Configure an SPA fallback so unmatched paths return index.html with a 200 status. It never appears in development because ng serve does this automatically.

Is ng build —prod still valid?

No. Production is the default configuration since Angular 12 and the flag was removed, so passing it produces an unknown-argument error. Use ng build, or ng build —configuration production if you prefer to be explicit.

#angular project run command#ng serve#ng build#angular cli#deploy angular