Dieses Setup baut ein eigenes Drupal-Image auf Basis des offiziellen drupal-Containers und stellt Services per Compose-Profil bereit:
- Webserver wählbar:
web-nginx,web-apache,web-ols - Reverse Proxy:
reverse-proxy(NGINX) - Datenbank wählbar:
db-mysql,db-mariadb,db-postgres - Cache wählbar:
cache-redis,cache-varnish - Zusatztools:
phpmyadmin,mailpit,tools(composer,drush,node)
Optional zuerst ENV-Konfiguration übernehmen:
cp .env.example .envDas Template kann direkt als neue Projektinstanz für dev, stag oder prod erzeugt werden:
scripts/create-instance.sh -n <projektname> -p <zielpfad> -e <dev|stag|prod>Beispiel:
scripts/create-instance.sh -n shop360 -p /home/dev/projects -e devWenn Werte fehlen, fragt das Skript assistierend nach (Projektname, Pfad, Umgebung, Domain, Composer-Name).
Zusätzliche Optionen:
# Composer-Projektname für drupal/composer.json direkt setzen
scripts/create-instance.sh -n shop360 -p /srv/stag -e stag -c infrasightsolutions/shop360
# PUBLIC_DOMAIN direkt setzen
scripts/create-instance.sh -n shop360 -p /srv/prod -e prod -d www.shop360.example.com
# Bestehenden Projektordner behalten/mergen (sicher für vorhandenes Git-Repo)
scripts/create-instance.sh -n shop360 -p /home/dev/projects -e dev -k
# Non-interactive + bestehende Inhalte behalten
scripts/create-instance.sh -n shop360 -p /home/dev/projects -e dev -y -k
# Nur Vollständigkeit prüfen (ohne Änderungen), Exit 0/5/6
scripts/create-instance.sh -n shop360 -p /home/dev/projects -e dev --verify-onlyGitHub-Workflow (bestehendes Projekt nicht löschen):
cd /home/dev/projects
git clone [email protected]:your-org/shop360.git
cd /home/dev/projects/drupal_docker_instance_generator
scripts/create-instance.sh -n shop360 -p /home/dev/projects -e dev -y -kHinweis: Der Composer-Name wird aus COMPOSER_PROJECT_NAME der jeweiligen .env.<env> gelesen und in drupal/composer.json als name eingetragen.
Der Standard-Zielpfad wird aus PROJECT_BASE_PATH der jeweiligen .env.<env> gelesen.
create-instance.sh setzt außerdem PUBLIC_DOMAIN direkt in die Server-Konfigurationen:
config/nginx/web-nginx.conf(server_name)config/apache/httpd.conf(ServerName)config/openlitespeed/httpd_config.conf(serverName,map,virtualHost)config/openlitespeed/vhconf.conf(vhDomain)
Wenn COMPOSE_PROFILES das Profil reverse-proxy enthält, setzt der Generator UPSTREAM_HOST automatisch passend zum aktiven Webprofil:
web-ols→web-openlitespeedweb-apache→web-apache- sonst →
web-nginx
STACK_ENVkorrekt (dev,stag,prod)PUBLIC_DOMAINals Hostname ohnehttp(s)://und ohne PfadREVERSE_PROXY_HOST_PORTals numerischer Port (1..65535)- Zielpfad beschreibbar und Zielordner entweder leer oder Aufruf mit
-f(alles löschen) bzw.-k(bestehendes behalten/mergen) - Optional vorab prüfen mit
scripts/validate-env.sh .env.<env>
Im Repo gibt es Wrapper unter bin/, die dir das docker compose exec ... abnehmen (ähnlich vom Gefühl wie bei Lando):
bin/composer install
bin/drush status
bin/drush crDie Wrapper sind sudo-kompatibel und verwenden bei Bedarf automatisch SUDO_UID/SUDO_GID. Falls der Composer-Cache einmal mit falschem Owner angelegt wurde, hilft:
sudo chown -R $(id -u):$(id -g) .cacheWenn du willst, dass du im Projektordner einfach composer/drush tippen kannst, kannst du bin/ temporär in deinen PATH nehmen:
export PATH="$PWD/bin:$PATH"Oder direkt mit fertigen Umgebungsdateien starten:
docker compose --env-file .env.dev up -d --build
docker compose --env-file .env.stag up -d --build
docker compose --env-file .env.prod up -d --buildUngültige Umgebung: nurdev,stag,prodsind erlaubt.- Zielordner nicht leer: mit
-fkomplett überschreiben/löschen oder mit-kInhalte behalten/mergen. - Vollständigkeit prüfen: mit
--verify-only(0 = vollständig, 5 = fehlende Pfade, 6 = Zielordner fehlt). - Rechtefehler bei
/srv/stagoder/srv/prod: Skript mit einem erlaubten Benutzer ausführen oder Pfad per-pauf einen beschreibbaren Ort setzen. - Falscher Standard-Zielpfad:
PROJECT_BASE_PATHin.env.dev,.env.stag,.env.prodanpassen. - Falscher Composer-Name:
COMPOSER_PROJECT_NAMEin.env.<env>setzen oder beim Aufruf mit-cüberschreiben.
Der Generator ruft die Env-Validierung automatisch auf. Du kannst sie auch manuell ausführen:
scripts/validate-env.sh .env.dev
scripts/validate-env.sh .env.stag
scripts/validate-env.sh .env.prodGeprüft werden u. a.:
- Pflicht-Keys:
COMPOSER_PROJECT_NAME,PROJECT_BASE_PATH,CONTAINER_PREFIX,STACK_ENV,COMPOSE_PROFILES PUBLIC_DOMAIN: muss Hostname ohne Schema/Pfad/Leerzeichen seinREVERSE_PROXY_HOST_PORT: muss TCP-Port im Bereich1..65535sein- Empfehlung:
TRAEFIK_PREFIXsetzen für stabile Traefik-Router-Namen
Exit-Codes:
0: Validierung erfolgreich2: Env-Datei fehlt oder ist nicht lesbar3: Pflicht-Keys fehlen4: Ungültige Env-Werte (z. B. Domain/Port)
Häufige Fehlermeldungen und schnelle Fixes:
PUBLIC_DOMAIN must be a hostname ...- Setze
PUBLIC_DOMAINohne Schema/Pfad, z. B.PUBLIC_DOMAIN=shop.example.com
- Setze
REVERSE_PROXY_HOST_PORT must be a valid TCP port 1-65535- Setze einen numerischen Port im gültigen Bereich, z. B.
REVERSE_PROXY_HOST_PORT=8088
- Setze einen numerischen Port im gültigen Bereich, z. B.
Missing required .env keys ...- Fehlende Keys aus
.env.exampleoder der passenden.env.<env>ergänzen
- Fehlende Keys aus
Oder über Hilfsskripte:
scripts/up-dev.sh
scripts/up-stag.sh
scripts/up-prod.shscripts/up-dev.sh ruft automatisch scripts/ensure-settings-local.sh auf, damit drupal/web/sites/default/settings.php vorhanden und für den Installer schreibbar ist.
Nach Installation/Änderungen kann die Datei mit scripts/lock-settings.sh wieder auf 0444 gesetzt werden.
Mit optionalem IPv6-Override-Modus:
scripts/up-stag.sh ipv6
scripts/up-stag.sh ipv6-rpStoppen je Umgebung:
scripts/down-dev.sh
scripts/down-stag.sh
scripts/down-prod.shMit Volume-/Orphan-Bereinigung:
scripts/down-stag.sh purgeLogs je Umgebung:
scripts/logs-dev.sh
scripts/logs-stag.sh
scripts/logs-prod.shLogs für einzelnen Service:
scripts/logs-stag.sh web-nginxDev-DB (MySQL) zurücksetzen (Achtung: Datenverlust in DEV-DB):
scripts/reset-dev-db.shStaging-/Production-DB zurücksetzen (Achtung: Datenverlust, doppelte Sicherheitsabfrage):
scripts/reset-stag-db.sh
scripts/reset-prod-db.shStatus je Umgebung:
scripts/status-dev.sh
scripts/status-stag.sh
scripts/status-prod.shBeachte: Services werden nur gestartet, wenn ihr Profil in COMPOSE_PROFILES der verwendeten .env enthalten ist. Beispiele:
fpm— PHP-FPM-Services (z. B.drupal-fpm,bedrock-fpm).phpmyadmin,mailpit— UI-Tools; starten nur, wenn diese Profile inCOMPOSE_PROFILESstehen.
Starten (Beispiel):
docker compose --env-file .env.dev up -d --buildÄndern: Passe COMPOSE_PROFILES in .env.dev, .env.stag oder .env.prod an, oder verwende bei Bedarf --profile beim docker compose-Aufruf.
Status für einzelnen Service:
scripts/status-prod.sh web-nginxphpMyAdmin und Mailpit UI Zugriff:
- Port-basiert (DEV):
http://localhost:8081(phpMyAdmin) undhttp://localhost:8025(Mailpit UI) - Domain-basiert via Traefik:
PHPMYADMIN_DOMAINundMAILPIT_DOMAIN(TLS + Middleware) - SMTP für Mailpit bleibt auf
MAILPIT_SMTP_HOST_PORT(Default1025) - Wenn
TRAEFIK_SECURITY_MIDDLEWARES=dev-vpn-only@filegesetzt ist, ist Domainzugriff außerhalb des VPN erwartbar403. - Optional kann für nur diese UIs
TRAEFIK_UI_SECURITY_MIDDLEWARESseparat gesetzt werden.
Hinweis: Domain-, Port- und IP-Bindings werden aus den jeweiligen .env-Dateien gelesen (PUBLIC_DOMAIN, PHPMYADMIN_DOMAIN, MAILPIT_DOMAIN, PUBLIC_BIND_ADDRESS, PUBLIC_BIND_ADDRESS_V4, *_HOST_PORT).
HTTPS/SSL-Check je Umgebung:
scripts/check-https-dev.sh
scripts/check-https-stag.sh
scripts/check-https-prod.shDirekt über Hauptskript (mit optionaler Domain-Override):
scripts/check-https.sh stag
scripts/check-https.sh prod www.example.comProjektpfade je Umgebung:
- DEV:
/home/dev/projects/<project> - STAG:
/srv/stag/<project> - PROD:
/srv/prod/<project>
Wichtig: docker compose immer im jeweiligen Projektordner ausführen, damit relative Volumes (./drupal, ./config) korrekt aufgelöst werden.
Service-Auswahl erfolgt primär über .env (COMPOSE_PROFILES):
- Webserver genau einen wählen:
web-nginxoderweb-apacheoderweb-ols - Datenbank genau einen wählen:
db-mysqloderdb-mariadboderdb-postgres - Cache optional wählen:
cache-redisund/odercache-varnish - Reverse-Proxy nur bei Bedarf:
reverse-proxy - Zusatzdienste optional:
phpmyadmin,mailpit,tools
Dann reicht:
docker compose up -d --buildAlternativ ohne .env direkt per CLI-Profile:
docker compose --profile web-nginx --profile reverse-proxy --profile tools up -d --buildapp_netist intern (internal: true) für FPM ↔ Web ↔ Toolsedge_netist extern und wird über.envauf bestehende Umgebung gemappt:- Dev:
EDGE_NETWORK_NAME=dev_net - Staging:
EDGE_NETWORK_NAME=stag_net - Prod:
EDGE_NETWORK_NAME=prod_net
- Dev:
proxyist extern für Traefik-Docker-Provider (TRAEFIK_DOCKER_NETWORK=proxy)
Falls Netze noch nicht existieren:
docker network create dev_net
docker network create stag_net
docker network create prod_netEmpfohlene .env-Beispiele:
# DEV
COMPOSE_PROFILES=web-ols,db-mysql,cache-redis,phpmyadmin,tools
EDGE_NETWORK_NAME=dev_net
UPSTREAM_HOST=web-openlitespeed
DB_HOST=db-mysql
# STAGING
COMPOSE_PROFILES=web-nginx,reverse-proxy,db-mysql,cache-redis,tools
EDGE_NETWORK_NAME=stag_net
UPSTREAM_HOST=web-nginx
DB_HOST=db-mysql
# PROD (Beispiel ohne internen Reverse-Proxy)
COMPOSE_PROFILES=web-nginx,db-postgres,cache-redis,tools
EDGE_NETWORK_NAME=prod_net
DB_DRIVER=pgsql
DB_HOST=db-postgres
DB_PORT=5432Best Practice: phpmyadmin nur mit db-mysql/db-mariadb nutzen; für db-postgres stattdessen pgAdmin in einem separaten Profil ergänzen.
- Domain je Umgebung über
.env:PUBLIC_DOMAIN(z. B.stag.example.com,www.example.com) - Öffentlich gebundene Ports IPv6-first über
PUBLIC_BIND_ADDRESS(Default[::]) - Traefik Labels sind auf Webservern vorkonfiguriert (HTTP→HTTPS Redirect + TLS Router)
- HTTP/2 und HTTP/3 werden in Traefik primär über EntryPoint-Konfiguration aktiviert; Compose liefert die Router/TLS-Bindings.
- Für Dev-Projekte als Subdomains unter
projects.infrasight-solutions.comverwendeTRAEFIK_CERTRESOLVER=le-dns. - Setze pro Projekt eine eindeutige Domain, z. B.
PUBLIC_DOMAIN=myproject.projects.infrasight-solutions.com. - Für STAG/PROD kann
PUBLIC_DOMAINeine beliebige öffentliche Domain sein (z. B.stag.example.com,www.example.com). - Starte genau einen Webserver-Profiltyp gleichzeitig (
web-nginxoderweb-apacheoderweb-ols), um konkurrierende Router für dieselbe Domain zu vermeiden. - Für Dev-Zugriff bleibt zusätzlich
TRAEFIK_SECURITY_MIDDLEWARES=dev-vpn-only@fileaktiv.
Voraussetzungen für Zertifikatsausstellung:
- Domain DNS zeigt auf den Traefik-Host (A/AAAA je Setup).
- Der in Traefik konfigurierte Resolver-Name entspricht
TRAEFIK_CERTRESOLVER(hier standardmäßigle-dns). - Bei DNS-01 müssen die DNS-Provider-Credentials in der zentralen Traefik-Umgebung vorhanden sein.
Hinweis zu Trusted Hosts:
- Standardmäßig werden Trusted Hosts aus
PUBLIC_DOMAINabgeleitet (viadocker-compose.ymlDefault). DRUPAL_TRUSTED_HOST_PATTERNSnur setzen, wenn mehrere Domains/Patterns benötigt werden.
Konfigurationscheck:
docker compose --profile web-nginx config | grep -E "Host\(|tls\.certresolver|middlewares"Wenn Web-Container eine feste IPv6 im externen Netz (stag_net, prod_net) benötigen, ist das optional über ein Override gelöst:
docker compose -f docker-compose.yml -f docker-compose.ipv6.yml up -d --buildIn .env setzen:
EDGE_NETWORK_NAME=stag_net
WEB_IPV6_ADDRESS=2a0a:4cc0:0:2d05:1300::10Für PROD entsprechend:
EDGE_NETWORK_NAME=prod_net
WEB_IPV6_ADDRESS=2a0a:4cc0:0:2d05:1200::10Ohne Override-Datei werden IPs wie bisher dynamisch vergeben (Best Practice für DEV).
Wenn reverse-proxy aktiv ist, kann die primäre öffentliche IPv6 auf dem Reverse-Proxy liegen:
docker compose -f docker-compose.yml -f docker-compose.ipv6.reverse-proxy.yml up -d --buildBeispiel .env:
EDGE_NETWORK_NAME=stag_net
WEB_IPV6_ADDRESS=2a0a:4cc0:0:2d05:1300::10
WEB_BACKEND_IPV6_ADDRESS=2a0a:4cc0:0:2d05:1300::11Damit erhält reverse-proxy die primäre IPv6 (WEB_IPV6_ADDRESS), der eigentliche Web-Container eine separate Backend-IPv6 im selben edge_net.
PHPMYADMIN_IPV6_ADDRESS wird nur wirksam, wenn Profil phpmyadmin aktiv ist und ein IPv6-Override geladen wird.
Empfehlung:
dev:TRAEFIK_SECURITY_MIDDLEWARES=dev-vpn-only@file(Public Domain nur via VPN erreichbar)stag:TRAEFIK_SECURITY_MIDDLEWARES=staging-auth@fileprod: leere oder produktive Security-Middleware je Bedarf
Wichtig: Für DEV muss die Domain (z. B. dev.infrasight-solutions.com) auf Traefik zeigen, aber der Zugriff wird durch dev-vpn-only@file auf VPN-Quellnetze begrenzt.
- Für alle Services sind
mem_limitundcpusper.enveinstellbar. - Drupal-Code ist in den Webserver-Containern read-only gemountet; im
drupal-fpmread-write, damit Drupal-Dateioperationen und Unter-Mounts zuverlässig funktionieren. - Schreibbar sind nur notwendige Pfade per Named Volumes:
web/sites/default/filesweb/sites/default/private/tmp/drupal
- Rechte werden beim FPM-Start gesetzt über:
DRUPAL_WRITABLE_UIDDRUPAL_WRITABLE_GIDDRUPAL_WRITABLE_DIR_MODEDRUPAL_WRITABLE_FILE_MODE
Drupal wird nicht out-of-the-box installiert. Installation erfolgt via Composer:
docker compose --profile tools run --rm composer installDrush nutzen:
docker compose --profile tools up -d drush
docker compose exec drush vendor/bin/drush status
docker compose exec drush vendor/bin/drush crNode nutzen:
docker compose --profile tools up -d node
docker compose exec node npm ci
docker compose exec node npm run buildPro Service konfigurierbar über ENV in der docker-compose.yml:
UPDATE_POLICY=off|check|upgradeUPDATE_TARGET=system|php
Beispiele:
FPM_UPDATE_POLICY=check→ listet verfügbare Updates beim StartFPM_UPDATE_POLICY=upgrade+FPM_UPDATE_TARGET=system→ führt System-Upgrade beim Start aus
Hinweis: Für echte PHP-Sicherheitsupdates im offiziellen Drupal-Image ist in der Praxis meist Image-Rebuild + Redeploy der saubere Weg.
docker/drupal-fpm/Dockerfiledocker/drupal-fpm/container-entrypoint.shdocker/drupal-fpm/update-check.shconfig/php/php.iniconfig/nginx/web-nginx.confconfig/apache/httpd.confconfig/openlitespeed/httpd_config.confconfig/openlitespeed/vhconf.confconfig/reverse-proxy/reverse-proxy.conf.template
docker build -f docker/drupal-fpm/Dockerfile -t <dockerhub-user>/drupal11-fpm:11.3-php8.4 .
docker login
docker push <dockerhub-user>/drupal11-fpm:11.3-php8.4