Skip to content
AstroCraft Docs
On this theme

Deployment

Urengi is a fully static site with no adapter. pnpm build produces a directory of HTML, CSS, JS and images, and that is the whole deliverable. There is no server-rendered route, no serverless function, no runtime environment to configure. Anything that can serve files can host it.

That is a deliberate stance rather than an unfinished one, and it is the same stance astro.config.mjs takes on site: the template picks no host for its buyer. The six forms ship with real, live server code behind an action Astro never mounts, precisely so the build stays static until you decide where it runs. Connecting one is the only thing that changes this — see Forms & Email.

Build

pnpm build

Output lands in dist/. Twenty-five route files produce 72 pages, plus robots.txt, llms.txt, rss.xml and sitemap-index.xml, all four of which derive their absolute URLs from site — so setting that once fixes them together.

pnpm preview

serves the built output locally. Preview it before you ship: a few things only exist in a production build, and one only stops existing there — the dev catalog at /examples/ui emits no paths in production, so it is the one route you cannot check this way.

The SITE_URL guard

site in astro.config.mjs feeds canonical links, Open Graph URLs, JSON-LD, the sitemap, robots.txt and llms.txt. One wrong value poisons six things at once, and none of them is visibly broken in review — which is why the config refuses to let it happen:

const site = process.env.SITE_URL ?? "https://example.com";
const isProductionDeploy =
  process.env.CONTEXT === "production" ||        // Netlify
  process.env.VERCEL_ENV === "production" ||     // Vercel
  process.env.DEPLOY_ENV === "production";       // anything else

if (isProductionDeploy && site.includes("example.com")) {
  throw new Error("SITE_URL is unset or still the placeholder. …");
}

A fresh clone builds on the placeholder so you can see the site before you own a domain; a production deploy refuses to. Local builds and deploy previews are unaffected, because none of those three variables is set by either.

On a host that is neither Netlify nor Vercel, set DEPLOY_ENV=production yourself — in the production build environment and nowhere else, or preview deploys will start failing on the placeholder too. The gate is only as good as the signal it can see, and an unset signal means the guard silently does nothing.

Hosting

Point your host at the standard Astro build:

Setting Value
Build command pnpm build
Output directory dist
Node version 22.13 or newer
Install command pnpm install

Then set SITE_URL in the host’s environment variables. Netlify, Vercel, Cloudflare Pages, GitHub Pages, S3 + CloudFront, or a plain nginx root all work identically, because the output is plain files.

Two behaviours worth configuring on the host side. trailingSlash: "always" means every internal URL ends in a slash and canonical, Open Graph and the sitemap all agree on that shape — make sure your host does not strip or add one. And redirects in astro.config.mjs sends the seventeen designed-but-unbuilt routes to /; Astro emits these as meta-refresh HTML pages in a static build, so if your host supports native redirects, mirroring the list there is faster.

What changes if you connect a form

Nothing, until you do. When you do, three things change together:

  1. You add an adapter, so the build is no longer static.
  2. The pages that receive a POST set export const prerender = false in their own route files. That is six routes — /blog/, /contact/, /customers/, /integrations/, /pricing/ and /signup/ — because the newsletter and reference-call bands are drawn in the footer across many pages but post to the route that owns their action.
  3. Each of those six must be added to the sitemap’s customPages list in astro.config.mjs, because @astrojs/sitemap enumerates the built tree and cannot see an on-demand route. Every one of them is indexable, so dropping them would be a real loss rather than a tidy-up.

The two lists — the prerender = false routes and customPages — are the same six pages seen from opposite ends. Keep them in step.

Before you launch

  1. SITE_URL — your production domain, in the host’s environment variables. The guard above makes this unskippable for a production deploy.
  2. src/config/siteData.json.ts — name, the home page’s title, the site description, and the author block. author.twitter and sameAs ship empty on purpose: the tags that would carry them are guarded, so nothing renders until you own the accounts. Fill sameAs and navData.json.ts’s social hrefs together — they must name the same URLs.
  3. src/config/legalData.json.ts — the terms and privacy copy are placeholders. Have them reviewed; they are not legal advice.
  4. Favicons and the social imagepublic/favicon.svg, public/favicon.ico, and public/og.jpg (a real 1200×630). og.jpg doubles as the JSON-LD Organization logo until you pass a real one, so replacing it fixes two things.
  5. Read THIRD-PARTY.md end to end. The icons, the 35 brand logos and the demo photography are not covered by the template’s licence, and three of its sections are marked confirm before production. This is the step people skip and should not.
  6. Replace the demo contentsrc/data/ ships eight posts, four authors, seven customer stories and 24 integrations written for the fictional Urengi. The integrations carry real companies’ trademarked logos as demo content: drop any you do not actually integrate with. Delete a folder and the routes, listings, RSS and llms.txt all follow; the build fails loudly if you leave a dangling reference("authors").
  7. Resolve the planned routessrc/config/plannedRoutes.json.ts lists seventeen designed URLs that currently redirect to /. Build them, or trim the links out of navData, aboutData, homeData and productData. pnpm test prints the unbuilt set on every run.
  8. Delete the dev catalogsrc/components/Sections/UiCatalog/ and src/pages/examples/, once you no longer need the showroom. It builds no pages in production, but Tailwind still scans its markup, so its demo classes sit in the stylesheet every page loads: the shared sheet is 118,821 bytes carrying 96 @keyframes, and the site’s own pages use about nineteen of the catalog’s ninety-one animations. Keep it while you are still picking primitives — the cost is CSS, not JS, and it is the fastest way to see all 44.

Verifying a change

pnpm lint && pnpm check && pnpm build && pnpm test && pnpm wiki:lint

The build is the real check: content-schema and config mistakes surface there. Run the whole chain before a deploy — three of its five commands fail closed, and each catches a different class of mistake. Commands & Testing breaks down what each one actually finds.

NEXT STEPContent Collections