<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>SSH &#8211; abow &#8211; how do you do IT</title>
	<atom:link href="https://abow.info/tag/ssh/feed/" rel="self" type="application/rss+xml" />
	<link>https://abow.info</link>
	<description>Alle möglichen Handkniffe, die ich mir so zusammentrage im Berufsalltag</description>
	<lastBuildDate>Wed, 03 Jun 2026 17:20:31 +0000</lastBuildDate>
	<language>de</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	

<image>
	<url>https://abow.info/wp-content/uploads/2026/06/cropped-favicon-512-32x32.png</url>
	<title>SSH &#8211; abow &#8211; how do you do IT</title>
	<link>https://abow.info</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>WordPress // Umzug von Strato auf einen Hostinger-VPS (Docker + Traefik)</title>
		<link>https://abow.info/persoenlich/wordpress-umzug-von-strato-auf-einen-hostinger-vps-docker-traefik/</link>
					<comments>https://abow.info/persoenlich/wordpress-umzug-von-strato-auf-einen-hostinger-vps-docker-traefik/#respond</comments>
		
		<dc:creator><![CDATA[Andi Bow]]></dc:creator>
		<pubDate>Fri, 10 Jul 2026 17:14:29 +0000</pubDate>
				<category><![CDATA[Hosting]]></category>
		<category><![CDATA[Persönlich]]></category>
		<category><![CDATA[Wordpress]]></category>
		<category><![CDATA[Bash]]></category>
		<category><![CDATA[DB_HOST]]></category>
		<category><![CDATA[DNS]]></category>
		<category><![CDATA[Docker]]></category>
		<category><![CDATA[docker-compose]]></category>
		<category><![CDATA[Hostinger]]></category>
		<category><![CDATA[Lets Encrypt]]></category>
		<category><![CDATA[MariaDB]]></category>
		<category><![CDATA[Migration]]></category>
		<category><![CDATA[MySQL]]></category>
		<category><![CDATA[Named Volume]]></category>
		<category><![CDATA[Powershell]]></category>
		<category><![CDATA[Reverse Proxy]]></category>
		<category><![CDATA[search-replace]]></category>
		<category><![CDATA[SSH]]></category>
		<category><![CDATA[SSL]]></category>
		<category><![CDATA[Strato]]></category>
		<category><![CDATA[Traefik]]></category>
		<category><![CDATA[Umzug]]></category>
		<category><![CDATA[VPS]]></category>
		<category><![CDATA[WordPress]]></category>
		<category><![CDATA[WORDPRESS_TABLE_PREFIX]]></category>
		<category><![CDATA[wp-config]]></category>
		<guid isPermaLink="false">https://abow.info/?p=499</guid>

					<description><![CDATA[Dieser Beitrag ist ein erprobtes Runbook: ein WordPress von Strato auf einen Hostinger-VPS ziehen, auf dem ein Docker-Compose-Stack (wordpress:latest + mysql:8) läuft und Traefik als Reverse Proxy im…]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">Dieser Beitrag ist ein <strong>erprobtes Runbook</strong>: ein WordPress von <strong>Strato</strong> auf einen <strong>Hostinger-VPS</strong> ziehen, auf dem ein <strong>Docker-Compose-Stack</strong> (<code>wordpress:latest</code> + <code>mysql:8</code>) läuft und <strong>Traefik</strong> als Reverse Proxy im <strong>Host-Mode</strong> das TLS macht. Files (Verzeichnis) und DB-Dump liegen bereits auf dem VPS. Es sind genau die Schritte – inklusive der vier, fünf Stolperfallen, an denen man garantiert hängenbleibt.<br>WordPress wurde zuvor via Hostinger VPS Catalog in einem Docker Container installiert.<br>Die Datenbank und das Verzeichnis bei Strato wurden zuvor via FileZilla heruntergeladen und via phpMyAdmin als .sql gesichert.</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p class="wp-block-paragraph">Die Beispielwerte (<code>wordpress-ibr4-*</code>, <code>jryy_</code>, <code>abow.info</code>, IP <code>72.61.92.170</code>) stammen aus dem realen Umzug. Passe sie an deinen Stack an.</p>
</blockquote>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading">Ziel</h2>



<p class="wp-block-paragraph">WordPress läuft im vorhandenen Docker-Stack, ist über <code>https://abow.info</code> öffentlich erreichbar, Zertifikat von Let&#8217;s Encrypt – ohne Datenverlust.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading">Mentales Modell (vorab verstehen, spart 2 Stunden)</h2>



<ul class="wp-block-list">
<li><strong>Named Volume:</strong> Der WP-Container mountet <code>…/&lt;volume>/_data</code> → <code>/var/www/html</code>. Deine Files müssen in <strong><code>_data</code></strong> liegen, nicht daneben. Und: <code>_data</code> <strong>niemals löschen</strong> – ein Named Volume braucht diesen Ordner, sonst startet der Container nicht.</li>



<li><strong>Die <code>wp-config.php</code> erzeugt das offizielle Image selbst</strong> – aber <strong>nur</strong>, wenn keine existiert, und zwar aus den <code>WORDPRESS_*</code>-ENV-Variablen. Eine mitgebrachte Strato-<code>wp-config.php</code> wird <strong>nicht</strong> überschrieben.</li>



<li><strong>DB-Verbindung geht über den Service-Namen</strong> (<code>db</code>), nie <code>localhost</code>/<code>rdbms.strato.de</code>.</li>



<li><strong>Traefik im Host-Mode</strong> routet <strong>nicht</strong> über das Docker-Netz, sondern über einen <strong>festen veröffentlichten Port</strong> des Containers.</li>
</ul>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading">Schritt 0: Stack inspizieren</h2>



<pre class="wp-block-code"><code>ssh root@DEINE_VPS_IP

docker ps --format "table {{.Names}}\t{{.Image}}\t{{.Ports}}"
</code></pre>



<p class="wp-block-paragraph">Du suchst den WP- und den DB-Container (hier <code>wordpress-ibr4-wordpress-1</code> / <code>wordpress-ibr4-db-1</code>, DB-Image <code>mysql:8</code>) und den Reverse Proxy (<code>traefik-traefik-1</code>). Projektpfad und Volume finden:</p>



<pre class="wp-block-code"><code>docker inspect wordpress-ibr4-wordpress-1 \
  --format '{{ index .Config.Labels "com.docker.compose.project.working_dir" }}'
# -&gt; z.B. /docker/wordpress-ibr4

docker inspect wordpress-ibr4-wordpress-1 \
  --format '{{ range .Mounts }}{{ .Name }} | {{ .Source }} -&gt; {{ .Destination }}{{ println }}{{ end }}'
# -&gt; wordpress-ibr4_wordpress | …/_data -&gt; /var/www/html
</code></pre>



<p class="wp-block-paragraph">DB-Zugangsdaten + Prefix aus den Containern (nur ansehen):</p>



<pre class="wp-block-code"><code>docker exec wordpress-ibr4-wordpress-1 env | grep -i WORDPRESS_DB
docker exec wordpress-ibr4-db-1          env | grep -iE 'MYSQL_DATABASE|MYSQL_USER'
</code></pre>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading">Schritt 1: Files an die richtige Stelle (<code>_data</code>)</h2>



<p class="wp-block-paragraph">Liegen deine Strato-Files in einem Ordner <strong>neben</strong> <code>_data</code> (z. B. <code>_app</code>), sieht WordPress sie nicht. Ist <code>_data</code> nur die nackte Standard-Installation, deine echte Seite aber in <code>_app</code>, machst du <code>_app</code> zum Live-Verzeichnis:</p>



<pre class="wp-block-code"><code>cd /var/lib/docker/volumes/wordpress-ibr4_wordpress
# Falls _data noch die Default-Files enthaelt: sichern statt loeschen
mv _app  _data

# Core vollstaendig? (muessen existieren)
ls _data/wp-admin _data/wp-includes _data/wp-load.php
</code></pre>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p class="wp-block-paragraph"><strong>Falle:</strong> <code>_data</code> zu <strong>löschen</strong> killt den Mountpunkt → <code>Error response from daemon: open …/_data: no such file or directory</code>. Immer <strong>umbenennen</strong>, nie löschen.</p>
</blockquote>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading">Schritt 2: Alte Strato-wp-config raus, ENV-Prefix korrekt setzen</h2>



<p class="wp-block-paragraph">Die mitgebrachte <code>wp-config.php</code> zeigt noch auf Strato:</p>



<pre class="wp-block-code"><code>grep -E "table_prefix|DB_HOST|DB_NAME|DB_USER" _data/wp-config.php
# define('DB_HOST','rdbms.strato.de');  &lt;- muss weg
# $table_prefix = 'jryy_';              &lt;- Prefix merken!
</code></pre>



<p class="wp-block-paragraph"><strong>Prefix merken</strong> (hier <code>jryy_</code>) und die alte Config wegsichern, damit das Image neu generiert:</p>



<pre class="wp-block-code"><code>mv _data/wp-config.php _data/wp-config.php.strato-bak
</code></pre>



<p class="wp-block-paragraph">Jetzt den <strong>Table-Prefix in die Compose</strong> – und zwar im <code>environment:</code>-Block des <strong><code>wordpress</code></strong>-Service (nicht beim <code>db</code>-Service!). Häufigster Fehler: Zeile landet bei den <code>MYSQL_*</code>-Variablen und greift nie.</p>



<pre class="wp-block-code"><code>services:
  wordpress:
    environment:
      - WORDPRESS_DB_HOST=db
      - WORDPRESS_DB_NAME=wordpress
      - WORDPRESS_DB_USER=wordpress
      - WORDPRESS_DB_PASSWORD=${MYSQL_PASSWORD}
      - WORDPRESS_TABLE_PREFIX=jryy_          # &lt;- HIER, beim wordpress-Service
</code></pre>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading">Schritt 3: Container neu erzeugen und Prefix <strong>effektiv</strong> prüfen</h2>



<p class="wp-block-paragraph"><code>docker compose up -d</code> allein startet einen laufenden Container <strong>nicht</strong> neu – dann läuft der Entrypoint nicht und die <code>wp-config.php</code> wird nicht geschrieben. Daher <code>--force-recreate</code>:</p>



<pre class="wp-block-code"><code>cd /docker/wordpress-ibr4
docker compose up -d --force-recreate wordpress
</code></pre>



<p class="wp-block-paragraph">Jetzt <strong>nicht</strong> nur <code>grep</code> (das offizielle Image schreibt <code>getenv_docker('…','wp_')</code> mit Fallback). Den <strong>aufgelösten</strong> Wert prüfen:</p>



<pre class="wp-block-code"><code>docker exec wordpress-ibr4-wordpress-1 \
  php -r 'require "/var/www/html/wp-config.php"; echo DB_HOST.PHP_EOL.DB_NAME.PHP_EOL.$table_prefix.PHP_EOL;'
</code></pre>



<p class="wp-block-paragraph">Zielausgabe:</p>



<pre class="wp-block-code"><code>db
wordpress
jryy_
</code></pre>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p class="wp-block-paragraph">Kommt <code>wp_</code> statt <code>jryy_</code>: Die ENV-Zeile steht im falschen Service-Block oder die alte <code>wp-config.php</code> existiert noch. Beides fixen, <code>--force-recreate</code> wiederholen. Effektiv-Check mit <code>docker exec … env | grep WORDPRESS_TABLE_PREFIX</code> zeigt, ob die Variable im Container überhaupt ankommt.</p>
</blockquote>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading">Schritt 4: Datenbank importieren</h2>



<p class="wp-block-paragraph"><code>docker exec -i … &lt; dump.sql</code> liest die Datei vom <strong>Host</strong> und pipet sie in den Container – der <strong>Pfad im Container ist egal</strong>, nur der Host-Pfad muss stimmen. Passwort kommt aus der Container-ENV, du tippst nichts:</p>



<pre class="wp-block-code"><code>docker exec -i wordpress-ibr4-db-1 \
  sh -c 'exec mysql -uwordpress -p"$MYSQL_PASSWORD" wordpress' \
  &lt; /var/lib/docker/volumes/wordpress-ibr4_wordpress/dbs12659889.sql
</code></pre>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p class="wp-block-paragraph"><strong>Falle <code>ERROR 1050 … Table already exists</code>:</strong> In der DB liegen schon Tabellen (Reste der Default-Installation oder ein abgebrochener Import). Schema einmal komplett leeren, dann neu importieren:</p>



<pre class="wp-block-code"><code>docker exec wordpress-ibr4-db-1 sh -c '
  exec mysql -uwordpress -p"$MYSQL_PASSWORD" -N -e "
    SET FOREIGN_KEY_CHECKS=0; SET GROUP_CONCAT_MAX_LEN=1000000;
    SELECT IFNULL(CONCAT(\"DROP TABLE IF EXISTS \", GROUP_CONCAT(\"\`\",table_name,\"\`\"), \";\"), \"SELECT 1;\")
    FROM information_schema.tables WHERE table_schema=\"wordpress\";
  " wordpress' \
| docker exec -i wordpress-ibr4-db-1 sh -c 'exec mysql -uwordpress -p"$MYSQL_PASSWORD" wordpress'
</code></pre>



<p class="wp-block-paragraph">Danach <code>SHOW TABLES;</code> muss leer sein → Import wiederholen.</p>
</blockquote>



<p class="wp-block-paragraph">Rechte setzen und verifizieren (Prefix beachten – hier <code>jryy_</code>):</p>



<pre class="wp-block-code"><code>docker exec wordpress-ibr4-wordpress-1 chown -R www-data:www-data /var/www/html

docker exec wordpress-ibr4-db-1 \
  sh -c 'exec mysql -uwordpress -p"$MYSQL_PASSWORD" wordpress \
  -e "SELECT option_name,option_value FROM jryy_options WHERE option_name IN (\"siteurl\",\"home\");"'

docker exec wordpress-ibr4-wordpress-1 \
  php -r 'require "/var/www/html/wp-load.php"; echo "WP ".$GLOBALS&#91;"wp_version"]." | ".get_option("blogname").PHP_EOL;'
</code></pre>



<p class="wp-block-paragraph">Erwartung: deine <code>siteurl</code>/<code>home</code> und WP-Version + Blogname.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading">Schritt 5: URLs (nur bei Domain-/Protokollwechsel)</h2>



<p class="wp-block-paragraph">Stehen <code>siteurl</code>/<code>home</code> bereits korrekt (z. B. <code>https://abow.info</code>), ist <strong>nichts</strong> zu tun – diesen Schritt überspringen. Andernfalls <strong>serialisierungssicher</strong> mit WP-CLI ersetzen, nie per rohem SQL:</p>



<pre class="wp-block-code"><code>docker compose exec wordpress bash -c '
  curl -sO https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar
  php wp-cli.phar search-replace "http://alt.tld" "https://abow.info" --all-tables --allow-root --dry-run'
</code></pre>



<p class="wp-block-paragraph">Sieht der Probelauf gut aus → <code>--dry-run</code> weglassen.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading">Schritt 6: HTTPS hinter Traefik</h2>



<p class="wp-block-paragraph">Traefik terminiert TLS und spricht intern per HTTP mit WordPress. Ohne Hinweis baut WordPress Redirect-Loops / Mixed-Content. Per ENV in den <strong>wordpress</strong>-Service (die <code>$$</code> sind Absicht – Compose würde einfaches <code>$</code> als Variable interpretieren):</p>



<pre class="wp-block-code"><code>      - WORDPRESS_CONFIG_EXTRA=if (isset($$_SERVER&#91;'HTTP_X_FORWARDED_PROTO']) &amp;&amp; $$_SERVER&#91;'HTTP_X_FORWARDED_PROTO'] === 'https') { $$_SERVER&#91;'HTTPS'] = 'on'; }
</code></pre>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading">Schritt 7: Traefik-Routing im Host-Mode</h2>



<p class="wp-block-paragraph">Prüfen, wie Traefik tickt:</p>



<pre class="wp-block-code"><code>docker inspect traefik-traefik-1 --format '{{ json .Config.Cmd }}'
# -&gt; providers.docker=true, entrypoints web:80 / websecure:443,
#    HTTP-&gt;HTTPS-Redirect, certresolver "letsencrypt" (HTTP-Challenge)

docker inspect traefik-traefik-1 \
  --format '{{ range $n,$v := .NetworkSettings.Networks }}{{ $n }} {{ end }}'
# -&gt; host   (= Host-Mode!)
</code></pre>



<p class="wp-block-paragraph"><strong>Host-Mode-Konsequenzen</strong> (das ist die größte Falle):</p>



<ul class="wp-block-list">
<li><code>docker network connect</code> ist <strong>nicht möglich</strong> (Container im Host-Namespace).</li>



<li>Traefik erreicht den WP-Container nur über einen <strong>festen, veröffentlichten Port</strong> auf <code>127.0.0.1</code>.</li>



<li>Im Label statt <code>loadbalancer.server.port</code> → <strong><code>loadbalancer.server.url</code></strong> mit der festen URL.</li>
</ul>



<p class="wp-block-paragraph">Daher in der Compose den WP-Port <strong>fix</strong> mappen (nicht dynamisch <code>"80"</code>, das vergibt Docker zufällig):</p>



<pre class="wp-block-code"><code>    ports:
      - "127.0.0.1:8081:80"
</code></pre>



<p class="wp-block-paragraph">Und die <strong>Labels</strong> auf <strong>Service-Ebene</strong> (gleiche Einrückung wie <code>environment:</code>/<code>volumes:</code>, nur <strong>einmal</strong> <code>labels:</code> pro Service!):</p>



<pre class="wp-block-code"><code>    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.abow.rule=Host(`abow.info`)"
      - "traefik.http.routers.abow.entrypoints=websecure"
      - "traefik.http.routers.abow.tls=true"
      - "traefik.http.routers.abow.tls.certresolver=letsencrypt"
      - "traefik.http.services.abow.loadbalancer.server.url=http://127.0.0.1:8081"
</code></pre>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p class="wp-block-paragraph"><strong>YAML-Fallen:</strong> <code>labels:</code> darf nicht unter <code>volumes:</code> verschachtelt sein und nicht doppelt vorkommen (<code>mapping key "labels" already defined</code>). Nach jeder Änderung validieren:</p>



<pre class="wp-block-code"><code>docker compose config &gt;/dev/null &amp;&amp; echo "YAML OK" || echo "YAML kaputt"
</code></pre>
</blockquote>



<p class="wp-block-paragraph">Anwenden und <strong>intern</strong> testen (ohne auf DNS/Zertifikat zu warten):</p>



<pre class="wp-block-code"><code>docker compose up -d wordpress

curl -s -I http://127.0.0.1:8081/ | head -1            # WordPress direkt: 200/301
curl -s -I -H "Host: abow.info" http://127.0.0.1/ | head -1   # ueber Traefik
</code></pre>



<p class="wp-block-paragraph"><code>HTTP/1.1 308 Permanent Redirect</code> beim zweiten Befehl = <strong>Traefik kennt den <code>abow</code>-Router</strong> und leitet auf HTTPS um. Routing steht.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading">Schritt 8: DNS umstellen → Zertifikat kommt automatisch</h2>



<p class="wp-block-paragraph">Das Let&#8217;s-Encrypt-Zertifikat (HTTP-Challenge) zieht <strong>erst</strong>, wenn die Domain öffentlich auf den VPS zeigt. Beim DNS-Anbieter für <code>abow.info</code>:</p>



<figure class="wp-block-table"><table class="has-fixed-layout"><thead><tr><th>Typ</th><th>Name</th><th>Wert</th></tr></thead><tbody><tr><td>A</td><td><code>@</code></td><td><code>72.61.92.170</code> (VPS-IPv4, via <code>curl -s -4 ifconfig.me</code>)</td></tr><tr><td>AAAA</td><td><code>@</code></td><td>deine VPS-IPv6 (oder AAAA entfernen)</td></tr></tbody></table></figure>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p class="wp-block-paragraph">Vorher testen ohne DNS-Wartezeit: per Windows-<code>hosts</code>-Datei <code>abow.info</code> → VPS-IP (PowerShell-Skript unten). Browser-Zertifikatswarnung ist dabei normal, bis das echte Zert gezogen ist.</p>
</blockquote>



<p class="wp-block-paragraph">Nach Propagation prüfen:</p>



<pre class="wp-block-code"><code>dig +short abow.info                                   # -&gt; 72.61.92.170
curl -sI https://abow.info | head -3                   # -&gt; HTTP/2 200
echo | openssl s_client -connect abow.info:443 -servername abow.info 2&gt;/dev/null \
  | openssl x509 -noout -issuer -subject -dates        # Issuer: Let's Encrypt
</code></pre>



<p class="wp-block-paragraph"><code>HTTP/2 200</code> ohne TLS-Fehler = <strong>live mit gültigem Zertifikat.</strong></p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading">Schritt 9: Nacharbeiten &amp; Aufräumen</h2>



<pre class="wp-block-code"><code># In WordPress: Einstellungen -&gt; Permalinks -&gt; ohne Aenderung speichern (Rewrites/.htaccess)

# Backups/Reste entfernen
rm -rf /var/lib/docker/volumes/wordpress-ibr4_wordpress/_data_OLD
rm -f  /var/lib/docker/volumes/wordpress-ibr4_wordpress/_data/wp-config.php.*-bak*
rm -f  /docker/wordpress-ibr4/docker-compose.yml.bak*
</code></pre>



<ul class="wp-block-list">
<li><strong>DB-Passwort rotieren</strong>, falls es irgendwo im Klartext aufgetaucht ist.</li>



<li>Strato-Dump nach erfolgreicher Kontrolle sichern/löschen.</li>
</ul>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading">PowerShell: Vorab-Test per hosts-Datei</h2>



<pre class="wp-block-code"><code>&lt;#
.NOTES
    Name:        PS_Hosts_Preview_VPS
    Author:      Andreas Bowitz
    Version:     0.2
    LastUpdated: 2026-Jun-03

.SYNOPSIS
    Setzt temporär einen hosts-Eintrag, um eine Domain vor der DNS-Umstellung
    auf die neue VPS-IP zeigen zu lassen (lokaler Test vom Windows-Client).
#&gt;

$VpsIP  = "72.61.92.170"
$Domain = "abow.info"

$HostsFile = "$env:WINDIR\System32\drivers\etc\hosts"
$Entry     = "$VpsIP`t$Domain"

if (-not (&#91;Security.Principal.WindowsPrincipal] &#91;Security.Principal.WindowsIdentity]::GetCurrent()
    ).IsInRole(&#91;Security.Principal.WindowsBuiltInRole]::Administrator)) {
    Write-Warning "Bitte PowerShell als Administrator starten."; return
}
if ((Get-Content $HostsFile) -match &#91;Regex]::Escape($Domain)) {
    Write-Host "Eintrag fuer '$Domain' existiert bereits." -ForegroundColor Yellow
} else {
    Add-Content -Path $HostsFile -Value $Entry
    Write-Host "Hinzugefuegt: $Entry" -ForegroundColor Green
}
ipconfig /flushdns | Out-Null
Write-Host "Test: https://$Domain  (Zert-Warnung bis echtem DNS normal). Danach Zeile wieder entfernen." -ForegroundColor Cyan
</code></pre>



<h2 class="wp-block-heading">Bash: Import-Helfer (auf dem VPS)</h2>



<pre class="wp-block-code"><code>#!/usr/bin/env bash
# .NOTES
#   Name:        migrate_strato_to_docker
#   Author:      Andreas Bowitz
#   Version:     0.2
#   LastUpdated: 2026-Jun-03
#   Zweck:       DB-Dump in den db-Container importieren + Rechte setzen.
#                Im Compose-Projektverzeichnis ausfuehren.
set -euo pipefail

DB_CONTAINER="wordpress-ibr4-db-1"
WP_CONTAINER="wordpress-ibr4-wordpress-1"
DB_NAME="wordpress"
PREFIX="jryy_"
DUMP="${1:?Pfad zum SQL-Dump angeben}"

echo "&gt;&gt; Pruefe, ob Tabellen existieren ..."
COUNT=$(docker exec "$DB_CONTAINER" sh -c \
  "exec mysql -uwordpress -p\"\$MYSQL_PASSWORD\" -N -e \
   'SELECT COUNT(*) FROM information_schema.tables WHERE table_schema=\"$DB_NAME\";'")
if &#91; "$COUNT" -gt 0 ]; then
  echo "!! $COUNT Tabellen vorhanden. Erst leeren (manuell), dann erneut starten." ; exit 1
fi

echo "&gt;&gt; Importiere '$DUMP' ..."
docker exec -i "$DB_CONTAINER" \
  sh -c 'exec mysql -uwordpress -p"$MYSQL_PASSWORD" '"$DB_NAME" &lt; "$DUMP"

echo "&gt;&gt; Rechte setzen ..."
docker exec "$WP_CONTAINER" chown -R www-data:www-data /var/www/html

echo "&gt;&gt; Kontrolle ..."
docker exec "$DB_CONTAINER" sh -c \
  "exec mysql -uwordpress -p\"\$MYSQL_PASSWORD\" $DB_NAME \
   -e 'SELECT option_value FROM ${PREFIX}options WHERE option_name=\"siteurl\";'"
echo "&gt;&gt; Fertig."
</code></pre>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading">Troubleshooting (gesammelte Stolperfallen)</h2>



<figure class="wp-block-table"><table class="has-fixed-layout"><thead><tr><th>Symptom</th><th>Ursache</th><th>Lösung</th></tr></thead><tbody><tr><td><code>open …/_data: no such file or directory</code></td><td><code>_data</code> (Mountpunkt des Named Volume) gelöscht</td><td>Volume-Ordner wiederherstellen, Files in <code>_data</code> legen – nie löschen</td></tr><tr><td><code>php</code>-Check zeigt <code>wp_</code> statt <code>jryy_</code></td><td><code>WORDPRESS_TABLE_PREFIX</code> im falschen Service-Block oder alte <code>wp-config.php</code> da</td><td>ENV beim <code>wordpress</code>-Service, alte Config umbenennen, <code>--force-recreate</code></td></tr><tr><td><code>wp-config.php</code> wird nicht neu geschrieben</td><td><code>docker compose up -d</code> recreatet laufenden Container nicht</td><td><code>docker compose up -d --force-recreate wordpress</code></td></tr><tr><td><code>ERROR 1050 … already exists</code></td><td>Tabellen schon vorhanden</td><td>Schema leeren (DROP-Loop), dann neu importieren</td></tr><tr><td><code>Error establishing a database connection</code></td><td><code>DB_HOST</code> = <code>localhost</code>/<code>rdbms.strato.de</code></td><td>auf Service-Namen <code>db</code> setzen</td></tr><tr><td><code>mapping key "labels" already defined</code> / <code>mapping values not allowed</code></td><td><code>labels:</code> doppelt oder falsch eingerückt</td><td>nur <strong>ein</strong> <code>labels:</code> auf Service-Ebene; <code>docker compose config</code> validieren</td></tr><tr><td>Traefik routet nicht / 404</td><td>Host-Mode + dynamischer Port / <code>server.port</code> statt <code>server.url</code></td><td>fixes <code>127.0.0.1:8081:80</code>, Label <code>loadbalancer.server.url=http://127.0.0.1:8081</code></td></tr><tr><td>Kein Zertifikat</td><td>HTTP-Challenge scheitert, DNS zeigt noch auf Strato</td><td>A/AAAA auf VPS-IP, dann zieht Traefik automatisch</td></tr><tr><td>Redirect-Loop / Mixed Content</td><td>HTTPS hinter Proxy nicht erkannt</td><td><code>WORDPRESS_CONFIG_EXTRA</code> mit <code>HTTP_X_FORWARDED_PROTO</code></td></tr><tr><td>404 auf Unterseiten</td><td>Permalinks/Rewrite</td><td>Permalinks ohne Änderung neu speichern</td></tr></tbody></table></figure>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<p class="wp-block-paragraph"><strong>Fazit:</strong> Auf einem Docker-VPS verlagert sich der Umzug komplett in die Shell. Die eigentliche Arbeit sind nicht die Standard-Schritte (Dump rein, Files rein), sondern die <strong>Eigenheiten der Umgebung</strong>: Named Volume = <code>_data</code> (nie löschen), <code>WORDPRESS_TABLE_PREFIX</code> beim <strong>richtigen</strong> Service plus <code>--force-recreate</code>, Effektiv-Prüfung mit <code>php -r</code> statt <code>grep</code>, und Traefik im <strong>Host-Mode</strong> mit <strong>festem Port</strong> und <code>server.url</code>. Wer diese vier Punkte kennt, zieht beim nächsten Mal in 20 Minuten um – ohne Hilfe.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://abow.info/persoenlich/wordpress-umzug-von-strato-auf-einen-hostinger-vps-docker-traefik/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>
