Deployment
Auf dieser Seite
Ein sitelo-Build besteht aus schlichten statischen Dateien: sitelo build schreibt HTML, CSS und JS nach dist/. Jeder statische Hoster funktioniert — die Konfigurationen unten setzen lediglich npm run build → dist/ voraus.
Saubere URLs sind Verzeichnisse mit einer index.html (/about/index.html → /about), deshalb funktionieren hübsche URLs sofort und ohne Weiterleitungsregeln. Eine 404.html entsteht automatisch — genau die Konvention, die Netlify, Cloudflare Pages und GitHub Pages verstehen.
Kopierfertige Fassungen von all dem stecken im Basis-Beispiel (examples/basic im Repository).
Statische Hoster
Diese Hoster bauen die Website und liefern dist/ über ihr eigenes CDN aus.
Netlify · Vercel · Cloudflare · AWS Amplify · AWS S3 + CloudFront · Azure Static Web Apps · Firebase Hosting · GitHub Pages · GitLab Pages · Render · DigitalOcean App Platform
Netlify
[build]
command = "npm run build"
publish = "dist"Vercel
{
"$schema": "https://openapi.vercel.sh/vercel.json",
"framework": null,
"buildCommand": "npm run build",
"outputDirectory": "dist",
"installCommand": "npm install"
}Cloudflare
Cloudflare empfiehlt für neue Projekte inzwischen Workers statt Pages. Mit dieser Konfiguration baut npx wrangler deploy die Website und lädt dist/ hoch; verbindest du das Repository im Cloudflare-Dashboard, läuft derselbe Befehl bei jedem Push. html_handling: "drop-trailing-slash" liefert /about ohne Weiterleitung aus, und unbekannte URLs bekommen 404.html.
// Cloudflare Workers
{
"name": "my-site",
"compatibility_date": "2026-10-01",
"build": {
"command": "npm run build"
},
"assets": {
"directory": "./dist",
"not_found_handling": "404-page",
"html_handling": "drop-trailing-slash"
}
}Noch auf Pages? Setze den Build-Befehl auf npm run build und das Ausgabeverzeichnis auf dist oder lade einen lokalen Build mit npx wrangler pages deploy dist hoch.
AWS Amplify
# AWS Amplify Hosting
version: 1
frontend:
phases:
preBuild:
commands:
- npm install
build:
commands:
- npm run build
artifacts:
baseDirectory: dist
files:
- '**/*'
cache:
paths:
- node_modules/**/*AWS S3 + CloudFront
Halte den Bucket privat und setze CloudFront mit Origin Access Control davor. CloudFront ordnet nur / der index.html zu, also hänge diese Funktion an die Viewer-Requests der Distribution, damit sitelos saubere URLs funktionieren.
function handler(event) {
var request = event.request
var uri = request.uri
if (uri.endsWith('/')) {
request.uri += 'index.html'
} else if (uri.slice(uri.lastIndexOf('/') + 1).indexOf('.') === -1) {
request.uri += '/index.html'
}
return request
}Ein privater Bucket beantwortet fehlende Dateien mit 403, also lege eigene Fehlerantworten für 403 und 404 an, die /404.html mit Status 404 liefern. Danach lädst du jeden Build hoch und leerst den Cache:
npm run build
aws s3 sync dist/ s3://my-bucket --delete
aws cloudfront create-invalidation --distribution-id MY_DISTRIBUTION_ID --paths "/*"Azure Static Web Apps
Lege die Static Web App im Azure-Portal mit App-Speicherort / und Ausgabespeicherort dist an; Azure legt im Repository einen GitHub-Actions-Workflow an, der npm run build ausführt. Lege diese Datei in public/ ab, damit der Build sie nach dist/ kopiert — ohne sie beantwortet Azure unbekannte URLs mit einer eigenen Fehlerseite statt mit 404.html.
{
"responseOverrides": {
"404": {
"rewrite": "/404.html"
}
}
}Firebase Hosting
Bei Firebase Hosting wählst du mit firebase use --add ein Projekt und führst dann firebase deploy aus — der Predeploy-Hook baut vorher. Unbekannte URLs bekommen automatisch 404.html, und trailingSlash: false liefert /about ohne Weiterleitung aus.
{
"hosting": {
"public": "dist",
"predeploy": "npm run build",
"trailingSlash": false
}
}GitHub Pages
# GitHub Pages
name: Deploy
on:
push:
branches: [main]
permissions:
contents: read
pages: write
id-token: write
jobs:
deploy:
runs-on: ubuntu-latest
environment:
name: github-pages
steps:
- uses: actions/checkout@v7
- uses: actions/setup-node@v7
with:
node-version: 24
- run: npm ci && npm run build
- uses: actions/upload-pages-artifact@v5
with:
path: dist
- uses: actions/deploy-pages@v5Du lieferst unter einem Unterpfad aus (user.github.io/repo)? Dann baue mit --base /repo/.
GitLab Pages
GitLab liefert 404.html und Verzeichnis-URLs selbst aus, der Job baut und veröffentlicht also nur dist/.
# GitLab Pages
deploy-pages:
image: node:24
script:
- npm ci
- npm run build
pages:
publish: dist
rules:
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCHRender
Füge den Blueprint unten zum Repository hinzu oder lege im Dashboard eine Static Site mit demselben Build-Befehl und Publish-Verzeichnis an. Saubere URLs und 404.html funktionieren ohne Weiterleitungsregeln.
# Render
services:
- type: web
name: my-site
runtime: static
buildCommand: npm install && npm run build
staticPublishPath: ./distDigitalOcean App Platform
Trage dein Repository in github.repo ein und lege die App dann mit doctl apps create --spec .do/app.yaml an — oder füge die Spec im Dashboard ein. App Platform löst Verzeichnis-URLs auf und liefert standardmäßig 404.html aus.
# DigitalOcean App Platform
name: my-site
static_sites:
- name: site
github:
repo: your-name/your-site
branch: main
deploy_on_push: true
build_command: npm run build
output_dir: distContainer-Hoster
Diese Hoster führen einen Container aus, und ein einziges Dockerfile deckt alle ab: Node baut die Website, dann liefert Caddy dist/ mit sauberen URLs aus und beantwortet unbekannte mit 404.html. Caddy lauscht auf PORT, wenn der Hoster ihn setzt, sonst auf Port 80. Lege alle drei Dateien ins Projektstammverzeichnis.
Railway · Fly.io · Heroku · Google Cloud Run
FROM node:24-slim AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
FROM caddy:2-alpine
COPY Caddyfile /etc/caddy/Caddyfile
COPY --from=build /app/dist /app/distnode_modules
dist
.git:{$PORT:80} {
root * /app/dist
encode zstd gzip
try_files {path} {path}/index.html
file_server
handle_errors {
rewrite * /{err.status_code}.html
file_server
}
}Railway
Railway baut das Dockerfile, sobald es eines findet, du musst also nichts einstellen. Ohne Dockerfile setzt du die Service-Variable RAILPACK_SPA_OUTPUT_DIR=dist und behältst das Caddyfile: Railpack liefert dist/ dann darüber aus statt über seinen SPA-Standard, der unbekannte URLs mit der Startseite beantwortet.
Fly.io
Lege die App mit fly launch an und liefere danach mit fly deploy aus.
# Fly.io
app = "my-site"
[http_service]
internal_port = 80
force_https = true
auto_stop_machines = "stop"
auto_start_machines = true
min_machines_running = 0Heroku
Heroku baut das Dockerfile über heroku.yml. Stelle die App einmal mit heroku stack:set container auf den Container-Stack um und führe danach git push heroku main aus.
# Heroku
build:
docker:
web: DockerfileGoogle Cloud Run
Führe gcloud run deploy --source . im Projektstammverzeichnis aus: Cloud Build baut das Dockerfile, und Cloud Run liefert es aus.
Dein eigener Server
Kopiere den Build mit rsync -a --delete dist/ you@server:/var/www/my-site/ auf den Server und liefere ihn mit Caddy oder nginx aus — saubere URLs und 404.html funktionieren wie bei den Hostern oben. Caddy holt sich das HTTPS-Zertifikat für die Domain selbst; bei nginx ergänzt du eines mit certbot.
example.com {
root * /var/www/my-site
encode zstd gzip
try_files {path} {path}/index.html
file_server
handle_errors {
rewrite * /{err.status_code}.html
file_server
}
}server {
listen 80;
server_name example.com;
root /var/www/my-site;
location / {
try_files $uri $uri/index.html =404;
}
error_page 404 /404.html;
}Vor dem Ausliefern
- Setze
siteinsitelo.config.js, damit diesitemap.xmlerzeugt wird — siehe Konfiguration - Lege eine
src/404.ht.jsan, um eine Fehlerseite im eigenen Look zu bekommen (sonst wird eine schlichte Standardseite erzeugt) sitelo previewserviert den Produktions-Build lokal für eine letzte Kontrolle