---
title: 'Run it, then build it'
description: 'What the dev server does for you, what changes in a production build, and what to look for in dist/.'
slug: 'run-and-build'
type: 'article'
date: 2026-08-15T00:00:00.000Z
lang: 'en'
url: 'https://www.eleventy-baseline.dev/docs/tutorial/run-and-build/'
---

You have run `npm start` twice already, so the dev server is not news. A build is a different site, though, and the gap between the two is where the surprises live.

---

## The dev server

```bash
npm start
```

It serves on http://localhost:8080/ and rebuilds whenever you save. It also writes to `dist/` as it goes, so you can open the generated files while the server is running and see what your Markdown became.

{% alertBlock "info" %}

Eleventy never empties `dist/` for you. That is why both scripts run `rimraf` first: a page you deleted last week would otherwise still be sitting there, still being served, looking entirely convincing.

{% endalertBlock %}

---

## Build it for production

A build wants a real origin, so the absolute URLs come out pointing somewhere that exists. Set it for the one command:

```bash
BASELINE_URL=https://www.example.com/ npm run build
```

Three things change compared to `npm start`. Your CSS is minified, because the `build` script sets `ELEVENTY_ENV=production` and Baseline's PostCSS config turns on `cssnano` when it sees that. The navigator page, a development-only introspection page you have not met yet, is left out for the same reason. And any page marked `draft: true` in its front matter is dropped, this time because Eleventy itself knows it is building rather than serving. Two switches, not one.

Then the `BASELINE_URL` in front of the command. Everything absolute that Baseline emits, the canonical link on every page and every entry in the sitemap, is anchored to that value. A build without a real origin produces a site that quietly tells search engines it lives on your laptop. The `start` script hardcodes localhost because that is where dev runs; a build has no such fixed answer, so it takes one from you. On a real deployment your host sets `BASELINE_URL` and you never think about it again.

{% alertBlock "info" %}

The `NAME=value` prefix works in bash and zsh. Windows `cmd` and PowerShell do not take it, which is what `cross-env` is for. It is already installed, and it belongs inside the `build` script in `package.json` rather than typed at the prompt.

{% endalertBlock %}

---

## Read what came out

Open `dist/`. Five things are worth a look before you close the folder again.

`dist/index.html`, and specifically its `<head>`. You never wrote one. Everything in there came from your front matter and your settings file, sorted and deduplicated on the way out.

`dist/sitemap.xml`, which nobody asked for. The sitemap module is on by default and lists all three of your URLs, the post included, and they start with the origin you passed rather than `localhost`. If they say localhost, `BASELINE_URL` did not reach the build.

`dist/posts/hello-world/index.html`, next to `dist/about/index.html`. Two folders, two kinds of thing, and the addresses came from two different rules in two different directory data files. Neither page knows which rule made it.

`dist/posts/not-finished/`, which is not there. The draft has been showing up in the browser since you wrote it two pages ago, and it never reached the build. The sitemap above lists three URLs rather than four for the same reason.

`dist/assets/css/index.css`, now on a single line. That is the minification, and it is the visible proof that `ELEVENTY_ENV` did its job.

Everything past this point is deployment, which has enough of its own things to check that it gets a guide of its own. The next page points you at it.
