Dein Portfolio mit Manablox · Teil 7 von 7 der Serie
Teil 7: Bring dein Portfolio online
Miete einen Server, richte deine Domain darauf und betreib dort das CMS, die öffentliche API und deine Astro-Website mit Docker und automatischem HTTPS.

Quest-Ziele
- Wissen, was auf einem Produktionsserver wo läuft
- Eine Domain mit DNS-Einträgen auf einen Server zeigen lassen
- Die Astro-Website als Docker-Image verpacken
- Alles mit Docker Compose und Caddy starten
- Deine Inhalte von deinem Computer auf den Server umziehen
Dein Portfolio läuft auf deinem Computer. Jetzt zieht es auf einen Server, damit alle es erreichen. Auf dem Server laufen sechs Programme nebeneinander, jedes in seinem eigenen Docker-Container:
das CMS mit dem Admin, unter
cms.robin.example,die öffentliche API, unter
api.robin.example,deine Astro-Website, unter
robin.example,der Site-Prozess aus Teil 2, falls du zu einer gestalteten Website zurückwechselst,
Valkey, der Cache aus Teil 1,
und Caddy, ein Webserver vor all dem. Er schickt jeden Besucher zum richtigen Programm und besorgt sich selbst HTTPS-Zertifikate, sodass deine Website das Schloss in der Adresszeile hat.
Ersetze in diesem Teil überall robin.example durch deine Domain und 203.0.113.10 durch die Adresse deines Servers.
Was du brauchst
Einen Server: einen kleinen virtuellen Server (VPS) mit Ubuntu 24.04 und mindestens 2 GB Arbeitsspeicher. Jeder Hoster bietet so etwas an, für ein paar Euro im Monat. Du bekommst seine IP-Adresse und ein Passwort oder einen SSH-Schlüssel für den Benutzer
root.Eine Domain, etwa
robin.example, von einem beliebigen Domain-Anbieter.
Die Domain auf den Server zeigen lassen
DNS ist das Adressbuch des Internets: Es macht aus robin.example die IP-Adresse deines Servers. Leg in den DNS-Einstellungen deines Domain-Anbieters drei A-Einträge an:
Typ Name Wert
A robin.example 203.0.113.10
A cms.robin.example 203.0.113.10
A api.robin.example 203.0.113.10Das Server-Projekt anlegen
Das Projekt aus Teil 1 lässt das CMS mit pnpm dev auf deinem Computer laufen. Für den Server schreibt die CLI ein zweites Projekt, in dem alles in Containern läuft. Führ das auf deinem Computer aus, in deinem Projektordner, mit deiner Domain und deiner E-Mail-Adresse:
$ pnpm dlx @manablox/cli create portfolio-server --preset docker --proxy caddy --database sqlite --website --admin-domain cms.robin.example --public-domain api.robin.example --acme-email robin@example.com --yesWriting portfolio-serverWrote 19 files to portfolio-serverInstalling dependencies with pnpmDependencies installedInitialised a git repository
--preset dockersteckt jeden Prozess in einen Container, mit einemDockerfileund einercompose.yml, die sie verbindet.--proxy caddystellt Caddy davor, mit automatischen Zertifikaten.--admin-domainund--public-domainsind die Adressen des Admins und der öffentlichen API.--acme-emailist die Adresse, an die die Zertifizierungsstelle Let's Encrypt schreibt, falls ein Zertifikat einmal deine Aufmerksamkeit braucht.--database sqliteund--websitepassen zu deinem Projekt aus Teil 1, damit deine Inhalte hineinpassen.
Die Geheimnisse in portfolio-server/.env wurden für den Server neu erzeugt. Gib diese Datei nie weiter.
Deine Astro-Website hinzufügen
Kopier die Website als Ordner web in das Server-Projekt, ohne die installierten Pakete und den alten Build:
$ cp -r my-site portfolio-server/web$ rm -rf portfolio-server/web/node_modules portfolio-server/web/dist portfolio-server/web/.git
Ein Dockerfile ist das Rezept für ein Container-Image. Dieses hier baut deine Website in einem ersten Schritt und kopiert nur, was sie zum Laufen braucht, in ein zweites, kleineres Image. Leg web/Dockerfile an:
# The portfolio: build the Astro site, then run its Node server.
FROM node:24-bookworm-slim AS build
WORKDIR /app
RUN corepack enable
ENV CI=true
COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./
RUN pnpm install --frozen-lockfile
COPY . .
RUN pnpm build
FROM node:24-bookworm-slim
WORKDIR /app
ENV NODE_ENV=production HOST=0.0.0.0 PORT=3005
COPY --from=build /app/node_modules ./node_modules
COPY --from=build /app/dist ./dist
COPY --from=build /app/package.json ./
USER node
EXPOSE 3005
CMD ["node", "./dist/server/entry.mjs"]Die .dockerignore daneben hält die Dateien aus dem Image heraus, die darin ohnehin neu entstehen:
node_modules
dist
.astro
.envDas CMS-Image wird aus dem ganzen Server-Ordner gebaut, sag ihm also, dass es deine Website weglassen soll. Füg am Ende von portfolio-server/.dockerignore eine Zeile hinzu:
webDie Website in Docker Compose eintragen
compose.yml listet die Container. Füg deine Website als neuen Dienst ein, direkt über caddy:. Im Compose-Netzwerk erreichen sich die Container über ihre Dienstnamen, deshalb liest die Website von http://public:3100:
# The portfolio itself: the Astro site in web/. It reads the content from the public
# API over the compose network.
web:
build: ./web
restart: unless-stopped
<<: *logging
depends_on:
- public
environment:
MANABLOX_URL: http://public:3100
MANABLOX_ADMIN_ORIGIN: ${PUBLIC_URL:?set PUBLIC_URL}Im Dienst caddy darunter ergänzt du web bei depends_on und reichst die neue Domain weiter:
caddy:
# ...
depends_on:
- api
- public
- web
# ...
environment:
ADMIN_DOMAIN: ${ADMIN_DOMAIN:?set ADMIN_DOMAIN}
SITE_DOMAIN: ${SITE_DOMAIN:?set SITE_DOMAIN}
# ...Dann sagst du Caddy, wo deine Website wohnt. Füg in caddy/Caddyfile diesen Block über der Zeile # manablox:slot caddy.sites ein:
# The portfolio, the Astro site in web/.
{$SITE_DOMAIN} {
import hardening
reverse_proxy web:3005
}Und gib der Domain in .env einen Wert, neben PUBLIC_DOMAIN:
# The portfolio, the Astro site in web/.
SITE_DOMAIN=robin.exampleHochladen und starten
rsync kopiert den Ordner auf den Server und lässt weg, was die Images selbst bauen:
$ rsync -av --exclude node_modules --exclude dist --exclude .git portfolio-server/ root@203.0.113.10:/opt/portfolio/Dann meldest du dich mit ssh auf dem Server an, installierst Docker mit seinem offiziellen Skript und startest alles. Der erste Build dauert ein paar Minuten.
$ ssh root@203.0.113.10$ curl -fsSL https://get.docker.com | sh$ cd /opt/portfolio$ docker compose up -d --build Image portfolio-server:latest Built Image portfolio-server-web Built Container portfolio-server-valkey-1 Healthy Container portfolio-server-api-1 Started Container portfolio-server-public-1 Started Container portfolio-server-site-1 Started Container portfolio-server-web-1 Started Container portfolio-server-caddy-1 Started
Deine Inhalte umziehen
Der Server hat ein leeres CMS. Deine Inhalte liegen auf deinem Computer, also nimmst du sie als Export mit. Öffne in deinem lokalen Admin unter localhost:3000 Settings → Export and transfer, wähl Zip archive, das auch die Bilder mitnimmt, und klick auf Download export.
Öffne jetzt https://cms.robin.example. Der Server begrüßt dich mit demselben Einrichtungsassistenten wie in Teil 1. Leg dein Konto an, mit einem neuen, starken Passwort, und wähl dann Import a space, statt einen Space anzulegen.
Der Import hat noch die Website-Adresse von deinem Computer. Öffne Settings, ändere die Website address auf https://robin.example und speichere. Der Knopf Visual zeigt dann die echte Website.
Online
Öffne https://robin.example. Dein Portfolio ist im Internet, mit HTTPS, deinen Inhalten und deinen Bildern, die jetzt von api.robin.example kommen. Teil den Link, und die Vorschauen zeigen deine Titelbilder.
Später etwas ändern
Inhalte änderst du im Admin unter cms.robin.example, und sie sind live, sobald du veröffentlichst. Das CMS auf deinem Computer war nur die Werkstatt.
Code änderst du auf deinem Computer, in my-site. Wenn du zufrieden bist, kopierst du die geänderten Dateien nach portfolio-server/web, lädst hoch und baust neu:
$ rsync -av --exclude node_modules --exclude dist --exclude .git portfolio-server/ root@203.0.113.10:/opt/portfolio/$ ssh root@203.0.113.10 "cd /opt/portfolio && docker compose up -d --build"
Das ist die ganze Reise: vom leeren Ordner zu einem CMS, einer gestalteten Website, deinem eigenen Astro-Frontend und einem Portfolio im Internet. Herzlichen Glückwunsch! Füg weiter Projekte hinzu, und wenn dir die Blöcke nicht mehr reichen, legst du im Admin neue Blocktypen an und schreibst für jeden eine Komponente, genau wie beim Kontaktknopf.



