Laravel Docker Deployment: Sauber deployen mit Docker Compose

Einleitung: Warum Laravel Docker Deployment?

Moderne Laravel-Projekte stoßen schnell auf Probleme wie inkonsistente Umgebungen, langwierige Server-Konfiguration und schwierige Skalierung. Mit Laravel Docker Deployment nutzt du Containerisierung, um lokale und Produktionsumgebungen identisch zu gestalten, Abhängigkeiten klar zu definieren und Deployments zu automatisieren. In diesem Artikel lernst du praxisnah, wie du deine Projektstruktur aufsetzt, maßgeschneiderte Dockerfiles und eine docker-compose.yml erstellst, Performance-Optimierungen umsetzt und eine CI/CD-Pipeline für automatisierte Deployments integrierst.

Voraussetzungen: Was brauchst du für das Laravel Docker Deployment?

Bevor du loslegst, solltest du folgende Tools und Kenntnisse mitbringen:

  • Docker (Community Edition) installiert und lauffähig
  • Docker Compose für dein Multi-Container-Setup
  • Composer zur Verwaltung von PHP-Abhängigkeiten
  • Git für Versionskontrolle und Deployment-Skripte
  • Laravel-Grundkenntnisse (Routing, Migrationen, .env-Dateien)
  • Terminal-/CLI-Erfahrung, um Container zu starten und Logs zu prüfen
  • Code-Editor wie VS Code oder PHPStorm für komfortables Arbeiten

Projektstruktur und Dockerfile – So bereitest du deine Laravel-App vor

Projektstruktur im Überblick

Lege im Wurzelverzeichnis deiner Laravel-App folgende Struktur an:

my-laravel-app/
├─ app/                  # Applikationscode
├─ bootstrap/            # Framework-Bootstrapping
├─ config/               # Konfigurationsdateien
├─ database/             # Migrationen & Seeds
├─ public/               # Webroot: index.php, Assets
├─ resources/            # Views, SCSS, JS-Ressourcen
├─ routes/               # Route-Definitionen
├─ storage/              # Logs, Caches, Uploads
├─ tests/                # Unit- & Feature-Tests
├─ Dockerfile            # PHP-FPM-Container-Build
├─ docker-compose.yml    # Multi-Container-Orchestrierung
├─ .env                  # Umgebungsvariablen
└─ composer.json         # PHP-Abhängigkeiten

Damit hast du deine Laravel-Struktur um Docker-Konfigurationsdateien erweitert. So bleibt alles sauber getrennt und sofort einsatzbereit für Container.

Aufbau des Dockerfile

Erstelle im Projekt-Root ein Dockerfile mit folgendem Inhalt:

# 1. Basis-Image mit PHP-FPM
FROM php:8.1-fpm

# 2. Systemabhängigkeiten installieren
RUN apt-get update && apt-get install -y \
    libzip-dev zip unzip git \
 && docker-php-ext-install pdo_mysql zip bcmath \
 && pecl install xdebug \
 && docker-php-ext-enable xdebug

# 3. Composer aus dem offiziellen Image übernehmen
COPY --from=composer:2 /usr/bin/composer /usr/bin/composer

# 4. Arbeitsverzeichnis setzen
WORKDIR /var/www/html

# 5. Composer-Install separiert für effektivere Layer-Caches
COPY composer.json composer.lock ./
RUN composer install --no-dev --optimize-autoloader

# 6. Anwendungscode kopieren
COPY . .

# 7. Dateiberechtigungen für Storage & Cache
RUN chown -R www-data:www-data storage bootstrap/cache

# 8. Port freigeben & Startbefehl
EXPOSE 9000
CMD ["php-fpm"]

Dieses Dockerfile richtet einen schlanken PHP-FPM-Container ein, installiert Systembibliotheken und PHP-Extensions, bindet Composer direkt ein und optimiert durch frühes composer install den Cache-Aufbau. Die korrekten Berechtigungen stellen sicher, dass Laravel Logs und Caches schreiben kann.

docker-compose.yml konfigurieren: Nginx, PHP-FPM und MySQL zusammenbringen

Jetzt legen wir eine docker-compose.yml an, die deine Laravel-App im Container-Netzwerk betreibt:

version: '3.8'

services:
  app:
    build:
      context: .
      dockerfile: Dockerfile
    image: laravel_app
    container_name: laravel_app
    volumes:
      - ./:/var/www/html
    env_file:
      - .env
    networks:
      - laravel_network

  webserver:
    image: nginx:alpine
    container_name: laravel_nginx
    ports:
      - "80:80"
    volumes:
      - ./:/var/www/html:ro
      - ./nginx/default.conf:/etc/nginx/conf.d/default.conf:ro
    depends_on:
      - app
    networks:
      - laravel_network

  db:
    image: mysql:8.0
    container_name: laravel_db
    restart: unless-stopped
    volumes:
      - dbdata:/var/lib/mysql
    environment:
      - MYSQL_DATABASE=${DB_DATABASE}
      - MYSQL_USER=${DB_USERNAME}
      - MYSQL_PASSWORD=${DB_PASSWORD}
      - MYSQL_ROOT_PASSWORD=${DB_ROOT_PASSWORD}
    ports:
      - "3306:3306"
    networks:
      - laravel_network

volumes:
  dbdata:

networks:
  laravel_network:
    driver: bridge

Erklärung der wichtigsten Abschnitte:

  1. version: Definiert die Compose-Datei-Version für neue Features.
  2. app: Baut den PHP-FPM-Container aus deinem Dockerfile, bindet den Code und lädt Umgebungsvariablen aus .env.
  3. webserver: Nutzt nginx:alpine, mappt Port 80 und lädt deine Nginx-Konfiguration (readonly).
  4. db: Startet MySQL 8.0 mit Credentials aus .env, speichert Daten persistent in dbdata.
  5. networks & volumes: Erstellt ein isoliertes Netzwerk und ein Volume für die Datenbank.

Mit diesem Setup startest du alle drei Services gleichzeitig, stellst identische Umgebungen sicher und erleichterst spätere Skalierung.

Umgang mit .env-Dateien und Secrets sicher gestalten

Sensible Umgebungsvariablen und geheime Schlüssel gehören nicht ins Repository. So stellst du Sicherheit und Flexibilität sicher:

  1. .env aus Git ausschließen

    • Trage .env in .gitignore ein, versieh dein Repo stattdessen mit einer .env.example.
    • So haben alle Entwickler die Vorlage, ohne echte Secrets zu riskieren.
  2. Env-Files im Docker-Umfeld

    • Nutze in docker-compose.yml die Direktive env_file, um Variablen aus einer lokalen Datei zu laden:
      services:
        app:
          env_file:
            - .env
    
    • Achte darauf, dass .env auf deinem CI-Server oder im Produktiv-Cluster sicher bereit­gestellt wird (z. B. per Secret-Management).
  3. Docker Secrets (für Swarm & Kubernetes)

    • Lagere kritische Daten in Docker Secrets aus statt in Umgebungs­variablen:
      services:
        app:
          secrets:
            - laravel_env
    
      secrets:
        laravel_env:
          file: ./secrets/.env.production
    
    • Im Container erreichst du sie unter /run/secrets/laravel_env und bittest Laravel, diese Datei zu parsen.
  4. Laravel-Konfiguration cachen

    • Nach dem Deployment führst du aus:
      php artisan config:cache
    
    • So werden Umgebungswerte einmalig geladen und gelten performant im OPCache, ohne dass .env bei jedem Request gelesen wird.
  5. Externe Secret-Manager

    • AWS Secrets Manager, HashiCorp Vault oder Azure Key Vault lassen sich per SDK oder CLI anbinden.
    • Zieh Secrets bei Container-Start dynamisch, statt sie fest einzubetten.

Mit diesen Maßnahmen verhinderst du versehentliches Auschecken geheimer Schlüssel, behältst volle Kontrolle über Zugriffsrechte und sorgst für einen sauberen, skalierbaren Deployment-Workflow.

Performance optimieren: OPCache und Queue-Worker in Laravel unter Docker

OPCache-Einstellungen anpassen

Um deine Laravel-App performant zu betreiben, aktivierst du OPCache im PHP-Container und passt die Parameter an:

  1. Lege eine opcache.ini im Projekt-Root an:
   [opcache]
   opcache.enable=1
   opcache.memory_consumption=128        ; maximaler Speicher in MB
   opcache.interned_strings_buffer=8     ; Buffer für interned Strings
   opcache.max_accelerated_files=10000   ; Anzahl gecachter Dateien
   opcache.validate_timestamps=0        ; setze 1 in Dev, 0 in Prod
   opcache.revalidate_freq=60            ; Sekunden bis Re-Validation
   opcache.save_comments=1               ; für Annotationen nötig
  1. Binde die Datei im Dockerfile ein:
   # OPCache-Config kopieren
   COPY opcache.ini /usr/local/etc/php/conf.d/opcache.ini
  1. Baue das Image neu und prüfe mit php -i | grep opcache, ob OPCache aktiv ist.
  2. In Produktiv-Setups stellst du validate_timestamps=0 und revalidate_freq=0 ein, um Dateisystem-Checks zu vermeiden.

Queue-Worker als Docker-Service betreiben

So stellst du sicher, dass deine Jobs zuverlässig laufen und bei Fehlern neu starten:

  1. Ergänze deine docker-compose.yml um einen queue-Service:
   services:
     queue:
       build:
         context: .
         dockerfile: Dockerfile
       container_name: laravel_queue
       command: >
         php artisan queue:work
         --sleep=3
         --tries=3
         --timeout=90
       restart: unless-stopped
       volumes:
         - ./:/var/www/html
       env_file:
         - .env
       depends_on:
         - db
       networks:
         - laravel_network
  1. Erklärungen:
  • --sleep=3: Wartezeit zwischen leeren Warteschlangen
  • --tries=3: Anzahl maximaler Versuche pro Job
  • --timeout=90: Sekunden, ehe ein Job abgebrochen wird
  • restart: unless-stopped: Service restarts bei Fehlern automatisch
  1. Für größere Lasten empfiehlt sich Laravel Horizon oder ein Supervisor-Daemon im Container.

Mit aktiviertem OPCache und einem resilienten Queue-Worker-Service läuft deine Laravel-App unter Docker deutlich performanter und ausfallsicher.

CI/CD-Pipeline mit GitHub Actions für automatisiertes Laravel Deployment

Wir richten einen GitHub Actions Workflow ein, der beim Push in den main-Branch automatisch Build, Tests und Deployment auf deinem Server startet. Lege dazu die Datei .github/workflows/deploy.yml an:

name: CI/CD Laravel Docker

on:
  push:
    branches:
      - main

jobs:
  build-and-test:
    runs-on: ubuntu-latest
    steps:
      - name: Repository klonen
        uses: actions/checkout@v3

      - name: PHP einrichten
        uses: shivammathur/setup-php@v2
        with:
          php-version: '8.1'
          extensions: mbstring, pdo_mysql, bcmath, zip

      - name: Composer-Cache wiederverwenden
        uses: actions/cache@v3
        with:
          path: vendor
          key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
          restore-keys: |
            ${{ runner.os }}-composer-

      - name: Abhängigkeiten installieren
        run: composer install --no-dev --optimize-autoloader

      - name: Tests ausführen
        run: php artisan test --parallel

      - name: Docker-Image bauen und pushen
        uses: docker/build-push-action@v3
        with:
          context: .
          push: true
          tags: ${{ secrets.DOCKER_REGISTRY }}/laravel_app:${{ github.sha }}

  deploy:
    needs: build-and-test
    runs-on: ubuntu-latest
    if: github.ref == 'refs/heads/main'
    steps:
      - name: SSH-Verbindung zum Server
        uses: appleboy/ssh-action@v0.1.7
        with:
          host: ${{ secrets.SERVER_HOST }}
          username: ${{ secrets.SERVER_USER }}
          key: ${{ secrets.SERVER_SSH_KEY }}
          script: |
            docker pull ${{ secrets.DOCKER_REGISTRY }}/laravel_app:${{ github.sha }}
            docker-compose -f docker-compose.prod.yml up -d --remove-orphans

Erklärungen:

  1. Wir klonen das Repo und richten PHP in Version 8.1 mit den benötigten Extensions ein.
  2. Composer-Cache verringert Installationszeiten.
  3. Nach composer install laufen deine Laravel-Tests parallel.
  4. Das Docker-Image wird in deine Registry gepusht – nutze dafür DOCKER_REGISTRY als Secret.
  5. Im Deploy-Job verbindet sich die Action per SSH mit deinem Server (Credentials als GitHub Secrets) und führt docker-compose im Produktionsmodus aus.

So baust und verteilst du deine Laravel-App in Docker-Containern automatisiert und sicher – bei jedem Merge in den Hauptzweig.

Best Practices für Laravel Docker Deployment

Um deine Laravel-App unter Docker stabil, performant und sicher zu betreiben, solltest du folgende Empfehlungen befolgen:

  • Multi-Stage-Build einsetzen
    Trenne Build- und Runtime-Umgebung, um nur die nötigen Artefakte im finalen Image zu haben und die Größe zu reduzieren.

  • Minimal-Basisimages verwenden
    Nutze schlanke Varianten wie php:8.1-fpm-alpine oder nginx:alpine, um Angriffsfläche und Image-Größe zu minimieren.

  • Non-Root-User & Dateiberechtigungen
    Lege im Dockerfile einen unprivilegierten User an (z. B. www-data) und setze mit chown/chmod korrekte Rechte auf storage und bootstrap/cache.

  • Healthchecks definieren
    Füge in docker-compose.yml einen Healthcheck (z. B. CMD curl --fail http://localhost/health) hinzu, damit Orchestratoren Container-Zustände prüfen können.

  • Ressourcenlimits & Restart-Policy
    Definiere in Compose mem_limit, cpus und restart: unless-stopped, damit Container sich nach Fehlern selbst neu starten und nicht unbegrenzt Ressourcen verbrauchen.

  • Secrets & Umgebungsvariablen sicher verwalten
    Lade Konfigurationswerte per env_file, Docker Secrets oder externe Vault-Systeme, halte deine .env aus dem Git-Repo fern und versioniere nur .env.example.

  • Laravel-Konfiguration & Routen cachen
    Baue in dein Deployment-Skript php artisan config:cache, route:cache und view:cache ein, um Boot- und Routing-Zeiten deutlich zu verkürzen.

  • OPCache & Autoloader optimieren
    Aktiviere OPCache mit produktiven Settings (validate_timestamps=0) und verwende composer install --optimize-autoloader für schnellere Klassenladung.

  • Automatische Datenbank-Migrationen
    Integriere php artisan migrate --force in deine CI/CD-Pipeline oder als Entry-Point, damit Schema-Änderungen konsistent und ohne manuellen Eingriff ausgerollt werden.

  • Netzwerk-Isolation & Firewall-Regeln
    Platziere App-, Web- und DB-Container in separaten Docker-Netzwerken, beschränke direkte Datenbankzugriffe und definiere klare Firewall-Policies.

  • Logging, Monitoring & Sicherheits-Updates
    Leite Logs an Systeme wie ELK oder Papertrail weiter, überwache Container mit Prometheus/cAdvisor und scanne Images regelmäßig auf Schwachstellen.

Mit diesen Best Practices stellst du sicher, dass deine Laravel-App in Docker nicht nur performant läuft, sondern auch wartbar, sicher und skalierbar bleibt.

Fehlerbehebung & FAQ zu Laravel Docker Deployment

Häufige Probleme

  1. 502 Bad Gateway von Nginx
    Ursache: PHP-FPM-Container ist nicht erreichbar.
    Lösung: Prüfe Logs mit

    docker-compose logs -f webserver
    docker-compose logs -f app
    

    und stell sicher, dass fastcgi_pass app:9000 in deiner Nginx-Config stimmt.

  2. Datenbank-Verbindung schlägt fehl
    Ursache: Falsche Credentials oder Netzwerk-Setup.
    Lösung:

    • Kontrolliere .env-Werte (DB_HOST=db).
    • Starte docker-compose up -d db und teste mit
      docker exec -it laravel_db mysql -u root -p${DB_ROOT_PASSWORD}
    
  3. Dateiberechtigungen für Storage/Cache
    Ursache: Container-User hat keinen Schreibzugriff.
    Lösung:

    # im Dockerfile
    RUN chown -R www-data:www-data /var/www/html/storage /var/www/html/bootstrap/cache
    USER www-data
    
  4. .env wird nicht geladen
    Ursache: env_file fehlt oder Pfad stimmt nicht.
    Lösung: Füge in docker-compose.yml

    env_file:
      - .env
    

    beim app-Service hinzu und baue neu.

  5. Memory exhausted bei Composer
    Ursache: Standard-PHP-Limit zu gering.
    Lösung: Erhöhe Memory-Limit im Dockerfile:
    dockerfile
    RUN echo "memory_limit=1G" >> /usr/local/etc/php/conf.d/memory.ini

Häufig gestellte Fragen

  • Wie beobachte ich Logs aller Dienste?
    Nutze
  docker-compose logs -f

um alle Container-Logs in Echtzeit zu sehen.

  • Wie aktualisiere ich das Image ohne Downtime?
    Setze docker-compose up -d --no-deps --build webserver app ein und nutze Healthchecks für Zero-Downtime-Rollouts.

  • Kann ich Laravel Horizon in Docker betreiben?
    Ja. Füge einfach einen horizon-Service im Compose-File hinzu, ähnlich dem queue-Worker, und setze command: php artisan horizon.

  • Wie stelle ich Cron-Jobs in Docker bereit?
    Nutze einen separaten cron-Service mit command: crond -f und mounte deine crontab-Datei per Volume.

Mit diesen Tipps findest du Fehler schnell, löst typische Hürden und behältst den Überblick über dein Laravel-Docker-Deployment.

Fazit: Starte jetzt dein Laravel Docker Deployment – mit Erfolg!

Mit diesem Leitfaden zum Laravel Docker Deployment hast du alle Schritte – von der Projektstruktur über Umgebungsvariablen bis zur CI/CD-Pipeline – praxisnah umgesetzt. Richte deine Container so ein, dass sie sicher, performant und ausfallsicher laufen, automatisiere Builds und Deployments mit GitHub Actions und optimiere OPCache sowie Queue-Worker für echten Produktionsbetrieb. Starte jetzt dein erstes Deployment, teste deine Pipeline und teile deine Erfahrungen oder Fragen in den Kommentaren, damit wir gemeinsam noch besser werden!

Von admin

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert