Einleitung
Du hast eine großartige Laravel App auf eine EC2 Instanz deployed. Nginx brummt, PHP-FPM ist warm, und dann — wumm — deine Logs explodieren mit:
SQLSTATE[08006] [7] connection to server at "127.0.0.1", port 5432 failed: Connection refusedFATAL: role "ec2-user" does not existSQLSTATE[42501]: Insufficient privilege: permission denied for schema publicClass "Redis" not foundoderConnection refused [tcp://127.0.0.1:6379]
Wenn dir das bekannt vorkommt, ist dieser Leitfaden für dich.
In diesem umfassenden End-to-End Tutorial zeige ich dir, wie du PostgreSQL und Redis lokal auf derselben AWS EC2 Maschine installierst und konfigurierst, die Laravel ausführt — und wie du die häufigsten Fehler diagnostizieren und beheben kannst. Du erhältst eine Blaupause, die du für jedes Projekt wiederverwenden kannst: schlank, sicher und schnell.
Was du am Ende haben wirst:
- Einen funktionierenden PostgreSQL 15 und Redis Stack auf Amazon Linux (oder ähnlich) mit systemd Services und Persistenz.
- Eine gehärtete Laravel .env, die Redis für Cache/Sessions/Queues und PostgreSQL für die Datenbank verwendet.
- Eine schnelle Troubleshooting-Sektion, die Connection Refused, fehlende Rollen, Berechtigungsfehler und PHP Redis Erweiterungsprobleme löst.
- Praktische Performance- und Zuverlässigkeitstipps: Small-Instance Tuning, Log-Checks und Produktionshygiene.
Los geht's.
Was Du Benötigst
- Eine AWS EC2 Instanz (t3.micro oder größer). Amazon Linux 2023 ist perfekt; Befehle entsprechen weitgehend RHEL/CentOS mit
dnf. - Eine Security Group, die eingehenden Verkehr auf 80/443 (HTTP/HTTPS) aus dem Internet und 22 (SSH) von deiner IP erlaubt. Öffne nicht PostgreSQL (5432) oder Redis (6379) zum öffentlichen Internet.
- Eine deployete Laravel Codebase (Nginx + PHP-FPM) mit SSH-Zugang als
ec2-user(oder ähnlich).
Pakete Installieren, die Laravel + DB Stack Benötigen
Die folgenden Befehle sind für Amazon Linux 2023. Für Ubuntu ersetze
dnfdurchapt.
# Immer hier beginnen
sudo dnf -y update
# PHP & gängige Erweiterungen (Versionen bei Bedarf anpassen)
sudo dnf -y install php-cli php-fpm php-common php-opcache php-mbstring php-xml php-curl php-zip php-gd php-intl
# Postgres Client + PHP Erweiterung
sudo dnf -y install postgresql postgresql15 postgresql15-server php-pgsql
# Redis Server + PHP Redis Erweiterung
# Auf Amazon Linux 2023 wird Redis typischerweise als redis6 in amazon-linux-extras bereitgestellt.
sudo dnf -y install redis || true
sudo amazon-linux-extras enable redis6 || true
sudo dnf -y install redis
sudo dnf -y install php-pecl-redis || sudo dnf -y install php-redis
# Git & Tools
sudo dnf -y install git unzip
Warum sowohl postgresql als auch postgresql15*?
Auf AL2023 ist postgresql15 der Server, den du willst. Das generische postgresql Paket bietet Client-Utilities. Wenn postgresql15 nicht gefunden wird, aktiviere das richtige Repo oder verwende die vom OS bereitgestellte PostgreSQL Hauptversion.
Starte PHP-FPM nach der Installation von PHP-Erweiterungen neu:
sudo systemctl restart php-fpm
PostgreSQL 15 Initialisieren und Starten
Initialisiere das Datenverzeichnis als postgres Systembenutzer:
# Cluster initialisieren
sudo -u postgres /usr/bin/initdb -D /var/lib/pgsql/15/data
# (Falls initdb nicht gefunden wird, suche es)
# sudo find / -type f -name initdb 2>/dev/null
# Erstelle einen systemd Service für PG15 falls dein Image die Unit nicht hat:
cat <<'EOF' | sudo tee /etc/systemd/system/postgresql-15.service
[Unit]
Description=PostgreSQL 15 database server
After=network.target
[Service]
Type=notify
User=postgres
ExecStart=/usr/bin/pg_ctl -D /var/lib/pgsql/15/data -l /var/lib/pgsql/15/data/serverlog start
ExecStop=/usr/bin/pg_ctl -D /var/lib/pgsql/15/data stop
ExecReload=/usr/bin/pg_ctl -D /var/lib/pgsql/15/data reload
KillMode=mixed
[Install]
WantedBy=multi-user.target
EOF
sudo systemctl daemon-reload
sudo systemctl enable postgresql-15
sudo systemctl start postgresql-15
sudo systemctl status postgresql-15 --no-pager
Wenn der Status active (running) zeigt, bist du bereit.
Datenbank, Benutzer Erstellen und Lokale Auth Absichern
Öffne die Postgres Shell:
sudo -u postgres psql
Erstelle eine dedizierte Datenbank/Benutzer und vergib Eigentümerschaft (das vermeidet das gefürchtete "permission denied for schema public"):
-- in psql
CREATE DATABASE writeflow;
CREATE USER writeflow_user WITH ENCRYPTED PASSWORD 'StrongPassword123!';
GRANT ALL PRIVILEGES ON DATABASE writeflow TO writeflow_user;
\c writeflow
ALTER SCHEMA public OWNER TO writeflow_user;
-- optional: stelle sicher, dass zukünftige Objekte standardmäßig dem App-Benutzer gehören
ALTER DATABASE writeflow OWNER TO writeflow_user;
Sichere die lokale Authentifizierung ab, um MD5 für lokale TCP-Verbindungen zu verwenden (oder peer/ident wenn du Sockets bevorzugst):
sudo nano /var/lib/pgsql/15/data/pg_hba.conf
Füge hinzu oder stelle sicher, dass diese Zeilen oben stehen:
# TYPE DATABASE USER ADDRESS METHOD
local all all peer
host all all 127.0.0.1/32 md5
host all all ::1/128 md5
Lade PostgreSQL neu:
sudo systemctl restart postgresql-15
Schnelltests:
PGPASSWORD='StrongPassword123!' psql -U writeflow_user -d writeflow -h 127.0.0.1 -c "select now();"
Wenn das einen Zeitstempel zurückgibt, ist deine DB-Authentifizierung korrekt.
Redis Installieren & Starten (und Persistent Machen)
# Installieren (oben bereits erledigt); starten und aktivieren
sudo systemctl enable redis
sudo systemctl start redis
sudo systemctl status redis --no-pager
# Rauchtest
redis-cli ping
# Erwartet: PONG
Wenn dein Image Redis 6 unter einem anderen Unit-Namen liefert (z.B. redis6), passe einfach an:
sudo systemctl enable redis6
sudo systemctl start redis6
sudo systemctl status redis6 --no-pager
Bind + Sicherheit: Für rein lokale Nutzung, binde an 127.0.0.1 und lasse den Protected Mode aktiviert.
sudo sed -i 's/^#\?bind .*/bind 127.0.0.1/' /etc/redis/redis.conf || true
sudo sed -i 's/^protected-mode .*/protected-mode yes/' /etc/redis/redis.conf || true
sudo systemctl restart redis || sudo systemctl restart redis6
Laravels .env Konfigurieren
In deinem App-Root:
nano /var/www/your-app/.env
Verwende diese saubere Basiskonfiguration für DB und Redis:
APP_ENV=production
APP_DEBUG=false
APP_URL=https://your-domain.com
# --- Datenbank (PostgreSQL)
DB_CONNECTION=pgsql
DB_HOST=127.0.0.1
DB_PORT=5432
DB_DATABASE=writeflow
DB_USERNAME=writeflow_user
DB_PASSWORD=
# --- Cache/Session/Queue via Redis
CACHE_DRIVER=redis
SESSION_DRIVER=redis
QUEUE_CONNECTION=redis
REDIS_CLIENT=phpredis
REDIS_HOST=127.0.0.1
REDIS_PASSWORD=null
REDIS_PORT=6379
Wichtig: Stelle sicher, dass PHPs Redis-Erweiterung geladen ist. Nach der Installation von php-pecl-redis nimmt ein Neustart von PHP-FPM sie normalerweise auf:
php -m | grep -i redis # sollte anzeigen: redis
sudo systemctl restart php-fpm
Laravel App Migrieren & Optimieren
Aus deinem App-Verzeichnis:
php artisan key:generate
php artisan config:clear
php artisan cache:clear
php artisan view:clear
# Führe fresh oder standard migrate nach Bedarf aus
php artisan migrate --force
Wenn du permission denied for schema public erhältst, führe die Postgres Grants erneut aus (siehe Abschnitt 3). Stelle außerdem sicher, dass der Datenbankeigentümer dein App-Benutzer ist und nicht postgres.
Jeden Häufigen Fehler Beheben (Troubleshooting-Sammlung)
A) SQLSTATE[08006] [7] connection to server at "127.0.0.1", port 5432 failed: Connection refused
Bedeutung: Der PG Server lauscht nicht, ist abgestürzt oder blockiert.
Fix-Checkliste:
sudo systemctl status postgresql-15 --no-pager
journalctl -xeu postgresql-15 --no-pager
ss -ltnp | grep 5432
- Wenn die Unit inaktiv/fehlgeschlagen ist, prüfe
serverlogin/var/lib/pgsql/15/data/. - Überprüfe
pg_hba.confundpostgresql.conf(listen_addresses). Für lokales TCP stelle sicher, dasslisten_addresses = 'localhost'oder'*'(nur lokal ist in Ordnung). - Stelle sicher, dass deine
.envden korrekten DB-Namen, Benutzer und Passwort verwendet.
B) FATAL: role "ec2-user" does not exist
Bedeutung: Laravel hat versucht, sich mit einem Linux-Benutzernamen zu authentifizieren (passiert oft, wenn .env nicht geladen ist oder Env-Caching veraltet ist).
Fix:
- Überprüfe
.envWerte undphp artisan config:clear. - Bestätige, dass DB-Einstellungen in
config/database.phpenv()verwenden und nicht hartcodierte Fallbacks, die aufec2-userverweisen.
C) SQLSTATE[42501]: Insufficient privilege: permission denied for schema public
Bedeutung: Dein App-Benutzer besitzt das Schema nicht oder hat keine Berechtigungen.
Fix in psql:
\c writeflow
ALTER SCHEMA public OWNER TO writeflow_user;
GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO writeflow_user;
GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA public TO writeflow_user;
ALTER DATABASE writeflow OWNER TO writeflow_user;
D) Class "Redis" not found
Bedeutung: PHP Redis Erweiterung nicht geladen.
Fix:
sudo dnf -y install php-pecl-redis || sudo dnf -y install php-redis
php -m | grep -i redis
sudo systemctl restart php-fpm
Stelle sicher, dass .env REDIS_CLIENT=phpredis verwendet.
E) Redis connection refused [tcp://127.0.0.1:6379]
Bedeutung: Redis Service läuft nicht oder ist an eine andere IP gebunden.
Fix:
sudo systemctl status redis --no-pager || sudo systemctl status redis6 --no-pager
redis-cli ping
ss -ltnp | grep 6379
- Wenn die Unit down ist,
sudo systemctl start redisundsudo systemctl enable redis. - Stelle sicher, dass
bind 127.0.0.1in/etc/redis/redis.confsteht, dann neustarten.
F) migrations table does not exist (obwohl du weißt, dass sie existiert)
Bedeutung: Config-Cache zeigt auf falsche DB, oder .env wird nicht gelesen.
Fix:
php artisan config:clear
php artisan cache:clear
php artisan migrate --force
Performance-Gewinne für Kleine Instanzen
Du brauchst keinen DB-Administrator, um Out-of-Memory Fehler zu vermeiden.
PostgreSQL (auf t3.micro/t3.small):
- Halte
max_connectionsbescheiden (z.B. 100 oder weniger). - Verwende vernünftige Shared Buffers:
shared_buffers = 128MB. - Setze
effective_cache_size = 1GBauf kleinen Maschinen (wenn der Speicher es erlaubt).
PHP-FPM:
- Reduziere
pm.max_childrenpassend zum RAM. Auf kleinen Maschinen reichen 5–10 oft aus.
Redis:
- Halte es rein lokal mit
bind 127.0.0.1und Standard-Persistenz für Sessions/Queues.
Sicherheits- & Zuverlässigkeitshygiene
-
Security Groups: Setze 5432 oder 6379 niemals öffentlich frei. Halte sie nur an 127.0.0.1 gebunden.
-
Backups: Nächtlicher
pg_dumpnach S3:# /usr/local/bin/pg-backup.sh #!/usr/bin/env bash set -e TS=$(date +%F-%H%M) PGPASSWORD='StrongPassword123!' pg_dump -U writeflow_user -h 127.0.0.1 writeflow \ | gzip > /tmp/writeflow-$TS.sql.gz aws s3 cp /tmp/writeflow-$TS.sql.gz s3://your-db-backups/ rm -f /tmp/writeflow-$TS.sql.gzDann per Cron planen:
sudo crontab -e 0 3 * * * /usr/local/bin/pg-backup.sh >> /var/log/pg-backup.log 2>&1 -
Logs:
- PostgreSQL:
/var/lib/pgsql/15/data/serverlog - Redis:
journalctl -xeu redis - Nginx/PHP:
/var/log/nginx/access.log,/var/log/nginx/error.log,journalctl -xeu php-fpm
- PostgreSQL:
End-to-End Rauchtest-Checkliste
sudo systemctl status postgresql-15→ active (running)PGPASSWORD='StrongPassword123!' psql -U writeflow_user -d writeflow -h 127.0.0.1 -c "select version();"→ gibt Version zurücksudo systemctl status redis(oderredis6) → active (running)redis-cli ping→ PONGphp -m | grep -i redis→ redisphp artisan migrate --force→ DONE- Login/Registrierungs-Flows funktionieren; Sessions bestehen; Caches wärmen sich auf.
Zusammenfassung / Schnelle Erkenntnisse
- Setze DB/Redis niemals dem Internet aus. Binde an
127.0.0.1, sichere über Security Groups. - Behebe "role does not exist" indem du sicherstellst, dass
.envgeladen ist und der korrekte DB-Benutzer erstellt wurde. - Behebe "permission denied for schema public" durch Übertragung der Schema/Datenbank-Eigentümerschaft auf deinen App-Benutzer.
- Behebe Redis "Class not found" durch Installation von
php-pecl-redisund Neustart von PHP-FPM. - Behebe Redis "Connection refused" durch Aktivieren und Starten des Redis Services und Bestätigung, dass er auf 127.0.0.1:6379 lauscht.
- Sichere Zuverlässigkeit: Prüfe mit
systemctlundjournalctl, und sichere nächtlich nach S3.
Handlungsaufruf (CTA)
Willst du eine bewährte Deployment-Checkliste (Nginx, PHP-FPM, SSL, Postgres, Redis), die du in dein Terminal einfügen kannst? Brauchst du das schnell und richtig? 👉 Kaufe meinen Fiverr Gig: https://www.fiverr.com/s/DBjDz4a
FAQ (Häufig Gestellte Fragen)
F1) Soll ich lokales PostgreSQL/Redis oder verwaltete Dienste (RDS/ElastiCache) verwenden? Wenn du in einer frühen Phase bist oder kostenbewusst, ist lokal schlank und schnell aufzusetzen. Wenn dein Traffic wächst oder du Multi-AZ Failover brauchst, wechsle zu RDS + ElastiCache für verwaltete Zuverlässigkeit und Skalierung.
F2) Muss ich Port 5432 oder 6379 extern öffnen? Nein. Deine Laravel App verbindet sich lokal. Halte beide für das Internet geschlossen und an 127.0.0.1 gebunden.
F3) Wie behebe ich "SQLSTATE[08006] connection refused", obwohl PostgreSQL läuft?
Überprüfe, dass es auf 127.0.0.1:5432 lauscht (ss -ltnp | grep 5432), bestätige .env Einstellungen und prüfe pg_hba.conf auf lokale md5 Regeln. Führe abschließend php artisan config:clear aus, um veralteten Env-Cache zu löschen.
F4) Redis läuft, aber Sessions scheitern trotzdem — warum?
Bestätige, dass php -m | grep -i redis die Erweiterung anzeigt. Stelle sicher, dass .env REDIS_CLIENT=phpredis hat und starte php-fpm neu. Überprüfe auch, ob storage/framework/sessions nicht versehentlich verwendet wird (prüfe SESSION_DRIVER).
F5) Wie vermeide ich "permission denied for schema public" bei neuen Tabellen? Nach dem Erstellen der DB, setze den Eigentümer auf deinen App-Benutzer und führe aus:
ALTER SCHEMA public OWNER TO writeflow_user;
GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO writeflow_user;
GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA public TO writeflow_user;
Das behebt sowohl aktuelle als auch zukünftige Objekte.
F6) Was ist mit SSL/TLS zur Datenbank? Für Same-Host Verbindungen (127.0.0.1) ist SSL nicht notwendig. Wenn du die DB später auf eine separate Instanz oder RDS verschiebst, aktiviere SSL und beschränke Verbindungen über Security Groups.
Schlusswort
Du brauchst kein riesiges DevOps-Budget, um schnelle, stabile Laravel Apps auf AWS zu betreiben. Mit diesem Leitfaden hast du:
- PostgreSQL 15 + Redis lokal aufgesetzt,
- Laravel für Performance verdrahtet (Redis Cache/Sessions/Queues),
- Die frustrierenden Fehler gelöst, die die meisten Teams am ersten Tag blockieren.
Speichere diesen Leitfaden. Verwende ihn beim nächsten Launch wieder. Und wenn du bereit bist zu skalieren, bist du bereits produktionsreif.