Komplette Installationsanleitung für Anfänger Shopware 6 mit Docker

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.

:information_source: 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 up oder 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

:light_bulb: TIPP: Du wirst nach einem Passwort für nutzername gefragt. 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.

:warning: 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

:light_bulb: 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

:information_source: HINWEIS: Der Befehl yes | sudo ufw enable bestätigt die Aktivierung automatisch. Danach zeigt ufw status verbose die aktiven Regeln an – dort sollten die drei Ports 2222, 80 und 443 erscheinen.

:warning: 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

:information_source: 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

:information_source: HINWEIS: Der letzte Befehl (usermod) gibt nutzername die Berechtigung, Docker ohne sudo zu 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

:light_bulb: 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

:light_bulb: TIPP: Wenn alle vier Befehle ohne Fehlermeldung durchlaufen, ist die Basis fertig. Du siehst dann z. B.: Docker version 27.x.x und Docker 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.

:information_source: 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

:information_source: HINWEIS: su - nutzername wechselt in den nutzername-Account. Wenn du fertig bist und zurück willst, tippe exit. 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.

:warning: WARNUNG: WICHTIG: INSTALL_LOCALE legt 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 mit docker compose up startest.

# 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."

:warning: WARNUNG: Öffne die .env jetzt mit einem Texteditor (z. B. nano .env) und ersetze alle Platzhalter (sicheres_..._passwort_hier) durch echte, starke Passwörter. Tipp: openssl rand -hex 16 generiert ein sicheres Zufallspasswort. Diese Datei darf niemals in ein Git-Repository eingecheckt werden! Diese Datei darf niemals veröffentlicht werden!

:light_bulb: TIPP: Was ist APP_URL? Das ist die Adresse, unter der dein Shop erreichbar sein soll. Hast du noch keine Domain, trage zunächst http://deine.IP.vom.VPS:8000 ein. Sobald eine Domain vorhanden ist und Nginx mit SSL läuft (Schritt 6), änderst du sie auf https://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

:information_source: HINWEIS: --no-cache sorgt 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 (oder https://deine-domain.de nach Schritt 6)

  • Admin-Backend: http://deine.IP.vom.VPS:8000/admin

:light_bulb: TIPP: Falls der init-Container mit einem Fehler abbricht (Exit-Code 1): docker compose logs init zeigt dir die genaue Fehlermeldung. Häufige Ursache: ein Fehler in der .env-Datei (z. B. ein fehlendes Passwort oder falsche APP_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).

:information_source: 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.

:warning: 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

:information_source: 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 .env gesetzt

  • [ ] 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=prod in der .env gesetzt

  • [ ] Standard-Admin-Passwort geändert

  • [ ] SSH-Key-Login funktioniert, Passwort-Login deaktiviert

Weiterführende Links


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.

:information_source: 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';"

:warning: WARNUNG: DEIN_MYSQL_PASSWORD durch 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

:light_bulb: 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 –

2 „Gefällt mir“

Das ist eine ganz wundervolle Anleitung. Danke dafür @robertschoenfeld_1 !

Ich habe dazu ein paar kleine Anmerkungen:

  • Die ganz oben beschriebenen Serverparameter gibt’s im ganz kleinen Paket, oder? Ich bin mir nicht sicher, ob das wirklich dem Produktivumfeld standhält - kommt natürlich auch auf den Traffic und die Shopkonfiguration an (Anzahl Artikel, Kategorien etc). Das größte Problem ist dabei, dass die genannten Serverparameter bei verschiedenen Anbietern nicht fix sind und garantiert werden können, je nachdem, was sonst noch auf dem “Blech” passiert.
  • Wenn wir über den gleichen Anbieter sprechen, kann die Firewall durchaus auch über konsoleh aktiviert werden :wink:
  • Man könnte soweit gehen, die Anmeldung nur noch per SSH-Key zuzulassen. Dazu muss in /etc/ssh/sshd_config der Parameter PasswordAuthentication auf “no” gesetzt werden.
  • Last but not least: Mit dieser Servereinrichtung können natürlich auch andere Systeme mittels composer aufgesetzt werden.

Ich werfe diese Anleitung mal mit in https://hub.shopware.com :wink:

1 „Gefällt mir“

Gerne, freue mich für jeden, der dadurch Zeit sparen kann.

Welche anderen Systeme meinst du?

Ich würde mich noch freuen, wenn irgendjemand eine detaillierte Anleitung für eine Migration durchführt von einem zum anderen oder eine 1:1 Kopie macht.

Am besten von einer Shopware 6 Installation ohne Docker zu einer mit Docker. Und am liebsten wenn beide 6.7.8 haben :smiley:

Mit der Anleitung kann man jeden X-beliebigen Docker-Container auf dem Zielsystem hochfahren. Ich habe das auf dem gleichen Server mit Stirling PDF, DocuSeal und SnipeIT gemacht. Das geht sicher genauso mit Typo, Drupal, WP, Contao or whatever :wink: