Hallo zusammen,
ich habe Stunden benötigt und vielleicht kann sich jemand in Zukunft dadurch Zeit sparen.
Hier eine Hilfsanleitung für Anfänger zum Installieren von Shopware 6 auf einem VPS. Keine Garantie auf Vollständigkeit und Richtigkeit.
Konstruktiv kritische Kommentare und Verbesserungsvorschläge sehr gerne.
Viel Spaß
Shopware 6 auf einem VPS
mit Docker – Schritt für Schritt
Eine Anleitung für absolute Anfänger
| 4 vCores CPU | 4 GB RAM | 120 GB NVMe SSD | Debian 13 / PHP 8.4 |
|---|---|---|---|
Version: Februar 2026 | Shopware 6.7.x | PHP 8.4
Vorwort
Diese Anleitung richtet sich an absolute Einsteiger, die noch nie einen eigenen Server eingerichtet haben. Sie beschreibt Schritt für Schritt, wie ein VPS (virtueller privater Server) gesichert, Docker installiert und Shopware 6 als Online-Shop in Betrieb genommen wird.
Wir haben viele Stunden damit verbracht, diese Schritte selbst herauszufinden – und dabei mehr als einmal von vorne angefangen. Damit du dir vielleicht einiges sparst, ist diese Anleitung entstanden. Sie ersetzt keine professionelle Systemadministration, ist aber ein solider Startpunkt.
Über konstruktiv kritische Anmerkungen, Präzisierungen oder andere Hinweise freuen wir uns sehr und werden sie einarbeiten.
HINWEIS: Alle Befehle werden der Reihe nach in einem Terminal (SSH-Verbindung) eingegeben.
Was ist was? – Kurze Begriffserklärung
-
VPS (Virtual Private Server): Ein gemieteter virtueller Computer im Rechenzentrum. Du verbindest dich per Internet damit.
-
SSH: Eine sichere verschlüsselte Verbindung vom eigenen PC zum Server. Wie ein Fernzugriff übers Terminal.
-
Docker: Ein Programm, das Anwendungen in abgeschlossenen „Containern" laufen lässt – sauber, reproduzierbar und leicht zu starten/stoppen.
-
Docker Compose: Ein Werkzeug, das mehrere Container gleichzeitig steuert. Unser Shop besteht aus mehreren Teilen: der Web-App, der Datenbank, einem Hintergrundprozess (Worker) und einem Aufgabenplaner (Scheduler).
-
Shopware 6: Das Shop-System, das wir installieren. Es läuft komplett im Docker-Container.
-
Nginx: Ein Webserver, der Anfragen aus dem Internet entgegennimmt und an Shopware weiterleitet. Außerdem stellt er das SSL-Zertifikat (https://) bereit.
-
Domain: Deine Internetadresse, z. B. mein-shop.de. Ohne Domain erreichst du den Shop zunächst nur über die IP-Adresse des Servers.
-
Termius: Lade dir Termius herunter (free Version reicht aus). Termius ist ein SSH-Client – also eine App, mit der du dich von deinem lokalen Gerät (PC, Mac, Smartphone) auf einen entfernten Server verbinden kannst. Dein Shopware-Server läuft irgendwo (z.B. eine VM oder ein VPS). Um dort Befehle auszuführen – wie
docker compose upoder Logs lesen – brauchst du eine SSH-Verbindung zu diesem Server. Termius ist dabei einfach das Werkzeug, das diese Verbindung herstellt und dir ein Terminal-Fenster auf dem Server öffnet.
Teil 1 – VPS absichern
Bevor wir irgendetwas installieren, machen wir den Server sicher. Ein frischer VPS ist im Internet sofort sichtbar und wird binnen Minuten von automatisierten Angriffen gescannt. Die folgenden Schritte schützen ihn auf ein solides Grundniveau.
Schritt 1 – System aktualisieren & Benutzer anlegen
Als erstes melden wir uns als root-User am Server an und aktualisieren alle vorinstallierten Programme auf den neuesten Stand. Anschließend legen wir einen eigenen Benutzer an – denn der root-Account hat unbegrenzte Rechte und sollte nicht für die tägliche Arbeit genutzt werden.
Der Benutzername in dieser Anleitung lautet nutzername. Du kannst ihn frei wählen – tausche dann aber überall in der Anleitung nutzername gegen deinen Namen aus.
# System-Pakete aktualisieren
apt update && apt upgrade -y \
# Neuen Benutzer anlegen (nutzername nach Wunsch anpassen)
&& adduser nutzername \
# Benutzer zur sudo-Gruppe hinzufügen (damit er Admin-Befehle ausführen darf)
&& usermod -aG sudo nutzername
TIPP: Du wirst nach einem Passwort für
nutzernamegefragt. Wähle ein starkes Passwort und notiere es sicher. Die weiteren Fragen (Name, Telefon etc.) kannst du mit Enter überspringen.
Schritt 2 – SSH-Key-Authentifizierung einrichten
Normalerweise meldet man sich per Passwort am Server an. SSH-Keys sind sicherer: Du erzeugst ein Schlüsselpaar auf deinem lokalen PC, legst den öffentlichen Teil auf dem Server ab – und ab dem nächsten Schritt kommst nur du mit deinem privaten Schlüssel rein.
Auf deinem lokalen PC (Linux / macOS):
# Schlüsselpaar erzeugen (ed25519 ist der moderne, sichere Standard)
ssh-keygen -t ed25519 -C "deine@email.de"
# Öffentlichen Schlüssel auf den Server kopieren
ssh-copy-id nutzername@deine.IP.vom.VPS
Auf Windows (PowerShell7), falls ssh-copy-id nicht verfügbar ist:
type $env:USERPROFILE\.ssh\id_ed25519.pub | ssh nutzername@deine.IP.vom.VPS \
"mkdir -p ~/.ssh && chmod 700 ~/.ssh && cat >> ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys"
Verbindung testen:
# Sollte ohne Passwortabfrage funktionieren
ssh nutzername@deine.IP.vom.VPS
Schritt 3 – SSH-Konfiguration absichern
Jetzt deaktivieren wir den root-Login, schalten die Passwort-Anmeldung ab und verlegen SSH auf einen anderen Port (2222 statt Standard 22). Das macht automatisierte Angriffe erheblich schwerer. Den Port 2222 musst du ggf. noch in der Oberfläche deines VPS-Anbieters freischalten.
WARNUNG: WICHTIG: Lass deine aktuelle SSH-Verbindung (Termius-Session) offen! Öffne danach eine NEUE Verbindung in Termius, um zu prüfen, ob der Login noch klappt. Erst wenn der neue Login funktioniert, kannst du die alte Session schließen. Machst du das nicht, sperrst du dich aus! Dann musst du den VPS zurücksetzen und von vorne beginnen.
# Root-Login deaktivieren
sudo sed -i 's/^#\?PermitRootLogin.*/PermitRootLogin no/' /etc/ssh/sshd_config \
# Passwort-Login deaktivieren (nur noch SSH-Key erlaubt)
&& sudo sed -i 's/^#\?PasswordAuthentication.*/PasswordAuthentication no/' /etc/ssh/sshd_config \
# SSH-Key-Login explizit aktivieren
&& sudo sed -i 's/^#\?PubkeyAuthentication.*/PubkeyAuthentication yes/' /etc/ssh/sshd_config \
# SSH-Port von 22 auf 2222 ändern (reduziert automatische Scan-Angriffe)
&& sudo sed -i 's/^#\?Port .*/Port 2222/' /etc/ssh/sshd_config \
# Nur unseren Benutzer zum Login erlauben
&& echo 'AllowUsers nutzername' | sudo tee -a /etc/ssh/sshd_config \
# SSH-Dienst neu starten, damit die Änderungen wirksam werden
&& sudo systemctl restart sshd
Neue Verbindung im zweiten Termius-Tab testen (Port 2222 beachten!):
ssh -p 2222 nutzername@deine.IP.vom.VPS
TIPP: Ab jetzt lautet dein SSH-Befehl immer:
ssh -p 2222 nutzername@IP– vergiss den Port nicht!
Schritt 4 – UFW-Firewall einrichten
UFW (Uncomplicated Firewall) ist eine einfache Firewall für Linux. Wir erlauben nur die Ports, die wir wirklich brauchen: 2222 (SSH), 80 (HTTP) und 443 (HTTPS). Alles andere wird geblockt.
# UFW installieren
sudo apt install ufw -y \
# Standardregel: eingehenden Traffic verbieten
&& sudo ufw default deny incoming \
# Standardregel: ausgehenden Traffic erlauben
&& sudo ufw default allow outgoing \
# Benötigte Ports freigeben
&& sudo ufw allow 2222/tcp \ # SSH
&& sudo ufw allow 80/tcp \ # HTTP (für Let's Encrypt / Weiterleitung)
&& sudo ufw allow 443/tcp \ # HTTPS (der eigentliche Shop)
# Firewall aktivieren (yes | bestätigt automatisch die Rückfrage)
&& yes | sudo ufw enable \
# Status überprüfen (sollte die 3 Ports anzeigen)
&& sudo ufw status verbose
HINWEIS: Der Befehl
yes | sudo ufw enablebestätigt die Aktivierung automatisch. Danach zeigtufw status verbosedie aktiven Regeln an – dort sollten die drei Ports 2222, 80 und 443 erscheinen.
WARNUNG: Docker umgeht UFW standardmäßig über iptables direkt. Das bedeutet: Wenn MariaDB jemals mit
"ports: 3306:3306"in der compose.yaml exposed wird, ist sie von außen erreichbar – UFW sieht das nicht! Deshalb niemals den Datenbank-Port nach außen exponieren. In unserer Konfiguration ist das korrekt gelöst: Der Port 3306 ist in der compose.yaml bewusst nicht unter"ports:"aufgeführt.
Schritt 5 – Fail2Ban installieren (Brute-Force-Schutz)
Fail2Ban überwacht die Login-Versuche auf dem Server. Nach 3 Fehlversuchen innerhalb von 10 Minuten wird die angreifende IP-Adresse für 1 Stunde gesperrt. Das schützt vor automatisierten Passwort-Rateangriffen.
# Fail2Ban installieren
sudo apt install fail2ban -y \
# Standard-Konfiguration als Basis kopieren
&& sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local \
# SSH-Schutz konfigurieren
&& sudo tee -a /etc/fail2ban/jail.local > /dev/null << 'EOF'
[sshd]
enabled = true
port = 2222 # Unser angepasster SSH-Port
maxretry = 3 # Nach 3 Fehlversuchen sperren
bantime = 3600 # Sperre für 1 Stunde (3600 Sekunden)
findtime = 600 # Zeitfenster: 10 Minuten (600 Sekunden)
EOF
# Fail2Ban aktivieren und starten
sudo systemctl enable fail2ban && sudo systemctl start fail2ban
Teil 2 – Docker installieren
Docker ist die Grundlage für unsere Shopware-Installation. Es ermöglicht, Shopware und die dazugehörige Datenbank in isolierten Containern zu betreiben – sauber, wiederherstellbar und einfach zu aktualisieren.
Schritt 1 – Automatische Sicherheitsupdates aktivieren
Damit der Server immer auf dem aktuellen Stand bleibt, aktivieren wir automatische Sicherheitsupdates.
sudo apt install unattended-upgrades -y \
&& sudo dpkg-reconfigure -plow unattended-upgrades
HINWEIS: Du wirst gefragt, ob automatische Updates aktiviert werden sollen – wähle Ja.
Schritt 2 – Docker installieren
Docker ist nicht in den Standard-Paketquellen von Debian enthalten. Wir fügen die offizielle Docker-Paketquelle hinzu und installieren Docker darüber.
# Benötigte Hilfspakete für HTTPS-Downloads installieren
sudo apt install -y apt-transport-https ca-certificates curl gnupg lsb-release \
# Verzeichnis für Paketquellen-Schlüssel anlegen
&& sudo install -m 0755 -d /etc/apt/keyrings \
# Offiziellen Docker-GPG-Schlüssel herunterladen und speichern
&& curl -fsSL https://download.docker.com/linux/debian/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg \
&& sudo chmod a+r /etc/apt/keyrings/docker.gpg \
# Offizielle Docker-Paketquelle hinzufügen
&& echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] \
https://download.docker.com/linux/debian $(. /etc/os-release && echo "$VERSION_CODENAME") stable" \
| sudo tee /etc/apt/sources.list.d/docker.list > /dev/null \
# Paketliste aktualisieren und Docker installieren
&& sudo apt update \
&& sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin \
# Docker beim Systemstart automatisch starten
&& sudo systemctl enable docker && sudo systemctl start docker \
# Unserem Benutzer erlauben, Docker ohne sudo zu nutzen
&& sudo usermod -aG docker nutzername
HINWEIS: Der letzte Befehl (
usermod) gibtnutzernamedie Berechtigung, Docker ohnesudozu nutzen. Diese Änderung wird erst nach dem nächsten Ein- und Ausloggen wirksam.
Schritt 3 – Zeitzone, Dienste und Logwatch einrichten
# Zeitzone auf Berlin setzen (für korrekte Zeitstempel in Logs)
sudo timedatectl set-timezone Europe/Berlin \
# Unnötige Dienste deaktivieren (Bluetooth, Drucker, Netzwerkerkennung)
# 2>/dev/null unterdrückt Fehlermeldungen, falls ein Dienst nicht existiert
&& sudo systemctl disable bluetooth cups avahi-daemon 2>/dev/null; \
# Logwatch: Sendet täglich eine Zusammenfassung der Server-Aktivitäten
sudo apt install logwatch -y
TIPP: Logwatch schickt dir täglich eine Zusammenfassung der Server-Aktivitäten per E-Mail – sofern du E-Mail-Versand auf dem Server eingerichtet hast. Ohne E-Mail-Konfiguration speichert es die Berichte lokal.
Schritt 4 – Installation prüfen
Jetzt ausloggen und neu einloggen (damit die Docker-Gruppe wirksam wird), dann prüfen:
# Neu einloggen:
exit
ssh -p 2222 nutzername@deine.IP.vom.VPS
# Alles auf einmal prüfen:
sudo ufw status && sudo fail2ban-client status sshd && docker --version && docker compose version
TIPP: Wenn alle vier Befehle ohne Fehlermeldung durchlaufen, ist die Basis fertig. Du siehst dann z. B.:
Docker version 27.x.xundDocker Compose version v2.x.x
Teil 3 – Shopware 6 installieren
Jetzt kommt der eigentliche Shop. Wir erstellen ein Shopware-Projekt mit Composer, bauen ein Docker-Image und starten alle nötigen Dienste mit Docker Compose.
HINWEIS: Was ist Docker Compose? Docker Compose ist ein Werkzeug, das mehrere Container gleichzeitig steuert. Unser Shop besteht aus mehreren Teilen: der Web-App, der Datenbank, einem Hintergrundprozess (Worker) und einem Aufgabenplaner (Scheduler). Compose startet und verwaltet alle auf einmal.
Schritt 1 – Shopware-Projekt anlegen
Zuerst installieren wir Composer (ein PHP-Paketverwaltungsprogramm) und die benötigten PHP-Erweiterungen. Dann erstellen wir das Shopware-Projekt.
# Composer und PHP-Extensions installieren
sudo apt install -y php-cli unzip \
&& curl -sS https://getcomposer.org/installer | sudo php -- --install-dir=/usr/local/bin --filename=composer
sudo apt install -y \
php8.4-curl \
php8.4-gd \
php8.4-xml \
php8.4-mbstring \
php8.4-zip \
php8.4-intl \
php8.4-mysql \
php8.4-pdo \
php8.4-opcache
# Verzeichnis erstellen und Rechte setzen
sudo mkdir -p /opt/shopware
sudo chown nutzername:nutzername /opt/shopware
# Zum nutzername-Benutzer wechseln
su - nutzername
# Shopware-Projekt erstellen
cd /opt/shopware
composer create-project shopware/production:^6.7.7.1 .
composer require shopware/docker
HINWEIS:
su - nutzernamewechselt in den nutzername-Account. Wenn du fertig bist und zurück willst, tippeexit. Die Composer-Befehle dauern einige Minuten – das ist normal.
Schritt 2 – Dockerfile erstellen
Das Dockerfile beschreibt, wie das Docker-Image für Shopware gebaut wird. Es basiert auf einem fertigen Shopware-Basis-Image und ergänzt unseren Shop-Code.
cat > Dockerfile << 'EOF'
#syntax=docker/dockerfile:1.4
ARG PHP_VERSION=8.4
FROM ghcr.io/shopware/docker-base:${PHP_VERSION}-frankenphp AS base-image
FROM ghcr.io/shopware/shopware-cli:latest-php-${PHP_VERSION} AS shopware-cli
# Build-Phase: Shop-Code vorbereiten
FROM shopware-cli AS build
ADD . /src
WORKDIR /src
RUN --mount=type=cache,target=/root/.composer \
--mount=type=cache,target=/root/.npm \
/usr/local/bin/entrypoint.sh shopware-cli project ci /src
# Produktions-Image: Shop-Code ins fertige Image kopieren
FROM base-image
COPY --from=build --chown=82 --link /src /var/www/html
EOF
Schritt 3 – Docker Compose Konfiguration erstellen
Die von Composer generierte compose.yaml ist nur für lokale Entwicklung gedacht. Wir ersetzen sie durch eine Production-Konfiguration. Diese startet folgende Dienste:
-
database – MariaDB-Datenbank, in der alle Shop-Daten gespeichert werden
-
init-perm – Setzt einmalig die Dateirechte für geteilte Ordner
-
init – Richtet Shopware ein (Datenbank-Migrationen, erste Installation)
-
web – Die eigentliche Shopware-Webanwendung, erreichbar auf Port 8000
-
worker – Verarbeitet Hintergrundaufgaben (z. B. E-Mails, Importe)
-
scheduler – Führt regelmäßige Aufgaben aus (z. B. Preis-Updates, Cache-Bereinigung)
rm -f compose.yaml compose.override.yaml \
&& cat > compose.yml << 'EOF'
x-shopware-environment: &shopware
environment:
APP_ENV: prod
APP_SECRET: "${APP_SECRET}"
APP_URL: "${APP_URL:-http://localhost:8000}"
DATABASE_URL: "mysql://shopware:${MYSQL_PASSWORD:-shopware}@database/shopware"
DATABASE_HOST: "database"
JWT_PRIVATE_KEY: "${JWT_PRIVATE_KEY}"
JWT_PUBLIC_KEY: "${JWT_PUBLIC_KEY}"
# Auf 0 setzen, wenn der webinstaller verwendet werden soll
SHOPWARE_SKIP_WEBINSTALLER: 1
# Installation defaults (only used by the init container on first run)
INSTALL_LOCALE: "${INSTALL_LOCALE:-de-DE}"
INSTALL_CURRENCY: "${INSTALL_CURRENCY:-EUR}"
INSTALL_ADMIN_USERNAME: "${INSTALL_ADMIN_USERNAME:-admin}"
INSTALL_ADMIN_PASSWORD: "${INSTALL_ADMIN_PASSWORD:-shopware}"
volumes:
- files:/var/www/html/files
- theme:/var/www/html/public/theme
- media:/var/www/html/public/media
- thumbnail:/var/www/html/public/thumbnail
- sitemap:/var/www/html/public/sitemap
services:
database:
image: mariadb:11.4
restart: unless-stopped
environment:
MARIADB_ROOT_PASSWORD: "${MYSQL_ROOT_PASSWORD:-root}"
MARIADB_USER: shopware
MARIADB_PASSWORD: "${MYSQL_PASSWORD:-shopware}"
MARIADB_DATABASE: shopware
volumes:
- mysql-data:/var/lib/mysql
healthcheck:
test: ["CMD", "mariadb-admin", "ping", "-h", "localhost", "-p${MYSQL_ROOT_PASSWORD:-root}"]
interval: 10s
timeout: 5s
retries: 5
init-perm:
image: alpine
<<: *shopware
command: >
chown 82:82
/var/www/html/files
/var/www/html/public/theme
/var/www/html/public/media
/var/www/html/public/thumbnail
/var/www/html/public/sitemap
init:
image: shopware-local
build:
context: .
<<: *shopware
entrypoint: ["php", "vendor/bin/shopware-deployment-helper", "run"]
depends_on:
database:
condition: service_healthy
init-perm:
condition: service_completed_successfully
web:
image: shopware-local
build:
context: .
<<: *shopware
restart: unless-stopped
depends_on:
init:
condition: service_completed_successfully
ports:
- "8000:8000"
worker:
image: shopware-local
build:
context: .
<<: *shopware
restart: unless-stopped
depends_on:
init:
condition: service_completed_successfully
entrypoint: ["php", "bin/console", "messenger:consume", "async",
"low_priority", "--time-limit=300", "--memory-limit=512M"]
scheduler:
image: shopware-local
build:
context: .
<<: *shopware
restart: unless-stopped
depends_on:
init:
condition: service_completed_successfully
entrypoint: ["php", "bin/console", "scheduled-task:run"]
volumes:
mysql-data:
files:
theme:
media:
thumbnail:
sitemap:
EOF
Schritt 4 – Konfiguration & Passwörter (.env-Datei erstellen)
Die .env-Datei ist die zentrale Konfigurationsdatei. Hier werden alle wichtigen Einstellungen gespeichert: die Shop-URL, Datenbankpasswörter und kryptografische Schlüssel. Das folgende Skript erzeugt sie automatisch.
WARNUNG: WICHTIG:
INSTALL_LOCALElegt die Systemsprache fest. Diese Einstellung kann nach der ersten Installation NICHT mehr geändert werden! Setze sie daher jetzt korrekt, bevor du in Schritt 5 mitdocker compose upstartest.
# APP_SECRET aus der bestehenden .env wiederverwenden
APP_SECRET=$(grep -oP '^APP_SECRET=\K.*' .env)
# JWT-Schlüssel automatisch generieren
eval "$(docker run --rm ghcr.io/shopware/shopware-cli:latest project generate-jwt --env)"
# .env-Datei schreiben
cat > .env << EOF
APP_ENV=prod
APP_SECRET=${APP_SECRET}
# Deine Shop-URL -- JETZT anpassen!
# Hast du noch keine Domain, trage zunächst die IP ein: http://1.2.3.4:8000
# Sobald eine Domain vorhanden ist, hier auf https://deine-domain.de ändern
APP_URL=https://deine-domain.de
# Datenbankpasswörter -- starke eigene Passwörter wählen und notieren!!!
MYSQL_ROOT_PASSWORD=sicheres_root_passwort_hier
MYSQL_PASSWORD=sicheres_shopware_passwort_hier
# Kryptografische Schlüssel (automatisch generiert, nicht ändern)
JWT_PRIVATE_KEY='${JWT_PRIVATE_KEY}'
JWT_PUBLIC_KEY='${JWT_PUBLIC_KEY}'
# Systemsprache und Währung -- NUR VOR ERSTINSTALLATION ÄNDERBAR!
INSTALL_LOCALE=de-DE
INSTALL_CURRENCY=EUR
# Admin-Zugangsdaten für das Backend (nur bei Erstinstallation)
INSTALL_ADMIN_USERNAME=admin
INSTALL_ADMIN_PASSWORD=sicheres_admin_passwort_hier
EOF
echo "Die .env-Datei wurde erfolgreich erstellt."
WARNUNG: Öffne die
.envjetzt mit einem Texteditor (z. B.nano .env) und ersetze alle Platzhalter (sicheres_..._passwort_hier) durch echte, starke Passwörter. Tipp:openssl rand -hex 16generiert ein sicheres Zufallspasswort. Diese Datei darf niemals in ein Git-Repository eingecheckt werden! Diese Datei darf niemals veröffentlicht werden!
TIPP: Was ist
APP_URL? Das ist die Adresse, unter der dein Shop erreichbar sein soll. Hast du noch keine Domain, trage zunächsthttp://deine.IP.vom.VPS:8000ein. Sobald eine Domain vorhanden ist und Nginx mit SSL läuft (Schritt 6), änderst du sie aufhttps://deine-domain.de.
Schritt 5 – Shop bauen und starten
Jetzt bauen wir das Docker-Image und starten alle Dienste. Dieser Schritt dauert beim ersten Mal je nach Internetverbindung und Server-Leistung zwischen 5 und 20 Minuten – das ist normal!
# Image bauen und alle Dienste starten
docker compose build --no-cache \
&& docker compose up -d \
&& docker compose ps
HINWEIS:
--no-cachesorgt dafür, dass das Image komplett neu gebaut wird, ohne auf zwischengespeicherte Schichten zurückzugreifen. Das ist beim ersten Mal sinnvoll, dauert aber länger.
Logs beobachten (Ctrl+C zum Beenden):
docker compose logs -f
Sobald der init-Container mit „exit 0" abgeschlossen ist und der web-Container läuft, ist der Shop erreichbar:
-
Frontend:
http://deine.IP.vom.VPS:8000(oderhttps://deine-domain.denach Schritt 6) -
Admin-Backend:
http://deine.IP.vom.VPS:8000/admin
TIPP: Falls der
init-Container mit einem Fehler abbricht (Exit-Code 1):docker compose logs initzeigt dir die genaue Fehlermeldung. Häufige Ursache: ein Fehler in der.env-Datei (z. B. ein fehlendes Passwort oder falscheAPP_URL).
Schritt 6 – Nginx als Reverse Proxy mit SSL einrichten
Im Produktivbetrieb soll der Shop über eine Domain mit https:// erreichbar sein. Dafür brauchen wir Nginx als Reverse Proxy und ein kostenloses SSL-Zertifikat von Let’s Encrypt (Certbot).
HINWEIS: Was macht Nginx hier? Shopware läuft intern auf Port 8000. Nginx nimmt Anfragen auf Port 80 (http) und 443 (https) entgegen und leitet sie an Shopware weiter. Certbot besorgt automatisch ein SSL-Zertifikat und erneuert es alle 90 Tage selbst.
Ersetze test.beispiel.de in den folgenden Befehlen durch deine echte Domain.
WARNUNG: Deine Domain muss bereits auf die IP-Adresse des Servers zeigen (DNS-Eintrag), bevor Certbot das Zertifikat ausstellen kann! Überprüfe das vorher mit:
ping deine-domain.de
sudo apt install -y nginx certbot python3-certbot-nginx
sudo tee /etc/nginx/sites-available/shopware << 'EOF'
server {
listen 80;
server_name deine-domain.de;
location / {
proxy_pass http://127.0.0.1:8000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_buffer_size 128k;
proxy_buffers 4 256k;
proxy_busy_buffers_size 256k;
}
}
EOF
sudo ln -s /etc/nginx/sites-available/shopware /etc/nginx/sites-enabled/ \
&& sudo nginx -t && sudo systemctl reload nginx \
&& sudo certbot --nginx -d deine-domain.de
HINWEIS: Certbot fragt dich nach einer E-Mail-Adresse (für Ablaufwarnungen) und ob HTTP-Anfragen automatisch auf HTTPS umgeleitet werden sollen. Beides mit Ja / Enter bestätigen.
Nach erfolgreicher SSL-Einrichtung: APP_URL in der .env auf https://deine-domain.de ändern und dann:
docker compose down && docker compose up -d
Teil 4 – Referenz & Wartung
Nützliche Docker-Befehle
| Befehl | Beschreibung |
|---|---|
docker compose up -d |
Alle Dienste im Hintergrund starten |
docker compose stop |
Alle Dienste stoppen |
docker compose ps |
Status aller Container anzeigen |
docker compose logs -f |
Logs aller Dienste live anzeigen |
docker compose logs -f web |
Nur Logs des Web-Containers |
docker compose exec web bash |
Shell im Web-Container öffnen |
docker compose build --no-cache |
Image komplett neu bauen |
docker compose pull |
Neueste Basis-Images herunterladen |
Häufige Shopware CLI-Befehle
Diese Befehle werden alle im laufenden web-Container ausgeführt:
| Befehl | Beschreibung |
|---|---|
docker compose exec web php bin/console cache:clear |
Cache leeren |
docker compose exec web php bin/console theme:compile |
Theme neu kompilieren |
docker compose exec web php bin/console database:migrate --all |
Datenbank-Migrationen ausführen |
docker compose exec web php bin/console user:create admin --admin |
Admin-User erstellen |
docker compose exec web php bin/console system:update:finish |
Shopware-Update abschließen |
docker compose exec web php bin/console list |
Alle verfügbaren Befehle anzeigen |
Backup & Restore
Datenbank sichern
docker compose exec database mariadb-dump -u shopware -p"${MYSQL_PASSWORD}" shopware > backup_$(date +%Y%m%d).sql
Dateien sichern (Media, Theme, Uploads)
mkdir -p /opt/backups && docker run --rm \
-v shopware_files:/data/files \
-v shopware_media:/data/media \
-v shopware_theme:/data/theme \
-v /opt/backups:/backup \
alpine tar cvzf /backup/shopware_files_$(date +%Y%m%d).tar.gz /data
Datenbank wiederherstellen
DATUM im Dateinamen durch das tatsächliche Backup-Datum ersetzen, z. B. 20260301:
docker compose exec -T database mariadb -u shopware -p"${MYSQL_PASSWORD}" shopware < backup_DATUM.sql
Troubleshooting
| Problem | Lösung |
|---|---|
| Container startet nicht | docker compose logs init – zeigt den Fehler |
| Datenbankverbindung schlägt fehl | docker compose ps – ist der database-Container healthy? |
| Permission denied Fehler | docker compose up init-perm – Rechte neu setzen |
| Out of Memory | PHP_MEMORY_LIMIT in compose.yml erhöhen |
| 502 Bad Gateway (Nginx) | Ist der web-Container wirklich gestartet? |
| Langsame Performance | Redis als Cache/Session-Speicher ergänzen |
Security Checklist
Bevor der Shop live geht – diese Punkte abhaken:
-
[ ] Starke, individuelle Passwörter in der
.envgesetzt -
[ ] SSL/TLS mit gültigem Zertifikat aktiv (https://)
-
[ ] Datenbankport 3306 nicht nach außen offen (UFW)
-
[ ] Docker und Images regelmäßig aktualisieren
-
[ ] Regelmäßige Backups eingerichtet
-
[ ] UFW-Firewall aktiv und korrekt konfiguriert
-
[ ]
APP_ENV=prodin der.envgesetzt -
[ ] Standard-Admin-Passwort geändert
-
[ ] SSH-Key-Login funktioniert, Passwort-Login deaktiviert
Weiterführende Links
-
Offizielle Docker-Dokumentation: Docker Image | Shopware Documentation
-
Shopware Environment-Variablen: Environment Variables | Shopware Documentation
-
GitHub: shopware/docker: https://github.com/shopware/docker
-
Shopware Dokumentation: https://docs.shopware.com/
Anhang – Migration (Migrationsassistent)
Dieser Abschnitt ist nur relevant, wenn du Daten aus einem bestehenden Shop (z. B. Shopware 5 oder ein anderes System) in deine neue Shopware 6 Installation importieren möchtest. Der Migrationsassistent ist ein Plugin, das diesen Prozess begleitet.
HINWEIS: Wann brauche ich das? Nur wenn du einen bereits laufenden Shop migrierst – nicht bei einer Neu-Installation ohne bestehende Daten.
Problem: Migrationsassistent startet nicht oder friert ein
Falls der Migrationsassistent im Admin-Backend nicht startet oder eine Migration nicht weiterläuft, können folgende Befehle helfen:
Schritt 1: Feststeckende Migration zwangsweise abbrechen
Falls eine Migration im Status „aborting" feststeckt und nicht weiterläuft: bis Version 15.x
docker exec shopware-database-1 mariadb -u shopware -p'DEIN_MYSQL_PASSWORD' nutzername -e "
UPDATE swag_migration_run
SET step = 'aborted', updated_at = NOW(3)
WHERE step = 'aborting';
SELECT ROW_COUNT() as rows_updated;"
Ab Version 16.x
docker exec shopware-database-1 mariadb -u nutzername -psecure_shopware_password_here shopware -e "
UPDATE swag_migration_run
SET step = 'aborted', updated_at = NOW(3)
WHERE step = 'aborting';"
WARNUNG:
DEIN_MYSQL_PASSWORDdurch den tatsächlichen Wert aus der.env-Datei (MYSQL_PASSWORD) ersetzen!
Schritt 2: Lock-Dateien entfernen
Lock-Dateien können verhindern, dass der Worker-Prozess neu startet:
docker exec shopware-web-1 rm -f \
/tmp/sf.message_queue_consume_async..lock \
/tmp/sf.message_queue_consume_low_priority.*.lock \
&& echo "Lock files removed"
Schritt 3: CLI-Worker manuell starten
Den Hintergrundprozess für die Migration manuell anstoßen:
docker exec shopware-web-1 php bin/console messenger:consume async -vv --time-limit=3600
TIPP: Nach diesen drei Schritten den Migrationsassistenten im Admin-Backend neu aufrufen. Der Worker läuft jetzt im Vordergrund – du siehst die Ausgabe direkt im Terminal.
– Ende der Anleitung –