Om du någonsin har slösat bort en halv dag på grund av att "det fungerar på min maskin" men inte på en kollegas, är Docker ofta brytpunkten. Den verkliga fördelen är inte att "ha hood.discount "I en behållare", det handlar om att frysa toutes beroenden (PHP, tillägg, databas, Redis, e-post, verktyg) och möjligheten att återskapa miljön identiskt.

Vad vi ska bygga

Du ska sätta upp en utvecklingsmiljö hood.discount reproducerbar, designad för WordPress 6.9.4 (april 2026) och PHP 8.3 + (Minsta rekommenderade PHP-version: 8.1). Målet är att få en "nästan produktionsmässig" men utvecklingsorienterad stack: Xdebug, WP-CLI, läsbara loggar, e-postinsamling, cache Redis är valfritt, och det finns en automatiserad bootstrap.

Denna uppställning är lämplig för:

  • Visa upp webbplatser och bloggar med anpassade teman (eller barnteman),
  • Webbplatsbyggare (Divi 5, Elementor, Avada) där du vill isolera plugins och versioner,
  • Byråer som snabbt behöver introducera en ny utvecklare eller reproducera en klientbugg.

Till slut kommer du att veta:

  • Starta WordPress 6.9.4 med ett enda kommando,
  • Installera WordPress, plugins och grundläggande data automatiskt.
  • felsöka korrekt (Xdebug + loggar),
  • fånga utgående e-postmeddelanden utan att röra en riktig SMTP-server,
  • Versionshantering av konfigurationen sker utan att versionshantering av hemligheterna utförs.

Snabb sammanfattning

  • Vi börjar med ett Git-projekt med .env (oversionerad) + .env.example (versionerad).
  • Vi använder hamnare komponera med tjänster: wp, db, redis, mailpit, traefik (alternativ).
  • Vi bygger en bild wp-dev baserat på wordpress:php8.3-apache + Xdebug + WP-CLI.
  • Vi automatiserar installationen via WP-CLI (idempotent) och vi håller uppladdningarna i volym.
  • Vi tvingar fram en stabil loggnings- och felsökningsstrategi: WP_DEBUG_LOG, Apache-loggar, Xdebug i ”trigger”.

När ska man använda denna lösning

Jag rekommenderar det om du har minst ett av dessa behov:

  • Återskapa en klientbugg du fryser PHP/tillägg, MySQL/MariaDB-versioner och du undviker "osynliga skillnader".
  • Arbeta i ett team samma stack för alla, inklusive CI.
  • Underhålla flera projekt Varje projekt inkluderar sin egen stack, utan att förorena din maskin.
  • Testa tunga plugins (byggare, WooCommerce) utan att bryta din lokala miljö.

Enligt min erfarenhet är detta särskilt användbart på Elementor/Avada-webbplatser där ett saknat PHP-tillägg (intl, zip, gd) "bara" kraschar en mallimport, utan något användbart meddelande.

När man INTE ska använda denna lösning

  • Om du använder en mycket begränsad maskin (RAM/CPU) och ditt projekt är litet: Docker kan vara långsammare än lokal PHP.
  • Om du bara gör innehåll (ingen utveckling): är hanterad staging ofta enklare.
  • Om ditt team saknar disciplinen att versionera korrekt .env.example och skripten: du ska skapa en "Docker som bara fungerar för en person", vilket är sämre än tidigare.

Innan du börjar (förutsättningar)

Förbered dig som om du ska ha sönder något, för du kommer att ha sönder något.

Tekniska förutsättningar

  • Docker Desktop (macOS/Windows) eller Docker Engine (Linux) + Compose-pluginet. Dokumentation: Docker komponera.
  • git.
  • En editor som hanterar radavslutningar väl (till exempel VS Code).

Riktade versioner

  • WordPress: 6.9.4 (Du kan ändra versionen senare, men vi börjar med den här).
  • PHP: 8.3 i bilden (kompatibel med WP 6.9.4 och över minimum 8.1).
  • MariaDB: 10.11 LTS (bra kompromiss mellan stabilitet och kompatibilitet).

Säkerhetskopiering och miljö

  • Testa inte detta på en aktiv webbplats. Gör det i ett dedikerat arkiv.
  • Om du migrerar en befintlig webbplats: exportera DB + wp-content/uploads före starten.

Säkerhet (lokal men inte "riskfri")

  • Bind aldrig .env (DB-lösenord, salter, nycklar).
  • Undvik att exponera MySQL/Redis på 0.0.0.0 om det inte är nödvändigt.

Användbara officiella referenser:


Steg 1: Skapa en ren och versionsbar projektstruktur

Mål: att separera det som är versionerad (konfiguration, skript) av vad som inte är (hemligheter, volymer, uppladdningar).

1) Skapa trädstrukturen

I en tom mapp:

mkdir -p wp-docker/{docker,bin,wp,wp-content,logs}
cd wp-docker

Du kommer att få:

  • docker/ Dockerfile, Apache/PHP-konfiguration, inmatningsskript
  • bin/ : verktygsskript (installera, återställa, importera databas, etc.)
  • wp/ WordPress-kärna (uppskalad) eller docroot beroende på varianten
  • wp-content/ Versionsbaserade teman/plugins mu-plugins (valfritt)
  • logs/ beständiga loggar

2) Lägg till en strikt .gitignore

Skapa .gitignore vid roten:

cat > .gitignore <<'EOF'
# Secrets
.env

# Volumes / données locales
data/
logs/*.log
wp/wp-config.php

# Dépendances éventuelles
node_modules/
vendor/

# OS / IDE
.DS_Store
.idea/
.vscode/
EOF

3) Skapa dina miljöfiler

Skapa .env.example (versionerad):

cat > .env.example <<'EOF'
# Domaine local (utilisé par WP_HOME/WP_SITEURL)
WP_HOST=wp.local

# WordPress
WP_VERSION=6.9.4
WP_ENV=development

# Base de données
DB_NAME=wordpress
DB_USER=wordpress
DB_PASSWORD=wordpress
DB_ROOT_PASSWORD=root
DB_HOST=db

# Ports
HTTP_PORT=8080
MAILPIT_PORT=8025

# Xdebug
XDEBUG_MODE=debug,develop
XDEBUG_TRIGGER=1
EOF

Kopiera sedan in den i .env (oversionerad):

cp .env.example .env

Förväntat resultat

Du har en deposition redo att mottas docker-compose.yml utan dedikerade hemligheter. Det är här många gör fel: Jag har ofta sett databaslösenord i Git "för att det är lokalt". Den dagen ett arkiv blir offentligt av misstag är det för sent.


Steg 2: Skriv en reproducerbar docker-compose (PHP 8.3+, MariaDB, Redis)

Mål: att deklarera en stabil stack, med namngivna volymer och explicita portar.

1) Skapa docker-compose.yml

Skapa vid roten docker-compose.yml :

cat > docker-compose.yml <<'EOF'
services:
  wp:
    build:
      context: .
      dockerfile: docker/Dockerfile
    container_name: wp_app
    env_file: .env
    depends_on:
      db:
        condition: service_healthy
      redis:
        condition: service_started
      mailpit:
        condition: service_started
    ports:
      - "${HTTP_PORT:-8080}:80"
    volumes:
      # Code WordPress (core) + contenu
      - ./wp:/var/www/html
      - ./wp-content:/var/www/html/wp-content
      # Logs persistants
      - ./logs:/var/log/wp
    extra_hosts:
      # Permet à Xdebug de joindre l'hôte sur Linux (optionnel sur Mac/Windows)
      - "host.docker.internal:host-gateway"

  db:
    image: mariadb:10.11
    container_name: wp_db
    environment:
      MYSQL_DATABASE: ${DB_NAME:-wordpress}
      MYSQL_USER: ${DB_USER:-wordpress}
      MYSQL_PASSWORD: ${DB_PASSWORD:-wordpress}
      MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASSWORD:-root}
    volumes:
      - db_data:/var/lib/mysql
    healthcheck:
      test: ["CMD", "mariadb-admin", "ping", "-h", "127.0.0.1", "-uroot", "-p${DB_ROOT_PASSWORD}"]
      interval: 5s
      timeout: 3s
      retries: 30

  redis:
    image: redis:7-alpine
    container_name: wp_redis
    command: ["redis-server", "--appendonly", "yes"]
    volumes:
      - redis_data:/data

  mailpit:
    image: axllent/mailpit:latest
    container_name: wp_mailpit
    ports:
      - "${MAILPIT_PORT:-8025}:8025"

volumes:
  db_data:
  redis_data:
EOF

2) Varför dessa val

  • Volymer namngivna (db_data, redis_data): Du undviker versionshantering av data och du kan enkelt återställa.
  • Hälsokontroll-databasen Utan detta kan WP-CLI försöka installera innan MariaDB är redo (klassiskt kapplöpningsförhållande).
  • Brevlåda Du kan se utgående e-postmeddelanden utan SMTP. Mycket användbart för formulär.

Förväntat resultat

Vid denna punkt, docker compose up kommer att misslyckas igen eftersom bilden wp Den existerar inte. Det är normalt: vi bygger den i nästa steg.


Steg 3: Bygg en WordPress "dev"-avbildning (Xdebug, WP-CLI, plugins)

Målet är att undvika att förlita sig på en "vanilla WordPress"-container när man behöver verktyg (WP-CLI, Xdebug, zip, intl, etc.). Jag föredrar en dedikerad avbildning eftersom man annars måste mixtra manuellt i containern, vilket inte längre går att reproducera.

1) Skapa docker/Dockerfile

cat > docker/Dockerfile <<'EOF'
FROM wordpress:php8.3-apache

# Paquets système utiles en dev (zip, intl, mysql client, etc.)
RUN apt-get update && apt-get install -y --no-install-recommends 
    git 
    unzip 
    less 
    mariadb-client 
    libzip-dev 
    libicu-dev 
  && docker-php-ext-install zip intl 
  && rm -rf /var/lib/apt/lists/*

# Activer mod_rewrite (permalinks)
RUN a2enmod rewrite headers

# Xdebug (install via PECL)
RUN pecl install xdebug 
  && docker-php-ext-enable xdebug

# WP-CLI (binaire officiel)
RUN curl -sSLo /usr/local/bin/wp https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar 
  && chmod +x /usr/local/bin/wp 
  && wp --info

# Configuration PHP (logs, limites)
COPY docker/php.ini /usr/local/etc/php/conf.d/99-wp-dev.ini

# Configuration Apache (DocumentRoot, logs)
COPY docker/apache.conf /etc/apache2/sites-available/000-default.conf

# Script d'entrée (bootstrap idempotent)
COPY docker/entrypoint.sh /usr/local/bin/wp-entrypoint
RUN chmod +x /usr/local/bin/wp-entrypoint

ENTRYPOINT ["wp-entrypoint"]
CMD ["apache2-foreground"]
EOF

2) Lägg till docker/php.ini

cat > docker/php.ini <<'EOF'
; Logs PHP vers stderr + fichier (pratique en debug)
log_errors = On
error_reporting = E_ALL
display_errors = Off

; Ajustez selon vos besoins
memory_limit = 512M
upload_max_filesize = 128M
post_max_size = 128M
max_execution_time = 120

; Xdebug en mode "trigger" pour éviter de ralentir tout le monde
xdebug.mode = ${XDEBUG_MODE}
xdebug.start_with_request = trigger
xdebug.trigger_value = ${XDEBUG_TRIGGER}

; Sur Mac/Windows: host.docker.internal marche.
; Sur Linux: extra_hosts dans compose.
xdebug.client_host = host.docker.internal
xdebug.client_port = 9003
xdebug.log_level = 0
EOF

3) Lägg till docker/apache.conf

cat > docker/apache.conf <<'EOF'
<VirtualHost *:80>
  ServerAdmin webmaster@localhost
  DocumentRoot /var/www/html

  <Directory /var/www/html>
    AllowOverride All
    Require all granted
  </Directory>

  ErrorLog /var/log/wp/apache-error.log
  CustomLog /var/log/wp/apache-access.log combined
</VirtualHost>
EOF

4) Lägg till docker/entrypoint.sh

Det här skriptet gör tre saker: väntar på databasen, laddar ner WP om den inte finns där och genererar en... wp-config.php rengöra (med salter och felsökning).

cat > docker/entrypoint.sh <<'EOF'
#!/usr/bin/env bash
set -euo pipefail

# Petit helper de logs
log() {
  echo "[wp-entrypoint] $*"
}

# Attendre MariaDB (évite la course au démarrage)
wait_for_db() {
  log "Attente de la base de données ${DB_HOST:-db}..."
  for i in {1..60}; do
    if mariadb-admin ping -h"${DB_HOST:-db}" -u"${DB_USER:-wordpress}" -p"${DB_PASSWORD:-wordpress}" --silent; then
      log "Base de données prête."
      return 0
    fi
    sleep 2
  done
  log "ERREUR: la base n'est pas prête après 120s."
  return 1
}

# Télécharger WordPress si /var/www/html est vide (ou sans wp-includes)
ensure_wordpress_core() {
  if [ ! -d /var/www/html/wp-includes ]; then
    log "WordPress core absent, téléchargement de WP ${WP_VERSION:-6.9.4}..."
    wp core download --version="${WP_VERSION:-6.9.4}" --path=/var/www/html --allow-root
  else
    log "WordPress core déjà présent."
  fi
}

# Générer wp-config.php si absent
ensure_wp_config() {
  if [ ! -f /var/www/html/wp-config.php ]; then
    log "Génération de wp-config.php..."
    wp config create 
      --path=/var/www/html 
      --dbname="${DB_NAME:-wordpress}" 
      --dbuser="${DB_USER:-wordpress}" 
      --dbpass="${DB_PASSWORD:-wordpress}" 
      --dbhost="${DB_HOST:-db}" 
      --skip-check 
      --allow-root

    # Ajouter des constantes utiles en dev (et Redis en option)
    wp config set WP_ENVIRONMENT_TYPE "${WP_ENV:-development}" --type=constant --allow-root
    wp config set WP_DEBUG true --type=constant --allow-root
    wp config set WP_DEBUG_LOG true --type=constant --allow-root
    wp config set WP_DEBUG_DISPLAY false --type=constant --allow-root
    wp config set SCRIPT_DEBUG true --type=constant --allow-root

    # Logs WordPress vers un fichier monté
    wp config set WP_CONTENT_DIR "/var/www/html/wp-content" --type=constant --allow-root
    wp config set WP_CONTENT_URL "http://localhost/wp-content" --type=constant --allow-root

    # Redis (si plugin présent plus tard)
    wp config set WP_REDIS_HOST "redis" --type=constant --allow-root
    wp config set WP_REDIS_PORT 6379 --type=constant --allow-root
  else
    log "wp-config.php déjà présent."
  fi
}

wait_for_db
ensure_wordpress_core
ensure_wp_config

exec "$@"
EOF

Förväntat resultat

Bilden är klar. Du har en enda, versionsbar bootstrap-punkt, och du undviker den klassiska fällan "Jag installerade WP-CLI manuellt i min container, men ingen annan har det".


Steg 4: Installera WordPress 6.9.4 och lås konfigurationen

Mål: att starta containrarna och sedan utföra WordPress-installationen på ett kontrollerat (och reproducerbart) sätt.

1) Starta stacken

docker compose up -d --build

Kontrollera:

2) Installera WordPress via WP-CLI

Utföra:

docker compose exec wp wp core install 
  --url="http://localhost:8080" 
  --title="WP Docker Dev" 
  --admin_user="admin" 
  --admin_password="admin" 
  --admin_email="[email protected]" 
  --skip-email 
  --allow-root

Ja, lösenordet är avsiktligt svagt: detta är en lokal miljö. Kopiera inte detta mönster i en offentlig staging-miljö.

3) Justera permalänkarna

Utan detta kommer du att tro att mod_rewrite är trasig:

docker compose exec wp wp rewrite structure '/%postname%/' --hard --allow-root

Förväntat resultat

Du kan ansluta till /wp-adminSkapa en artikel, så fungerar de "fina" webbadresserna.


Steg 5: Automatisera installation och data (WP-CLI, skript)

Mål: ett kommando = miljöklar. Det är här reproducerbarhet blir verklighet.

1) Skapa ett skript bin/install.sh

cat > bin/install.sh <<'EOF'
#!/usr/bin/env bash
set -euo pipefail

# Script d'installation idempotent pour WordPress en Docker
# Usage: ./bin/install.sh

HTTP_PORT="${HTTP_PORT:-8080}"

docker compose up -d --build

# Attendre que WP réponde (évite les installs trop rapides)
echo "[install] Attente HTTP..."
for i in {1..60}; do
  if curl -sSf "http://localhost:${HTTP_PORT}/wp-login.php" > /dev/null; then
    break
  fi
  sleep 2
done

# Installer WP si pas déjà installé
if docker compose exec -T wp wp core is-installed --allow-root > /dev/null 2>&1; then
  echo "[install] WordPress déjà installé."
else
  echo "[install] Installation WordPress..."
  docker compose exec -T wp wp core install 
    --url="http://localhost:${HTTP_PORT}" 
    --title="WP Docker Dev" 
    --admin_user="admin" 
    --admin_password="admin" 
    --admin_email="[email protected]" 
    --skip-email 
    --allow-root

  docker compose exec -T wp wp rewrite structure '/%postname%/' --hard --allow-root
fi

# Installer des plugins utiles en dev
echo "[install] Installation plugins dev..."
docker compose exec -T wp wp plugin install query-monitor --activate --allow-root

# Redis cache (optionnel) : on l'installe mais on ne l'active pas si vous ne voulez pas
docker compose exec -T wp wp plugin install redis-cache --allow-root || true

# Réglages de base
docker compose exec -T wp wp option update blogdescription "Environnement reproductible Docker" --allow-root

echo "[install] OK. WP: http://localhost:${HTTP_PORT}  Admin: admin/admin"
EOF

chmod +x bin/install.sh

2) Starta bootstrap-programmet

./bin/install.sh

3) Varför det är bättre än en README-fil

En README-fil ligger alltid kvar någonstans. Ett skript, å andra sidan, slutar fungera omedelbart om ett steg saknas. Och när det slutar fungera vet du var.

Förväntat resultat

Du kan radera dina volymer, starta om ./bin/install.shoch återställa en fungerande WP, med Query Monitor aktiverad.


Steg 6: Felsökning och loggar (WP_DEBUG, Xdebug, Query Monitor, error_log)

Mål: att synliggöra buggar, utan att sakta ner hela stacken.

1) Var man ska läsa loggarna

  • Apache-loggar: logs/apache-error.log et logs/apache-access.log
  • WordPress-logg: som standard wp-content/debug.log (bil WP_DEBUG_LOG är kl true)

Bekväm beställning:

tail -f logs/apache-error.log wp-content/debug.log

2) Xdebug utan att sakta ner allt

Installationen använder xdebug.start_with_request=triggerKonkret uttryckt:

  • Utan trigger: Xdebug aktiveras inte, prestandan är korrekt.
  • Med trigger: du aktiverar felsökning bara när du behöver det.

Exempel: lägg till en parameter ?XDEBUG_TRIGGER=1 till en URL, eller konfigurera din IDE att skicka cookien/utlösaren. Referens: Xdebug Steg Felsökning.

3) Realistiskt fel: ”Allvarligt fel … anrop till odefinierad funktion”

Jag ser ofta detta när ett kodavsnitt klistras in i fel fil (eller körs för tidigt). I WordPress finns vissa hooks inte "direkt ur lådan". Om du testar ett mu-plugin, se till att du kopplar din kod till rätt kod.

Minitest mu-plugin (ska versioneras): skapa wp-content/mu-plugins/dev-tools.php :

<?php
/**
 * Plugin Name: Dev Tools (local)
 * Description: Aides au debug en environnement Docker.
 */

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

add_action( 'init', function () {
	// Exemple : log contrôlé (évitez var_dump en prod)
	if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
		error_log( '[dev-tools] init OK, WP ' . get_bloginfo( 'version' ) );
	}
}, 20 );

Resultat: en linje visas i wp-content/debug.log med varje begäran.


Steg 7: Samla in e-postmeddelanden och säkerställa cron-jobbets tillförlitlighet (Mailpit + systemcron)

Mål: att sluta "testa" e-postmeddelanden genom att skicka riktiga meddelanden, och att undvika överraskningar relaterade till WP-Cron.

1) Samla in e-postmeddelanden

Mailpit fångar upp det som matas ut via mail()Många plugins använder PHPMailer via WordPress, vilket ofta hamnar i sendmail i en behållare. Beroende på bilderna kan detta variera.

En pålitlig metod är att installera ett lokalt SMTP-plugin som pekar mot Mailpit (eller konfigurera PHPMailer via en hook). För att bibehålla reproducerbarhet och undvika ett gränssnitt föredrar jag ett mu-plugin.

Skapa wp-content/mu-plugins/mailpit.php :

<?php
/**
 * Plugin Name: Mailpit SMTP (local)
 * Description: Force l'envoi SMTP vers Mailpit en dev.
 */

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

add_action( 'phpmailer_init', function ( $phpmailer ) {
	// En local uniquement : si vous mettez ça en prod, vous allez perdre vos e-mails.
	if ( defined( 'WP_ENVIRONMENT_TYPE' ) && WP_ENVIRONMENT_TYPE !== 'development' ) {
		return;
	}

	$phpmailer->isSMTP();
	$phpmailer->Host       = 'mailpit';
	$phpmailer->Port       = 1025;
	$phpmailer->SMTPAuth   = false;
	$phpmailer->SMTPSecure = '';
}, 10, 1 );

test:

docker compose exec wp wp eval "wp_mail('[email protected]','Test','Hello');" --allow-root

Öppna Mailpit: http://localhost:8025.

2) Ersätt WP-Cron med ett system-cronjobb (under utveckling)

WP-Cron är beroende av trafik. Under utveckling är det slumpmässigt. Jag ersätter ofta WP-Cron med ett cron-jobb med några dagars mellanrum. minuter.

Lägg till wp-config.php (redan genererad):

docker compose exec wp wp config set DISABLE_WP_CRON true --type=constant --allow-root

Lägg sedan till en cron-tjänst i Compose (avancerat alternativ). Redigera docker-compose.yml och lägg till:

cat >> docker-compose.yml <<'EOF'

  cron:
    image: curlimages/curl:8.7.1
    container_name: wp_cron
    depends_on:
      - wp
    command: >
      sh -lc "while true; do
        curl -sS http://wp/wp-cron.php?doing_wp_cron=1 > /dev/null || true;
        sleep 60;
      done"
EOF

Starta om:

docker compose up -d

Resultat: dina schemalagda uppgifter (import, synkronisering, nyhetsbrev under utveckling) utlöses på ett stabilt sätt.


Steg 8: Lägg till snabba tester (PHPUnit + wp-env valfritt)

Mål: att ha ett minimalt skyddsnät. Även på en blogg förtjänar ett anpassat plugin eller ett specialdesignat tema åtminstone lite testning.

Alternativ A: PHPUnit i containern (enkel, snabb)

Du kan installera PHPUnit via Composer i ditt tema/plugin. Om ditt projekt redan har Composer är det nu den perfekta tiden. Annars kan du begränsa det till manuell testning.

Exempel (anpassat plugin): strukturera ett plugin wp-content/plugins/my-plugin och lägg till Composer. Jag kommer inte att gå in på detaljer om hela installationen här, eftersom det beror mycket på din arkitektur, men kom ihåg detta: håll tester utanför WordPress runtime.

Alternativ B: wp-env (officiellt utvecklingsverktyg) för isolerade tester

@wordpress/env (wp-miljöDetta är praktiskt för att lansera en engångs-WP för teständamål, utan att påverka din huvudsakliga Docker-stack. Detta är användbart i CI/CD.

Officiellt dokument: @wordpress/miljö.


Det kompletta resultatet

Om du vill kopiera allt på en gång, här är de samlade nyckelfilerna. Anpassa sedan via .env och mu-plugins.

docker-compose.yml (fullständig)

services:
  wp:
    build:
      context: .
      dockerfile: docker/Dockerfile
    container_name: wp_app
    env_file: .env
    depends_on:
      db:
        condition: service_healthy
      redis:
        condition: service_started
      mailpit:
        condition: service_started
    ports:
      - "${HTTP_PORT:-8080}:80"
    volumes:
      - ./wp:/var/www/html
      - ./wp-content:/var/www/html/wp-content
      - ./logs:/var/log/wp
    extra_hosts:
      - "host.docker.internal:host-gateway"

  db:
    image: mariadb:10.11
    container_name: wp_db
    environment:
      MYSQL_DATABASE: ${DB_NAME:-wordpress}
      MYSQL_USER: ${DB_USER:-wordpress}
      MYSQL_PASSWORD: ${DB_PASSWORD:-wordpress}
      MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASSWORD:-root}
    volumes:
      - db_data:/var/lib/mysql
    healthcheck:
      test: ["CMD", "mariadb-admin", "ping", "-h", "127.0.0.1", "-uroot", "-p${DB_ROOT_PASSWORD}"]
      interval: 5s
      timeout: 3s
      retries: 30

  redis:
    image: redis:7-alpine
    container_name: wp_redis
    command: ["redis-server", "--appendonly", "yes"]
    volumes:
      - redis_data:/data

  mailpit:
    image: axllent/mailpit:latest
    container_name: wp_mailpit
    ports:
      - "${MAILPIT_PORT:-8025}:8025"

  cron:
    image: curlimages/curl:8.7.1
    container_name: wp_cron
    depends_on:
      - wp
    command: >
      sh -lc "while true; do
        curl -sS http://wp/wp-cron.php?doing_wp_cron=1 > /dev/null || true;
        sleep 60;
      done"

volumes:
  db_data:
  redis_data:

Användbara anpassningar

  • Växlare HTTP_PORT om du redan har en lokal server på 8080.
  • Ersätt MariaDB med MySQL 8 om din produktionsmiljö är i MySQL (var uppmärksam på sorterings-/SQL-lägen).
  • Aktivera Redis på WordPress-sidan genom att installera/konfigurera pluginet korrekt (se avsnitt underhåll).

Anpassa för Divi 5 / Elementor / Avada

Byggarna ändrar inte Docker, men de ändrar dina begränsningar: minne, exekveringstid, uppladdningsstorlek och ibland PHP-beroenden (zip, intl, gd/imagemagick).

Avdelning 5

  • öka memory_limit (redan vid 512M) om du importerar tunga layouter.
  • Håll wp-content Delvis versionsredigerad: barntema + mu-plugins, men inte cacheminnen.

Elementor

  • Elementor utlöser ofta långa AJAX-förfrågningar: max_execution_time Vid 120 sekunder undviker den falska negativa resultat.
  • Om CSS/JS inte laddas: kontrollera permalänkarna och headercachen (ofta en dålig sådan) AllowOverride eller en webbläsarcache).

Avada (Fusion Builder)

  • Demoimporter: se till att du har zip et intl (installerat i Dockerfilen), annars får du tysta fel.
  • Avada kan generera många filer: keep wp-content/uploads i volym (detta är redan fallet via ./wp-content).

Slutkontroll

  1. Åtkomst framifrån: http://localhost:8080 visar webbplatsen.
  2. Administratörsåtkomst: /wp-admin Det fungerar, inloggning är okej.
  3. Permanenta länkar: en artikel i /%postname%/ returnerar inte 404.
  4. Loggar: ett avsiktligt PHP-fel visas i wp-content/debug.log (testa med en error_log('test') i ett mu-plugin).
  5. Post: en wp_mail() visas i Mailpit.
  6. Xdebug: genom att lägga till ?XDEBUG_TRIGGER=1, din IDE kommer att fånga en begäran (om den är konfigurerad).

Om resultatet inte är som förväntat

Här är en diagnostisk tabell som jag använder som ett första steg. Den täcker de vanligaste symptomen på WordPress Docker-stackar.

Symptôme Trolig orsak verifiering Lösning
Tom sida / 500 PHP-fel, inkompatibelt plugin, minne Lire logs/apache-error.log et wp-content/debug.log Inaktivera plugin via WP-CLI, öka memory_limiträtta felet
Felet "Fel vid upprättande av databasanslutning" Databasen är inte klar, felaktiga inloggningsuppgifter, skadade volymer docker compose logs db + test mariadb-admin ping Kontrollanten .envta bort databasvolymen och starta om installationen
Permanenta länkar 404 mod_rewrite off / AllowOverride blockerad Kontrollera Apache-konfigurationen och a2enmod rewrite Återskapa bilden, starta om wp rewrite structure ... --hard
Xdebug kan inte ansluta Felaktig värd/port, saknad trigger Lyssnar IDE efter 9003? Skickas triggern? Konfigurera IDE, använd host.docker.internalkontrollera brandväggen
E-postmeddelandena visas inte SMTP-plugin-åsidosättning, PHPMailer är inte konfigurerad Testare wp_mail() + loggar Aktivera Mailpit mu-plugin, verifiera värden mailpit port 1025

Felsökningskommandon (reflexer)

# Voir l'état
docker compose ps

# Logs d'un service
docker compose logs -f wp
docker compose logs -f db

# Entrer dans le conteneur WP
docker compose exec wp bash

# Désactiver tous les plugins (si wp-admin inaccessible)
docker compose exec wp wp plugin deactivate --all --allow-root

# Reconstruire proprement
docker compose down
docker compose up -d --build

Vanliga fallgropar och misstag

Fel Orsak Lösning
Kopiera ett utdrag till functions.php fel tema Du redigerar föräldratemat istället för undertemat Versionera ett barntema, eller använd ett mu-plugin för utvecklarkoden
Glömmer ett semikolon i ett mu-plugin Omedelbart parsningsfel, ibland en tom sida se debug.log et apache-error.log, korrigera, ladda om
Förvirring mellan handling och filter (olämplig hook) Koden körs inte, eller körs för tidigt. utiliser init/wp_loaded Kontrollera prioritet vid behov
Testa en gammal handledning (WP 5.x) med föråldrad kod Funktioner/konstanter ändrade, beteende annorlunda Kolla den officiella dokumentationen för WP 6.9+ på developer.wordpress.org
CSS/JS har inte laddats illa wp_enqueue_scriptdålig hook, webbläsarcache Undersökning om wp_enqueue_scripts/admin_enqueue_scriptsrensa cachen
WP-CLI misslyckas "Kunde inte ansluta till databasen" DB inte redo (rasvillkor) Hälsokontroll + väntan vid entrén (ingår redan)
Fel relaterat till föråldrad PHP Du ändrade PHP-avbildningen utan att bygga om den docker compose build --no-cache, kolla php -v i behållaren

Variant / alternativ

Alternativ 1: LocalWP / DevKinsta (utan Docker, "manuell")

Om ditt huvudmål är att utveckla ett tema eller en webbplatsbyggare utan att hantera infrastrukturen, kan verktyg som LocalWP vara snabbare att lära sig. Nackdelen är att teamets reproducerbarhet är mindre enkel än med ett Docker-arkiv, och CI/CD-integration är ofta mer komplex.

Alternativ 2 (avancerat): Traefik + HTTPS + flera domäner

När man hanterar 10 projekt parallellt byter jag ofta till Traefik för att undvika portkrig och ha lokal HTTPS. Det tar längre tid att konfigurera, men då har man:

  • https://projet-a.test, https://projet-b.test.
  • automatiska certifikat (mkcert eller Traefik + lokal CA)
  • en ren flerprojektsstack

Jag aktiverar det inte som standard här för att hålla fokus på WordPress, men det är en naturlig tillägg.


Tips för säkerhet, prestanda och underhåll

Säkerhet

  • Publicera inte din port 3306 (DB) till värden om det inte är nödvändigt.
  • Håll WP_DEBUG_DISPLAY à false Även under utveckling: du undviker "felsökning" via trasig HTML.
  • Om du exponerar webbplatsen på ett nätverk (klientdemo), ändra lösenorden och begränsa åtkomsten.

Prestanda

  • Xdebug som en utlösare: det är redan en stor vinst.
  • Undvik att aktivera aggressiv cachning i utvecklingen (minify, concat) medan du felsöker.
  • Opcache: I ren utveckling lämnar jag den ofta inaktiverad. Om du vill simulera produktion, aktivera den och inaktivera den sedan korrekt.

Underhåll

  • Frys avbildningsversioner (MariaDB, Redis). Undvik latest förutom en icke-kritisk tjänst (Mailpit kan finnas kvar i den senaste versionen).
  • Uppdatera WordPress via WP-CLI, inte genom att "klicka": du behåller en historik och kan automatisera.

Användbara kommandon:

# Mettre à jour WP (exemple)
docker compose exec wp wp core update --allow-root

# Vérifier version WP
docker compose exec wp wp core version --allow-root

# Vérifier PHP
docker compose exec wp php -v

# Nettoyer volumes (ATTENTION: supprime la DB)
docker compose down -v

För att gå vidare

  • Lägg till tjänst phpMyAdmin (eller administratör) endast om ditt team behöver det. Jag föredrar WP-CLI + SQL-dumpar, men vissa arbetsflöden kräver ett användargränssnitt.
  • Lägg till en Makefile (Eller justfile) för att standardisera kommandona (make up, make reset, make test).
  • Implementera en "säker" databasimport: komprimerad dump + URL-ersättning via wp search-replace avec --skip-columns=guid.
  • Skapa en "prod-liknande" avbildning (utan Xdebug, med opcache) och en "dev"-avbildning. Samma Compose, två profiler.

Resurser


FAQ

Varför klättra ./wp i volym istället för att bygga kärnan i bilden?

Eftersom du vill kunna växla snabbt mellan WordPress-versioner (6.9.4 → 6.9.5) via WP-CLI och versionsredigera dina skript, inte bygga om en image med varje patch. För projekt med hög kontroll kan du också "sälja" kärnan, men det är en annan metod.

Fungerar den här installationen exakt med PHP 8.1?

Ja, om du ändrar basbilden (wordpress:php8.1-apacheJag siktar på PHP 8.3 här eftersom det bättre återspeglar nuvarande miljöer. Om din produktionsmiljö är på 8.1, anpassa dig till det för att undvika överraskningar.

Hur importerar jag en kunddatabas?

Den mest pålitliga: SQL-dump + wp db import puis wp search-replace. Exempel:

docker compose exec -T wp wp db import /var/www/html/wp-content/uploads/dump.sql --allow-root
docker compose exec wp wp search-replace 'https://www.site-client.tld' 'http://localhost:8080' --skip-columns=guid --allow-root

Pourquoi WP_CONTENT_URL är inställd på http://localhost/wp-content i ingångspunkten?

Det här är en genväg som kan vara felaktig om du ändrar porten. Om du använder en annan port, justera den (eller ta bort dessa två konstanter och låt WordPress hantera det). Jag har sett att det löser knepiga fall där lokala omvända proxyservrar skriver om rubriker, men det är inte obligatoriskt.

Redis körs, men WordPress använder det inte. Är det normalt?

Ja. Redis på serversidan räcker inte: du behöver ett plugin för objektcachning (t.ex. redis-cache) och, beroende på plugin, aktivera cachning via WP-CLI eller administratörspanelen. Under utveckling installerar jag det men aktiverar det inte alltid.

Varför använda Query Monitor i utveckling?

Eftersom det är det snabbaste sättet att identifiera en långsam förfrågan, en hook som anropas för ofta eller ett blockerande HTTP-anrop. På builder-webbplatser är det ofta skillnaden mellan "Jag antar" och "Jag vet".

Jag ser "Åtkomst nekad" på filer i wp-contentVad ska man göra?

Detta händer när UID/GID för värden och containern inte är i linje (särskilt på Linux). Den rätta lösningen är att köra Apache/PHP med en motsvarande användare, eller att korrigera behörigheterna för din projektmapp. Undvik denna reflex. chmod -R 777.

Kan jag använda Nginx istället för Apache?

Ja, men du måste hantera PHP-FPM, Nginx-konfiguration och permalänkregler. Apache är enklare för en reproducerbar handledning, särskilt om du vill minimera skillnader mellan miljöer.

Hur kan jag verifiera den exakta versionen av WordPress i containern?

docker compose exec wp wp core version --allow-root

Var kan jag spåra kärnförändringar om ett beteende ändras mellan 6.9.x och 6.10?

Kolla biljetterna och commits på Trac och GitHub: