Docker-Demo in 10 Minuten
Diese Anleitung startet Workspace lokal mit Docker Compose. Nutzen Sie sie, wenn Sie Workspace ohne Linux-Paket ausprobieren oder eine technische Demo aufsetzen möchten.
Nach Abschluss verfügen Sie über:
- einen laufenden Workspace-Container,
- eine PostgreSQL-Datenbank im Compose-Stack,
- eine lokale Mailpit-Oberfläche für Testmails,
- ein persistentes Docker-Volume für Konfiguration und Storage,
- einen initialen Administratorzugang,
- einen gesicherten
NUCLEUS_KEYinnucleus.env.
Voraussetzungen
Sie benötigen:
- Docker und Docker Compose,
- Netzwerkzugriff auf
git.schukai.meund Docker Hub, - ein leeres Arbeitsverzeichnis,
- keine lokale Source-Repository-Kopie,
- keinen Registry-Login für das veröffentlichte Release-Image.
Verwenden Sie ein explizites Release-Image:
git.schukai.me/releases/nucleus:4.59.01. Arbeitsverzeichnis anlegen
mkdir workspace-demo
cd workspace-demoDocker legt die persistenten Volumes später selbst an.
2. Images prüfen
docker pull git.schukai.me/releases/nucleus:4.59.0
docker pull axllent/mailpit:latestWenn bereits ein Registry-Login auf dem Zielsystem gesetzt ist und der Pull fehlschlägt, melden Sie sich testweise ab und wiederholen Sie den Pull:
docker logout git.schukai.me
docker pull git.schukai.me/releases/nucleus:4.59.0
docker pull axllent/mailpit:latest3. Compose-Datei anlegen
Legen Sie docker-compose.yml an:
services:
postgres:
image: postgres:15
environment:
- POSTGRES_DB
- POSTGRES_USER
- POSTGRES_PASSWORD
volumes:
- postgres-data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U \"$${POSTGRES_USER}\" -d \"$${POSTGRES_DB}\""]
interval: 5s
timeout: 5s
retries: 20
restart: unless-stopped
mailpit:
image: axllent/mailpit:latest
environment:
MP_SMTP_AUTH_ACCEPT_ANY: "true"
MP_SMTP_AUTH_ALLOW_INSECURE: "true"
ports:
- "127.0.0.1:8025:8025"
- "127.0.0.1:1025:1025"
restart: unless-stopped
nucleus:
image: git.schukai.me/releases/nucleus:4.59.0
depends_on:
postgres:
condition: service_healthy
env_file:
- nucleus.env
environment:
NUCLEUS_CONFIG: /data/nucleus.config.enc
ports:
- "8443:8443"
volumes:
- nucleus-data:/data
stdin_open: true
tty: true
restart: unless-stopped
volumes:
postgres-data:
nucleus-data:Der abschließende volumes-Block steht auf Root-Ebene der Compose-Datei. Rücken Sie ihn nicht unter services oder unter einen einzelnen Service ein.
4. Demo-Werte vorbereiten
Setzen Sie die PostgreSQL-Werte in derselben Shell, in der Sie später Docker Compose ausführen:
export POSTGRES_DB=nucleus
export POSTGRES_USER=nucleus
read -r POSTGRES_PASSWORD
export POSTGRES_PASSWORDWenn read wartet, geben Sie nucleus ein und drücken Sie Enter.
Nutzen Sie in echten Umgebungen andere Passwörter und stellen Sie sie über die Geheimnisverwaltung Ihrer Plattform bereit.
5. Env-Datei anlegen
touch nucleus.envDie Datei muss vor dem ersten Start existieren. Der NUCLEUS_KEY ist erst nach dem Setup bekannt.
6. PostgreSQL starten
docker compose up -d postgres
docker compose psWarten Sie, bis PostgreSQL als healthy gemeldet wird.
7. Setup-Assistent starten
docker compose run --rm nucleusDer Setup-Assistent schreibt die verschlüsselte Konfiguration nach /data/nucleus.config.enc. Diese Datei liegt im Docker-Volume nucleus-data. Der Assistent sieht Container-Pfade, nicht die Pfade Ihres Arbeitsverzeichnisses auf dem Host. Verwenden Sie deshalb für Storage und gemountete Dateien die Pfade, die im Container erreichbar sind.
Verwenden Sie für die lokale Demo diese Werte:
| Feld | Wert |
|---|---|
| Listen Address | :8443 |
| Certificate | Generate self-signed certificate |
| Host | localhost |
| System Domains (CSV) | localhost |
| Storage path | /data/storage |
| Database host | postgres |
| Database port | 5432 |
| Database user | nucleus |
| Database name | nucleus |
| Database password | nucleus |
Wenn ein älteres Image beim Storage-Auswahldialog Bummer. No Files Found. meldet, legen Sie den Ordner im Volume an und starten Sie den Assistenten erneut:
docker compose run --rm --entrypoint sh nucleus -lc 'mkdir -p /data/storage'
docker compose run --rm nucleusWenn Sie eigene TLS-Dateien verwenden, mounten Sie das Zertifikatsverzeichnis in den Container und wählen Sie im Setup die Container-Pfade aus:
volumes:
- nucleus-data:/data
- ./certs:/certs:ro/certs/workspace.crt
/certs/workspace.keyPEM bezeichnet dabei das Dateiformat der Zertifikats- oder Schlüsseldatei. Eine PEM-Datei enthält Blöcke wie BEGIN CERTIFICATE. Der private Schlüssel darf für den Containerstart keine interaktive Passphrase benötigen.
8. NUCLEUS_KEY speichern
Der Setup-Assistent gibt am Ende NUCLEUS_KEY aus. Öffnen Sie nucleus.env und tragen Sie genau eine Zeile ein:
NUCLEUS_KEY=<bundle-aus-dem-setup>Ersetzen Sie <bundle-aus-dem-setup> durch den vollständigen Wert aus dem Setup. Kürzen Sie den Wert nicht.
9. Stack starten
docker compose up -d
docker compose ps10. Start prüfen
curl -sk -i https://localhost:8443/api/v1/ping
curl -fsS http://localhost:8025/ >/dev/nullEin erfolgreicher Start liefert HTTP 200, status: ok und die laufende Version. Der zweite Aufruf prüft, ob die lokale Mailpit-Oberfläche erreichbar ist. Wenn ein Befehl fehlschlägt, prüfen Sie zuerst Status und Logs:
docker compose ps
docker compose logs nucleus
docker compose logs mailpit11. Erstzugangsdaten sichern
Beim ersten regulären Start legt Workspace den initialen System-Admin und einen initialen API-Key an. Der Server gibt diese Werte einmalig im Logblock SYSTEM INITIALIZED aus.
Lesen Sie den Block direkt nach dem ersten Start aus:
docker compose logs --no-color nucleus | grep -Ei 'SYSTEM INITIALIZED|User|Password|API Key|GENERATED CREDENTIALS'Speichern Sie Login, Passwort, API-Key und NUCLEUS_KEY in Ihrer Geheimnisverwaltung.
Lokale Mails mit Mailpit testen
Mailpit fängt ausgehende SMTP-Mails lokal ab. Nutzen Sie es, wenn Sie Checkout-Mails, Auth-Mails, Angebotsmails, Benachrichtigungsvorlagen oder Queue-Verhalten entwickeln möchten, ohne echte Empfänger anzuschreiben.
Öffnen Sie die Mailpit-Oberfläche auf dem Host:
http://localhost:8025Richten Sie den Workspace-Mailserver unter System > Mail Server für die Docker-Demo so ein:
| Feld | Wert |
|---|---|
| Host | mailpit |
| Port | 1025 |
| Verschlüsselung | none |
| Benutzername | leer lassen |
| Kennwort | leer lassen |
| Absenderadresse | dev@example.test |
| Absendername | Workspace Demo |
Testen Sie zuerst die Verbindung und senden Sie danach eine Test-Mail. Die Test-Mail muss in Mailpit erscheinen. Wenn Sie einen SMTP-Test vom Host ausführen, verwenden Sie statt mailpit den Host localhost und Port 1025; aus dem Workspace-Container heraus bleibt mailpit:1025 korrekt.
Prüfen Sie beim Entwickeln zusätzlich die Mail-Warteschlange und die betroffene Vorlage unter System > Benachrichtigungsvorlagen. Weitere fachliche Details stehen unter Mail und Benachrichtigungen.
Lokale Storefront-Domain mit DNS-TXT testen
Wenn Sie in der Docker-Demo eine Storefront-Domain vollständig lokal prüfen möchten, nutzen Sie eine Subdomain unter localhost.alvine.dev, zum Beispiel shop.localhost.alvine.dev. Das vorhandene Wildcard-DNS löst solche Hosts im Browser auf 127.0.0.1 auf. Sie müssen dafür keine Hosts-Datei ändern und brauchen auf NixOS oder verwalteten Geräten keine Administratorrechte.
CoreDNS bleibt trotzdem nötig: web domains verify-dns liest den TXT-Record aus Sicht des Workspace-Servers, und web wait muss den Shop-Host aus der Workspace-Runtime erreichen. CoreDNS liefert deshalb im Compose-Netz den A-/AAAA-Record auf den lokalen HTTPS-Proxy und den TXT-Record für die Tenant-Domain-Verifikation. Der Host-Browser nutzt dagegen weiter die öffentliche Wildcard-Auflösung nach 127.0.0.1.
Verwenden Sie localhost.alvine.dev selbst nicht als Storefront-Domain. Dieser Host bleibt der lokale System- und Admin-Einstieg. Nutzen Sie pro Shop oder Demo einen eigenen Subhost wie shop.localhost.alvine.dev, web1.localhost.alvine.dev oder kunde-a.localhost.alvine.dev.
Legen Sie dafür optional docker-compose.local-dns.yml an:
services:
coredns:
image: coredns/coredns:1.11.3
command: ["-conf", "/Corefile"]
ports:
- "127.0.0.1:1053:53/udp"
- "127.0.0.1:1053:53/tcp"
volumes:
- ./coredns/Corefile:/Corefile:ro
networks:
storefront-dev:
ipv4_address: 172.30.0.53
restart: unless-stopped
postgres:
networks:
- storefront-dev
mailpit:
networks:
- storefront-dev
nucleus:
depends_on:
coredns:
condition: service_started
postgres:
condition: service_healthy
dns:
- 172.30.0.53
networks:
- storefront-dev
networks:
storefront-dev:
ipam:
config:
- subnet: 172.30.0.0/24Legen Sie danach coredns/Corefile an:
. {
errors
log
hosts {
172.30.0.10 shop.localhost.alvine.dev
# Optional, wenn Ihr lokaler Ingress im Docker-Netz über IPv6 erreichbar ist:
# fd00:30::10 shop.localhost.alvine.dev
fallthrough
}
template IN TXT _nucleus-verify.shop.localhost.alvine.dev {
rcode NOERROR
answer "{{ .Name }} 60 IN TXT \"<verificationValue-aus-start-verification>\""
}
forward . 127.0.0.11
cache 30
}Setzen Sie die A-Zieladresse auf den lokalen Ingress oder Reverse Proxy im Compose-Netz. Die Adresse 172.30.0.10 ist im Beispiel für den lokalen HTTPS-Proxy aus dem nächsten Abschnitt reserviert. Wenn Sie IPv6 im Docker-Netz aktiviert haben, ergänzen Sie im hosts-Block zusätzlich den passenden AAAA-Eintrag. Ohne lokalen HTTPS-Proxy löst dieses Beispiel nur die DNS-TXT-Verifikation.
forward . 127.0.0.11 leitet alle nicht lokal definierten Namen an Dockers eingebetteten DNS-Resolver weiter. Dadurch kann der Workspace-Container trotz eigenem Resolver weiterhin Compose-Service-Namen wie postgres auflösen.
Starten Sie den Stack mit dem Override:
docker compose -f docker-compose.yml -f docker-compose.local-dns.yml up -dWenn Sie coredns/Corefile später ändern, starten Sie CoreDNS neu oder erstellen Sie den Service neu. CoreDNS liest die Corefile nicht automatisch neu ein:
docker compose -f docker-compose.yml -f docker-compose.local-dns.yml restart corednsPrüfen Sie die Sicht aus dem Compose-Netz:
docker compose -f docker-compose.yml -f docker-compose.local-dns.yml exec nucleus \
sh -lc 'grep 172.30.0.53 /etc/resolv.conf'
docker run --rm --network workspace-demo_storefront-dev alpine:3.20 \
sh -lc 'apk add --no-cache bind-tools >/dev/null && dig @172.30.0.53 TXT _nucleus-verify.shop.localhost.alvine.dev +short'Der erste Befehl zeigt, dass der Workspace-Container CoreDNS als Resolver nutzt. Der zweite Befehl muss den TXT-Wert aus web domains start-verification ausgeben. Wenn Ihr Arbeitsverzeichnis nicht workspace-demo heißt, ersetzen Sie den Netzwerknamen im zweiten Befehl durch den Namen aus docker network ls. Danach kann web domains verify-dns aus der Workspace-Runtime heraus erfolgreich sein.
Lokales HTTPS für web wait
web wait prüft nicht den Browser, sondern den Workspace-Server. Der Check ruft https://<tenant-domain>/ ohne Portangabe auf. Für lokale Storefront-Domains ist der Flow deshalb erst vollständig, wenn diese Bedingungen erfüllt sind:
- CoreDNS löst den Shop-Host auf den lokalen HTTPS-Endpunkt im Compose-Netz auf.
- Der HTTPS-Endpunkt ist aus dem Workspace-Container auf Port
443erreichbar. - Das Zertifikat enthält den Shop-Host als
subjectAltName. Ein Wildcard-SAN deckt nur passende Unterdomains ab; der Common Name allein reicht nicht. - Die ausstellende lokale CA oder das Zertifikat ist im Browser und in der Workspace-Runtime vertrauenswürdig.
- Der HTTPS-Endpunkt liefert für
https://shop.localhost.alvine.dev/keine5xx-Antwort.
Verwenden Sie für vollständige lokale Tests keinen nackten localhost-Host und nicht localhost.alvine.dev selbst als Shop-Domain. Viele Resolver behandeln localhost besonders, und localhost.alvine.dev bleibt der lokale Systemhost. Ein Subhost wie shop.localhost.alvine.dev nutzt im Browser die vorhandene Wildcard-Auflösung und kann in CoreDNS zusätzlich mit A-/AAAA- und TXT-Records für die Workspace-Runtime abgebildet werden.
Erzeugen Sie zuerst eine lokale CA und ein Serverzertifikat für den Shop-Host:
mkdir -p certs
openssl genrsa -out certs/local-dev-ca.key 4096
openssl req -x509 -new -nodes \
-key certs/local-dev-ca.key \
-sha256 -days 825 \
-out certs/local-dev-ca.crt \
-subj "/CN=Workspace Local Dev CA"
cat > certs/shop.localhost.alvine.dev.ext <<'EOF'
authorityKeyIdentifier=keyid,issuer
basicConstraints=CA:FALSE
keyUsage = digitalSignature, keyEncipherment
extendedKeyUsage = serverAuth
subjectAltName = @alt_names
[alt_names]
DNS.1 = shop.localhost.alvine.dev
IP.1 = 127.0.0.1
EOF
openssl genrsa -out certs/shop.localhost.alvine.dev.key 2048
openssl req -new \
-key certs/shop.localhost.alvine.dev.key \
-out certs/shop.localhost.alvine.dev.csr \
-subj "/CN=shop.localhost.alvine.dev"
openssl x509 -req \
-in certs/shop.localhost.alvine.dev.csr \
-CA certs/local-dev-ca.crt \
-CAkey certs/local-dev-ca.key \
-CAcreateserial \
-out certs/shop.localhost.alvine.dev.crt \
-days 825 -sha256 \
-extfile certs/shop.localhost.alvine.dev.ext
openssl x509 -in certs/shop.localhost.alvine.dev.crt -noout -issuer -subject -ext subjectAltNameCommitten Sie den Ordner certs/ nicht. Er enthält lokale Schlüsseldateien und gehört nur in Ihr Arbeitsverzeichnis.
Prüfen Sie bei vorhandenen Zertifikaten, ob der Shop-Host als SAN enthalten ist:
openssl x509 -in certs/workspace.crt -noout -ext subjectAltNameWenn der Host fehlt, erzeugen Sie ein neues lokales Zertifikat mit dem konkreten Host oder einem passenden Wildcard-SAN. Importieren Sie anschließend die lokale CA in den Trust Store des Betriebssystems oder Browsers.
Linux mit Debian-/Ubuntu-Trust-Store:
sudo cp certs/local-dev-ca.crt /usr/local/share/ca-certificates/workspace-local-dev-ca.crt
sudo update-ca-certificatesmacOS:
sudo security add-trusted-cert -d -r trustRoot \
-k /Library/Keychains/System.keychain \
certs/local-dev-ca.crtWindows PowerShell:
Import-Certificate `
-FilePath .\certs\local-dev-ca.crt `
-CertStoreLocation Cert:\CurrentUser\RootWenn Ihr Browser einen eigenen Zertifikatsspeicher nutzt, importieren Sie certs/local-dev-ca.crt dort zusätzlich als vertrauenswürdige Zertifizierungsstelle.
Wenn Sie nucli außerhalb des Containers gegen diese lokale HTTPS-Demo verwenden, übergeben Sie ebenfalls die lokale CA:
SSL_CERT_FILE="$PWD/certs/local-dev-ca.crt" nucli --host https://shop.localhost.alvine.dev discoverySSL_CERT_FILE muss auf die ausstellende CA zeigen, nicht auf certs/shop.localhost.alvine.dev.crt. Wenn Sie nucli login in einer temporären Demo-Shell nutzen, setzen Sie zusätzlich ein beschreibbares --config-dir und stellen Sie einen Token-Store bereit, zum Beispiel mit secret-tool auf dem Host oder mit NUCLI_STORE_KEY für eine lokale verschlüsselte Token-Datei.
Für den Readiness-Check reicht der Browserimport nicht aus: Die Workspace-Runtime muss derselben CA ebenfalls vertrauen. Legen Sie dafür zum Beispiel Dockerfile.nucleus-local-ca an:
ARG NUCLEUS_IMAGE=git.schukai.me/releases/nucleus:4.59.0
FROM ${NUCLEUS_IMAGE}
USER 0
COPY certs/local-dev-ca.crt /tmp/workspace-local-dev-ca.crt
RUN set -eu; \
if command -v update-ca-certificates >/dev/null 2>&1; then \
mkdir -p /usr/local/share/ca-certificates; \
cp /tmp/workspace-local-dev-ca.crt /usr/local/share/ca-certificates/workspace-local-dev-ca.crt; \
update-ca-certificates; \
elif command -v trust >/dev/null 2>&1; then \
trust anchor /tmp/workspace-local-dev-ca.crt; \
elif command -v keytool >/dev/null 2>&1 && [ -n "${JAVA_HOME:-}" ] && [ -f "$JAVA_HOME/lib/security/cacerts" ]; then \
keytool -importcert -noprompt -storepass changeit -alias workspace-local-dev-ca \
-file /tmp/workspace-local-dev-ca.crt -keystore "$JAVA_HOME/lib/security/cacerts"; \
elif [ -d /etc/ssl/certs ]; then \
cat /tmp/workspace-local-dev-ca.crt >> /etc/ssl/certs/ca-certificates.crt; \
else \
echo "No supported CA trust store path found in Workspace image" >&2; \
exit 1; \
fi; \
rm -f /tmp/workspace-local-dev-ca.crtUSER 0 verwendet die numerische Root-UID und funktioniert auch in minimalen Images, in denen kein Benutzername root in /etc/passwd steht. Der Dockerfile-Block nutzt zuerst die üblichen Trust-Store-Werkzeuge. Wenn das Image keines dieser Werkzeuge enthält, hängt er die lokale CA nur für diese Demo an das übliche CA-Bundle unter /etc/ssl/certs/ca-certificates.crt an. Wenn auch dieser Pfad fehlt, bricht der Build mit einer klaren Meldung ab.
Erweitern Sie docker-compose.local-dns.yml um einen lokalen HTTPS-Proxy. Fügen Sie die build-Eigenschaft in den bereits vorhandenen nucleus-Service ein; legen Sie keinen zweiten nucleus-Block an:
services:
storefront-proxy:
image: caddy:2
depends_on:
nucleus:
condition: service_started
ports:
- "127.0.0.1:443:443"
volumes:
- ./certs:/certs:ro
- ./Caddyfile:/etc/caddy/Caddyfile:ro
networks:
storefront-dev:
ipv4_address: 172.30.0.10
restart: unless-stopped
nucleus:
build:
context: .
dockerfile: Dockerfile.nucleus-local-ca
args:
NUCLEUS_IMAGE: git.schukai.me/releases/nucleus:4.59.0Legen Sie danach Caddyfile an:
shop.localhost.alvine.dev {
tls /certs/shop.localhost.alvine.dev.crt /certs/shop.localhost.alvine.dev.key
reverse_proxy https://nucleus:8443 {
transport http {
tls_insecure_skip_verify
}
}
}tls_insecure_skip_verify gilt nur für diese lokale Demo, weil der Workspace-Upstream in Schritt 7 ein selbstsigniertes Zertifikat nutzt. Verwenden Sie diese Einstellung nicht vor produktiven Upstreams.
Auf dem Host ist kein Hosts-Datei-Eintrag nötig. Das Wildcard-DNS für *.localhost.alvine.dev löst shop.localhost.alvine.dev bereits auf 127.0.0.1 auf, und der lokale Caddy-Proxy bindet dort Port 443. Wenn Ihr Netz diese öffentliche Auflösung blockiert, nutzen Sie für einzelne Tests einen prozesslokalen Resolver-Override im Browser oder curl --resolve; dauerhafte Host- oder DNS-Änderungen gehören dann in die Verantwortung der Systemadministration. Die DNS-TXT-Verifikation läuft unabhängig davon über CoreDNS im Compose-Netz.
Starten Sie den Stack nach den Zertifikatsänderungen neu:
docker compose -f docker-compose.yml -f docker-compose.local-dns.yml up -d --buildPrüfen Sie den Pfad aus Host- und Runtime-Sicht:
curl -i https://shop.localhost.alvine.dev/
docker compose -f docker-compose.yml -f docker-compose.local-dns.yml exec nucleus \
sh -lc 'getent hosts shop.localhost.alvine.dev'
nucli --tenant <tenant> web readiness <site-id>
nucli --tenant <tenant> web wait <site-id> --timeout 5mweb wait kann erst operations readiness: ready melden, wenn DNS, Domain- Verifikation, Site-Bindung und HTTPS-Vertrauen zusammenpassen.
Demo-Stack zurücksetzen
Setzen Sie eine lokale Demo nur zurück, wenn Sie die Daten wirklich verwerfen wollen. Container und Volumes gehören zusammen: Wenn Sie nur nucleus.env löschen, bleibt die verschlüsselte Konfiguration im Volume erhalten und passt möglicherweise nicht mehr zum neuen NUCLEUS_KEY.
Stoppen Sie den Stack und entfernen Sie die relevanten Volumes:
docker compose down
docker volume ls
docker volume rm <postgres-volume>
docker volume rm <nucleus-volume>Starten Sie danach wieder bei PostgreSQL und Setup. Legen Sie bei Bedarf zuerst den Storage-Ordner im frischen Volume an:
docker compose run --rm --entrypoint sh nucleus -lc 'mkdir -p /data/storage'
docker compose run --rm nucleusHäufige Fehler
| Fehler | Lösung |
|---|---|
127.0.0.1:5432 ... connect: connection refused | Führen Sie das Setup erneut aus und verwenden Sie postgres als Database host. |
| Browser warnt vor Zertifikat | Für lokale Demos mit selbstsigniertem Zertifikat ist das erwartbar. |
nucleus.env fehlt | Legen Sie die Datei vor dem ersten docker compose run an. |
runtime key bundle missing | Führen Sie das Setup aus und tragen Sie den vollständigen NUCLEUS_KEY in nucleus.env ein. |
NUCLEUS_KEY fehlt nach dem Setup | Tragen Sie den vollständigen Wert aus dem Setup in nucleus.env ein. |
Neuer NUCLEUS_KEY, aber alte Konfiguration | Setzen Sie Demo-Volumes und nucleus.env gemeinsam zurück oder verwenden Sie den bisherigen passenden Schlüssel. |
| Erstzugangsdaten nicht gesichert | Lesen Sie den Logblock SYSTEM INITIALIZED direkt nach dem ersten Start. |
Weitere Details zu Containerbetrieb und Ingress stehen unter Container und Ingress.