Guides

Deployment

Meno builds your entire site to static HTML files in the dist/ directory. The output can be deployed to any static hosting provider -- Netlify, Cloudflare Pages, GitHub Pages, Vercel, or a simple file server.


Building the Site

  1. Run the build command from your project root:

bun run build
  1. The build process: - Renders every page in pages/ to static HTML. - Generates one HTML file per CMS item for every template page in pages/templates/. - Generates locale-prefixed copies of all pages if i18n is configured. - Creates auto-generated files from project.config.json. - Copies optional static files from the project root. - Bundles CSS and client-side JavaScript (including MenoFilter if used).

  1. Output appears in the dist/ directory, ready to upload.


Auto-Generated Files

These files are created automatically from project.config.json settings:

File

Source

Description

robots.txt

siteUrl

Search engine directives with sitemap reference

sitemap.xml

siteUrl + all pages/CMS items

Full sitemap including CMS item URLs and locale alternates

Favicon tags

icons.favicon

<link rel="icon"> tag in every HTML file

Apple Touch Icon

icons.appleTouchIcon

<link rel="apple-touch-icon"> tag in every HTML file

Configure these in project.config.json:

{
  "siteUrl": "https://example.com",
  "icons": {
    "favicon": "/favicon.ico",
    "appleTouchIcon": "/apple-touch-icon.png"
  }
}

Optional Static Files

If any of these files exist in your project root, they are copied to dist/ during the build:

File / Directory

Purpose

_headers

Netlify or Cloudflare Pages custom headers

_redirects

Netlify or Cloudflare Pages redirect rules

CNAME

GitHub Pages custom domain

manifest.json

PWA manifest

site.webmanifest

PWA manifest (alternative name)

llms.txt

LLM context file

humans.txt

Site credits

ads.txt

Ad network authorization

security.txt

Security contact information

.well-known/

Well-known URI directory (entire folder copied)


URL Redirects

Create a _redirects file in your project root. Each line defines one rule:

/old-page    /new-page    301
/blog/old    /blog/new    302
/app/*       /index.html  200

Format: source destination status-code

Status

Meaning

301

Permanent redirect (search engines update their index)

302

Temporary redirect

200

Rewrite (serve the destination without changing the URL)

This file is copied as-is to the build output. The redirect behavior depends on your hosting provider (Netlify and Cloudflare Pages support this format natively).


External Libraries and CSP

If your site uses external JavaScript or CSS from CDNs, configure them in project.config.json:

{
  "libraries": {
    "js": [
      { "url": "https://cdn.example.com/analytics.js" }
    ],
    "css": [
      { "url": "https://fonts.googleapis.com/css2?family=Roboto" }
    ]
  }
}

To allow these domains through Content Security Policy headers, add them to the csp configuration:

{
  "csp": {
    "scriptSrc": ["https://cdn.example.com"],
    "styleSrc": ["https://fonts.googleapis.com"],
    "fontSrc": ["https://fonts.gstatic.com"],
    "connectSrc": ["https://api.example.com"],
    "frameSrc": ["https://www.youtube.com"],
    "imgSrc": ["https://images.example.com"]
  }
}

CSP Directive

Controls

scriptSrc

JavaScript sources

styleSrc

CSS sources

fontSrc

Web font sources

connectSrc

Fetch/XHR/WebSocket destinations

frameSrc

Iframe embed sources

imgSrc

Image sources


CMS Static Generation

CMS template pages are pre-rendered for every item at build time. If you have a template at pages/templates/blog-post.json with URL pattern /blog/{{slug}} and 50 blog items, the build produces 50 HTML files:

dist/
  blog/
    my-first-post.html
    another-post.html
    ...

If i18n is configured, each locale gets its own copy:

dist/
  blog/
    my-first-post.html
  pl/
    blog/
      my-first-post.html

Important: CMS items are rendered at build time. After adding or editing items, you must rebuild and redeploy for changes to appear on the live site.


Deploying to Specific Providers

Netlify

  1. Build the site: ``bash bun run build ``

  2. Drag and drop the dist/ folder into the Netlify dashboard, or configure a Git-based deployment with these settings: - Build command: bun run build - Publish directory: dist

  3. For custom headers or redirects, create _headers and _redirects files in your project root. They are copied to dist/ automatically.

Cloudflare Pages

  1. Connect your repository to Cloudflare Pages.

  2. Set the build command to bun run build and the output directory to dist.

  3. Cloudflare Pages supports _headers and _redirects files natively.

GitHub Pages

  1. Build the site and push the dist/ folder to the gh-pages branch, or configure GitHub Actions to build and deploy.

  2. For a custom domain, create a CNAME file in your project root containing your domain name (e.g. www.example.com). It is copied to dist/ automatically.

Important: GitHub Pages does not support _headers or _redirects. Use meta-refresh redirects or configure redirects at the DNS level instead.

Any Static Host

The dist/ directory contains plain HTML, CSS, and JavaScript files. Upload them to any web server or static file host:

# Example: rsync to a server
rsync -avz dist/ user@server:/var/www/html/

# Example: AWS S3
aws s3 sync dist/ s3://my-bucket --delete

Build Output Structure

A typical build produces:

dist/
  index.html                   # Home page
  about.html                   # Static page
  blog/
    my-post.html               # CMS-generated page
    another-post.html
  pl/                          # Locale prefix (if i18n configured)
    index.html
    about.html
    blog/
      my-post.html
  assets/
    style.css                  # Bundled CSS
    meno-filter.js             # Client-side filtering (if used)
  data/
    posts/
      index.json               # Client-side CMS data (if clientData enabled)
  robots.txt                   # Auto-generated
  sitemap.xml                  # Auto-generated
  _headers                     # Copied from project root
  _redirects                   # Copied from project root

Fonts

Custom fonts declared in project.config.json are included in the build with proper @font-face declarations:

{
  "fonts": [
    {
      "path": "/fonts/Inter-VariableFont_opsz,wght.ttf",
      "family": "Inter",
      "weight": 400,
      "fontDisplay": "swap"
    }
  ]
}

The font files must exist at the specified paths in your project. They are copied to the build output and referenced with font-display: swap for optimal loading performance.


Verifying the Build

Before deploying, preview the build output locally:

# Serve the dist directory with any static server
bunx serve dist
# or
npx serve dist

Open http://localhost:3000 (or the port shown) and verify:

  • All pages render correctly.

  • CMS item pages are accessible at their expected URLs.

  • Locale-prefixed pages show the correct translations.

  • Client-side filtering and search work (if used).

  • The sitemap at /sitemap.xml lists all pages.

  • Font loading does not cause layout shifts.


Next Steps

Building the future of digital experiences, one website at a time

© 2026 Company. All rights reserved.