Astro.js & Docker: Dein Weg zur Live-Webseite (Statisch & Dynamisch auf dem VPS)

In diesem Artikel erfährst du, wie du eine Astro.js-Webseite mit Docker auf einem VPS bereitstellst. Ich zeige dir, wie du sowohl statische als auch dynamische Webseiten hosten kannst.

Astro.js & Docker: Dein Weg zur Live-Webseite (Statisch & Dynamisch auf dem VPS) hero image

Du möchtest eine moderne, schnelle Webseite mit Astro.js bauen und sie zuverlässig auf deinem eigenen Server bereitstellen? Docker ist der Schlüssel, um diesen Prozess sauber, wiederholbar und effizient zu gestalten. In diesem Beitrag führe ich dich durch den gesamten Ablauf – von der Einrichtung deiner Entwicklungsumgebung bis zum Live-Deployment deiner Astro-Anwendung (sowohl statisch als auch dynamisch) mit Docker und Docker Compose auf einem Virtual Private Server (VPS).

Kernpunkte auf einen Blick (Key Takeaways)

  • Werkzeuge: Visual Studio Code, Node.js (am besten über einen Versionsmanager wie NVM) und Git sind die Grundlagen.
  • Astro-Projekt: Schnellstart mit npm create astro@latest, Nutzung von Templates (z.B. minimal).
  • Astro-Konzepte: Seiten (.astro), Layouts (z.B. BaseLayout.astro mit <slot />) und Komponenten (z.B. Header.astro, Footer.astro) ermöglichen modularen Aufbau.
  • Docker ist Pflicht: Containerisierung vereinfacht das Deployment und sorgt für konsistente Umgebungen.
  • Multi-Stage Builds: Dockerfiles sollten mehrere Stufen nutzen (Build-Stage mit Node.js, Runtime-Stage mit Nginx oder Node.js), um schlanke End-Images zu erzeugen.
  • Statisch vs. Dynamisch: Statische Seiten können per Nginx ausgeliefert werden. Dynamische Routen in Astro erfordern den Node.js-Adapter und eine Node.js-Runtime im Docker-Container.
  • Docker Compose: docker-compose.yml erleichtert das Starten und Verwalten deiner Docker-Container auf dem Server.
  • Deployment-Workflow: Code mit Git verwalten (z.B. auf GitHub), auf dem Server klonen und mit docker-compose up -d starten.
  • Server-Setup: Auf dem VPS müssen Docker, Docker Compose und Git installiert sein.

Die Werkzeuge: Deine Entwicklungsumgebung vorbereiten

Bevor wir in Astro und Docker eintauchen, brauchen wir eine solide Basis. Diese drei Werkzeuge sind unerlässlich:

  1. Text-Editor: Visual Studio Code (VS Code) ist eine ausgezeichnete Wahl. Es ist kostenlos, extrem mächtig und hat eine riesige Community mit unzähligen Erweiterungen, die die Webentwicklung erleichtern.

  2. Node.js: Astro basiert auf Node.js. Anstatt Node.js direkt zu installieren, empfehle ich dringend einen Versionsmanager.

    • Für Windows: Nutze nvm-windows. Das vermeidet viele Rechteprobleme und erlaubt dir, einfach zwischen verschiedenen Node.js-Versionen zu wechseln, falls Projekte unterschiedliche Versionen benötigen.

    • Für Linux/macOS: Nutze das originale nvm (Node Version Manager). Die Installation erfolgt meist über ein curl- oder wget-Skript.

        nvm install lts # Installiert die neueste Long-Term-Support Version
        nvm use lts     # Aktiviert die installierte LTS-Version
  3. Git: Das Standardwerkzeug zur Versionskontrolle. Unverzichtbar, um Änderungen nachzuverfolgen und den Code auf den Server zu bringen. Git herunterladen.

    # Während der Installation unter Windows: Wähle VS Code als Standard-Editor für Git.
    # Stelle sicher, dass der Default Branch Name auf "main" gesetzt ist.

Nachdem diese Werkzeuge installiert sind, öffne dein Terminal (in VS Code: Strg+Shift+Ö oder Terminal > New Terminal) und überprüfe die Installationen:

node -v
npm -v
git --version
nvm version

Dein erstes Astro-Projekt erstellen

Wechsle im Terminal in den Ordner, in dem deine Projekte liegen sollen, und erstelle ein neues Astro-Projekt:

# Wähle ein einfaches Template für den Start
npm create astro@latest -- --template minimal mein-astro-projekt

# Wechsle in den Projektordner
cd mein-astro-projekt

# Installiere die Abhängigkeiten (wird oft schon während der Erstellung gemacht)
npm install

# Starte den Entwicklungs-Server
npm run dev

Dein Browser sollte sich öffnen (oder du öffnest http://localhost:4321 manuell) und die Standard-Astro-Seite anzeigen. Wenn du jetzt Code in deinem Projekt (z.B. in src/pages/index.astro) änderst und speicherst, aktualisiert sich die Seite im Browser automatisch (Hot Module Replacement).

Astro verstehen: Bausteine deiner Seite

Astro-Projekte sind klar strukturiert. Die wichtigsten Ordner und Konzepte:

  • src/pages/: Hier liegen deine Seiten. Jede .astro, .md oder .html Datei hier wird zu einer Route auf deiner Webseite. index.astro wird zu /, about.astro wird zu /about.
  • src/layouts/: Wiederverwendbare Seitenstrukturen. Ein typisches Layout (BaseLayout.astro) enthält das grundlegende HTML-Gerüst ( <html>, <head>, <body>), vielleicht einen Header und Footer, und einen <slot />.
  • <slot />: Das ist der Platzhalter im Layout, in den der spezifische Inhalt einer Seite eingefügt wird. Jede Seite, die das Layout nutzt, “füllt” diesen Slot.
  • src/components/: Wiederverwendbare UI-Elemente wie Buttons, Navigationsleisten, Karten etc. Diese können in Seiten und Layouts importiert und verwendet werden. (Header.astro, Footer.astro sind gute Beispiele).
  • Styling:
    • Global: CSS-Dateien (z.B. src/styles/global.css) können in Layouts importiert werden, um Stile für die gesamte Seite zu definieren (z.B. Body-Styling, CSS-Variablen, Resets).
    • Scoped: Innerhalb einer .astro-Datei kannst du <style>-Tags verwenden. Die Stile hier gelten standardmäßig nur für das HTML in derselben Datei. Das verhindert Konflikte zwischen Komponenten.
---
// Beispiel: src/layouts/BaseLayout.astro
import Header from "../components/Header.astro";
import Footer from "../components/Footer.astro";
import "../styles/global.css"; // Globales CSS importieren

interface Props {
	title: string;
	description?: string; // Optional
}

const { title, description = "Meine tolle Astro Seite" } = Astro.props;
---

<!doctype html>
<html lang="de">
	<head>
		<meta charset="UTF-8" />
		<meta name="viewport" content="width=device-width, initial-scale=1.0" />
		<meta name="description" content={description} />
		<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
		<title>{title} | Astro Demo</title>
	</head>
	<body>
		<Header />
		<main class="container">
			<slot />
		</main>
		<Footer />
	</body>
</html>

<style>
	.container {
		max-width: 1100px;
		margin-left: auto;
		margin-right: auto;
		padding-left: 1rem;
		padding-right: 1rem;
		flex-grow: 1;
	}
	body {
		display: flex;
		flex-direction: column;
		min-height: 100vh;
	}
</style>

Statisch oder Dynamisch? Der Build-Prozess

Astro kann beides: hochoptimierte statische Seiten generieren oder dynamische Seiten auf einem Server rendern.

Der statische Build

Das ist der Standard bei Astro. Wenn du npm run build ausführst, analysiert Astro dein Projekt und generiert reines HTML, CSS und JavaScript. Das Ergebnis landet im dist/ Ordner.

npm run build

Dieser dist/ Ordner enthält alles, was du brauchst, um deine Webseite auf jedem statischen Webhost (wie einem Nginx-Server in Docker) bereitzustellen. Es ist kein Node.js-Server mehr zur Laufzeit nötig.

Dynamische Routen & der Node-Adapter

Was, wenn eine Seite dynamische Daten braucht, die sich bei jeder Anfrage ändern können (z.B. Nutzerinformationen aus einem Header)? Hier kommt Server-Side Rendering (SSR) ins Spiel.

  1. Adapter installieren: Um SSR mit Node.js zu ermöglichen, brauchst du den Astro Node.js Adapter.

    npx astro add node

    Das passt deine astro.config.mjs an und installiert die nötigen Pakete.

  2. Prerendering deaktivieren: Für die dynamische Seite (z.B. src/pages/request-info.astro) musst du das Prerendering explizit ausschalten:

    ---
    // src/pages/request-info.astro
    export const prerender = false;
    
    const userAgent = Astro.request.headers.get("user-agent") || "Unbekannt";
    
    import BaseLayout from "../layouts/BaseLayout.astro";
    ---
    
    <BaseLayout title="Request Info">
    	<h1>Deine Request Info</h1>
    	<p>Dein User-Agent: {userAgent}</p>
    </BaseLayout>
  3. Build-Ergebnis: Wenn du jetzt npm run build ausführst, enthält der dist/ Ordner nicht nur statische Assets (client/), sondern auch Server-Code (server/entry.mjs). Dieser Server-Code muss zur Laufzeit mit Node.js ausgeführt werden.

Docker Marsch: Deine App im Container

Docker verpackt deine Anwendung und alle ihre Abhängigkeiten in einen isolierten Container. Das macht das Deployment extrem zuverlässig – “es läuft auf meinem Rechner” wird zu “es läuft überall, wo Docker läuft”.

Warum Docker?

  • Konsistenz: Deine App läuft in derselben Umgebung, egal ob lokal oder auf dem Server.
  • Isolation: Keine Konflikte mit anderen Anwendungen auf dem Server.
  • Wiederholbarkeit: Einfaches Erstellen und Verteilen identischer Umgebungen.
  • Skalierbarkeit: Leichteres Hoch- und Runterskalieren deiner Anwendung.

Die .dockerignore: Was nicht reingehört

Ähnlich wie .gitignore teilt diese Datei Docker mit, welche Dateien und Ordner nicht in das Image kopiert werden sollen. Das hält die Images klein und beschleunigt den Build.

# .dockerignore
node_modules
npm-debug.log
dist
.astro
.env*
*.env
.git
.vscode
Dockerfile*
docker-compose*
README.md

Szenario 1: Statische Astro-Seite mit Nginx

Für eine rein statische Seite nutzen wir einen mehrstufigen Docker Build:

  1. Build-Stage: Nutzt ein Node.js-Image, um npm install und npm run build auszuführen.
  2. Runtime-Stage: Nutzt ein schlankes Nginx-Image und kopiert nur den fertigen dist/ Ordner aus der Build-Stage sowie eine Nginx-Konfigurationsdatei hinein.

Dockerfile (für statische Seite):

# Dockerfile (für statische Seite)

# ---- Build Stage ----
FROM node:22-alpine AS builder
WORKDIR /app

# Kopiere nur package.json und lock-Datei zuerst für Cache-Optimierung
COPY package*.json ./
RUN npm install

# Kopiere den Rest des Codes (beachtet .dockerignore)
COPY . .
RUN npm run build

# ---- Runtime Stage ----
FROM nginx:1.27-alpine AS runtime
WORKDIR /usr/share/nginx/html

# Entferne Standard Nginx Inhalt
RUN rm -rf ./*

# Kopiere Build-Artefakte aus der Builder-Stage
COPY --from=builder /app/dist .

# Kopiere unsere Nginx Konfiguration
COPY nginx.conf /etc/nginx/conf.d/default.conf

# Exponiere Port 80
EXPOSE 80

# Startbefehl
CMD ["nginx", "-g", "daemon off;"]

nginx.conf (Beispiel):

Diese Datei konfiguriert Nginx, um die statischen Dateien auszuliefern, Gzip-Komprimierung zu aktivieren und Caching-Header zu setzen. Sie sorgt auch dafür, dass bei einem 404-Fehler (Datei nicht gefunden) die index.html ausgeliefert wird, was für Single-Page-Application (SPA)-ähnliches Routing in Astro nützlich sein kann.

# /nginx.conf
server {
    listen 80;
    server_name _; # Akzeptiert jeden Hostnamen
    root /usr/share/nginx/html;

    # Fehlerseite für SPA-Routing (optional, aber oft nützlich)
    error_page 404 /index.html;

    # Gzip Komprimierung aktivieren
    gzip on;
    gzip_vary on;
    gzip_proxied any;
    gzip_comp_level 6;
    gzip_types text/plain text/css text/xml application/json application/javascript application/xml+rss application/atom+xml image/svg+xml;

    location / {
        # Versuche die angefragte Datei, dann den Ordner, dann Fallback (z.B. für SPA)
        try_files $uri $uri/ /index.html;

        # Caching Header für Assets (z.B. 1 Jahr)
        location ~* \.(?:css|js|svg|gif|png|jpg|jpeg|webp|woff|woff2|ttf|eot)$ {
            expires 1y;
            add_header Cache-Control "public";
            access_log off; # Optional: Kein Logging für statische Assets
        }
    }

    # Verhindere Zugriff auf versteckte Dateien
    location ~ /\. {
        deny all;
    }
}

Szenario 2: Dynamische Astro-Seite mit Node.js

Wenn du den Node-Adapter nutzt, brauchst du zur Laufzeit Node.js. Die Dockerfile ist einfacher, da wir keine separate Nginx-Stufe benötigen.

Dockerfile (für dynamische Seite):

# Dockerfile (für dynamische Seite mit Node-Adapter)

# ---- Build Stage ----
FROM node:22-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build

# ---- Runtime Stage ----
FROM node:22-alpine AS runtime
WORKDIR /app

# Setze Umgebungsvariable für Produktion
ENV NODE_ENV=production

# Kopiere nur die notwendigen Build-Artefakte und node_modules
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/package.json ./package.json

EXPOSE 4321

# Setze den Host, damit der Server von außen erreichbar ist
ENV HOST="0.0.0.0"
ENV PORT="4321"

# Startbefehl für den Node.js Server
CMD ["node", "dist/server/entry.mjs"]

Orchestrierung mit Docker Compose

docker-compose ist ein Werkzeug, um Multi-Container-Anwendungen zu definieren und auszuführen. Auch wenn wir hier nur einen Container haben, vereinfacht es das Starten und Konfigurieren enorm.

docker-compose.yml:

services:
    astro-app:
        build:
            context: .
            dockerfile: Dockerfile
        image: mein-astro-projekt:latest
        container_name: mein-astro-container
        ports:
            # Beispiel für STATISCHE Seite (Nginx auf Port 80 im Container)
            - "80:80"
              # Beispiel für DYNAMISCHE Seite (Node-Adapter auf Port 4321 im Container)
            # - "4321:4321" # Host-Port kann auch anders sein, z.B. "80:4321"
        restart: unless-stopped

Wähle das passende ports-Mapping je nachdem, ob du die statische (Nginx, Port 80) oder dynamische (Node.js, z.B. Port 4321) Variante deployen willst. Der Host-Port (die linke Zahl) ist der Port, den du im Browser aufrufst, um auf die App zuzugreifen.

Ab auf den Server: Das Deployment

Jetzt bringen wir alles zusammen und deployen die Anwendung auf einem VPS.

1. Server-Vorbereitung (VPS, z.B. Hetzner)

  • Erstelle einen VPS bei einem Anbieter deiner Wahl (z.B. Hetzner Cloud, DigitalOcean, Linode). Wähle ein Betriebssystem wie Ubuntu 24.04.
  • Verbinde dich per SSH mit deinem Server (ssh root@DEINE_SERVER_IP).
  • Installiere Docker: Folge der offiziellen Docker-Dokumentation für dein Betriebssystem (meistens ein paar apt update, apt install Befehle und Hinzufügen des Docker-Repositories).
# Add Docker's official GPG key:

sudo apt-get update
sudo apt-get install ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc

# Add the repository to Apt sources:

echo \
 "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \
 $(. /etc/os-release && echo "${UBUNTU_CODENAME:-$VERSION_CODENAME}") stable" | \
 sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update
sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
  • Installiere Docker Compose: Auch hier der offiziellen Dokumentation folgen (oft ein curl-Befehl, um die Binärdatei herunterzuladen und ausführbar zu machen).

    # Beispiel (Version prüfen und anpassen!)
    sudo curl -L "https://github.com/docker/compose/releases/download/v2.24.6/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
    sudo chmod +x /usr/local/bin/docker-compose
    docker-compose --version # Prüfen
  • Installiere Git:

    sudo apt update
    sudo apt install git -y
    git --version # Prüfen

2. Code holen (Git Clone)

Klone dein Projekt-Repository (das du vorher z.B. auf GitHub gepusht hast) auf den Server:

git clone DEINE_REPOSITORY_URL
cd dein-projekt-ordner

3. Startschuss (docker-compose up -d)

Jetzt kommt der magische Moment. Im Projektordner auf dem Server, wo deine docker-compose.yml und Dockerfile liegen, führst du aus:

sudo docker-compose up --build -d
  • sudo: Docker-Befehle erfordern oft Root-Rechte.
  • docker-compose up: Startet die in der docker-compose.yml definierten Services.
  • --build: Erzwingt einen Neu-Bau des Images, falls sich die Dockerfile oder der Code geändert haben. Beim ersten Mal ist das nötig.
  • -d: Startet die Container im “detached” Modus (im Hintergrund).

Docker baut jetzt dein Image (das kann beim ersten Mal etwas dauern) und startet den Container.

Öffne nun http://DEINE_SERVER_IP:<Host-Port> (wobei <Host-Port> der Port ist, den du links im ports-Mapping der docker-compose.yml angegeben hast, z.B. 80 oder 4321) in deinem Browser. Deine Astro-Webseite sollte live sein!

Wichtige Details & Tipps

  • Port-Mapping: Das Mapping Host-Port:Container-Port ist entscheidend. Wenn du möchtest, dass deine Seite direkt über die IP (oder später Domain) ohne Portangabe erreichbar ist, mappe auf Host-Port 80 ("80:80" für Nginx oder "80:4321" für den Node-Adapter). Stelle sicher, dass kein anderer Dienst auf dem Server Port 80 blockiert.
  • Multi-Stage Build Vorteile: Der Hauptvorteil ist ein drastisch kleineres finales Docker-Image. Die Runtime-Stage enthält keine Build-Werkzeuge (Node.js, npm, Quellcode etc.) mehr, nur das Nötigste zum Ausführen. Das spart Speicherplatz und verbessert die Sicherheit.
  • Updates: Um deine Seite zu aktualisieren:
    1. Änderungen lokal machen, testen, committen und pushen (Git).
    2. Auf dem Server per SSH einloggen, in den Projektordner wechseln.
    3. git pull ausführen, um die neuesten Änderungen zu holen.
    4. sudo docker-compose up --build -d erneut ausführen, um das Image neu zu bauen und den Container neu zu starten.

Fazit

Die Kombination aus Astro.js und Docker bietet einen unglaublich leistungsfähigen und modernen Weg, Webanwendungen zu bauen und bereitzustellen. Astro liefert die Performance und Entwicklerfreundlichkeit, während Docker für robuste, konsistente und portable Deployments sorgt. Mit dem Wissen aus diesem Guide bist du bestens gerüstet, deine eigenen Astro-Projekte – ob statisch oder dynamisch – erfolgreich auf deinem VPS zu hosten. Viel Erfolg!

Diesen Beitrag teilen:

Diese Website verwendet Cookies. Diese sind notwendig, um die Funktionalität der Website zu gewährleisten. Weitere Informationen finden Sie in der Datenschutzerklärung