Introdução
Você fez deploy de uma ótima app Laravel em uma instância EC2. Nginx está rodando, PHP-FPM está quente, e então — bam! — seus logs explodem com:
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 foundouConnection refused [tcp://127.0.0.1:6379]
Se isso parece familiar, este guia é para você.
Neste tutorial premium de ponta a ponta, vou mostrar como instalar e configurar PostgreSQL e Redis localmente na mesma máquina AWS EC2 que executa o Laravel — e como diagnosticar e corrigir os erros mais comuns que você enfrentará no caminho. Você terá um blueprint que pode repetir para cada projeto: enxuto, seguro e rápido.
O que você terá no final:
- Um stack PostgreSQL 15 e Redis funcionando no Amazon Linux (ou similar) com serviços systemd e persistência.
- Um Laravel .env reforçado que usa Redis para cache/sessões/filas e PostgreSQL para o banco de dados.
- Uma seção rápida de troubleshooting que resolve connection refused, roles ausentes, erros de permissão e problemas com a extensão PHP Redis.
- Dicas práticas de performance e confiabilidade: tuning de instâncias pequenas, verificação de logs e higiene de produção.
Vamos lá.
O Que Você Vai Precisar
- Uma instância AWS EC2 (t3.micro ou maior). Amazon Linux 2023 é perfeito; os comandos correspondem de perto ao RHEL/CentOS com
dnf. - Um security group permitindo tráfego de entrada em 80/443 (HTTP/HTTPS) da internet e 22 (SSH) do seu IP. Não abra PostgreSQL (5432) ou Redis (6379) para a internet pública.
- Uma codebase Laravel deployada (Nginx + PHP-FPM) com acesso SSH como
ec2-user(ou similar).
Instalar Pacotes Necessários pelo Laravel + Stack de DB
Os comandos abaixo são para Amazon Linux 2023. Para Ubuntu, substitua
dnfporapt.
# Sempre comece aqui
sudo dnf -y update
# PHP & extensões comuns (ajuste versões se necessário)
sudo dnf -y install php-cli php-fpm php-common php-opcache php-mbstring php-xml php-curl php-zip php-gd php-intl
# Cliente Postgres + extensão PHP
sudo dnf -y install postgresql postgresql15 postgresql15-server php-pgsql
# Servidor Redis + extensão PHP Redis
# No Amazon Linux 2023, redis é tipicamente fornecido como redis6 no amazon-linux-extras.
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 & ferramentas
sudo dnf -y install git unzip
Por que tanto postgresql quanto postgresql15*?
No AL2023, postgresql15 é o servidor que você quer. O pacote genérico postgresql fornece utilitários de cliente. Se postgresql15 não for encontrado, habilite o repositório correto ou use a versão principal do PostgreSQL fornecida pelo SO.
Reinicie o PHP-FPM após instalar extensões PHP:
sudo systemctl restart php-fpm
Inicializar e Iniciar o PostgreSQL 15
Inicialize o diretório de dados como o usuário de sistema postgres:
# Inicializar cluster
sudo -u postgres /usr/bin/initdb -D /var/lib/pgsql/15/data
# (Se initdb não for encontrado, localize-o)
# sudo find / -type f -name initdb 2>/dev/null
# Crie um serviço systemd para PG15 se sua imagem não tiver a unidade:
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
Se o status mostrar active (running), você está pronto.
Criar Banco de Dados, Usuário e Proteger Auth Local
Entre no shell do Postgres:
sudo -u postgres psql
Crie um banco de dados/usuário dedicado e conceda propriedade (isso evita o temido "permission denied for schema public"):
-- dentro do 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;
-- opcional: garanta que objetos futuros pertençam ao usuário da app
ALTER DATABASE writeflow OWNER TO writeflow_user;
Reforce a autenticação local para usar MD5 para conexões TCP locais (ou peer/ident se preferir sockets):
sudo nano /var/lib/pgsql/15/data/pg_hba.conf
Adicione ou garanta que estas linhas estejam perto do topo:
# TYPE DATABASE USER ADDRESS METHOD
local all all peer
host all all 127.0.0.1/32 md5
host all all ::1/128 md5
Recarregue o PostgreSQL:
sudo systemctl restart postgresql-15
Testes rápidos:
PGPASSWORD='StrongPassword123!' psql -U writeflow_user -d writeflow -h 127.0.0.1 -c "select now();"
Se isso retornar um timestamp, sua autenticação de DB está correta.
Instalar e Iniciar o Redis (e Torná-lo Persistente)
# Instalar (já feito acima); iniciar e habilitar
sudo systemctl enable redis
sudo systemctl start redis
sudo systemctl status redis --no-pager
# Teste de fumaça
redis-cli ping
# Esperado: PONG
Se sua imagem traz Redis 6 com um nome de unidade diferente (ex. redis6), simplesmente adapte:
sudo systemctl enable redis6
sudo systemctl start redis6
sudo systemctl status redis6 --no-pager
Bind + Segurança: Para uso apenas local, vincule a 127.0.0.1 e mantenha o modo protegido ativado.
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
Configurar o .env do Laravel
Na raiz da sua app:
nano /var/www/your-app/.env
Use esta configuração base limpa para DB e Redis:
APP_ENV=production
APP_DEBUG=false
APP_URL=https://your-domain.com
# --- Banco de Dados (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
Importante: garanta que a extensão Redis do PHP esteja carregada. Após instalar php-pecl-redis, um restart do PHP-FPM geralmente a detecta:
php -m | grep -i redis # deve mostrar: redis
sudo systemctl restart php-fpm
Migrar e Otimizar a App Laravel
Do seu diretório da app:
php artisan key:generate
php artisan config:clear
php artisan cache:clear
php artisan view:clear
# Execute fresh ou standard migrate conforme necessário
php artisan migrate --force
Se você receber permission denied for schema public, execute novamente os grants do Postgres (veja Seção 3). Também garanta que o proprietário do banco de dados seja seu usuário da app e não postgres.
Corrigir Cada Erro Comum (Cofre de Troubleshooting)
A) SQLSTATE[08006] [7] connection to server at "127.0.0.1", port 5432 failed: Connection refused
Significado: O servidor PG não está ouvindo, travou ou está bloqueado.
Lista de verificação:
sudo systemctl status postgresql-15 --no-pager
journalctl -xeu postgresql-15 --no-pager
ss -ltnp | grep 5432
- Se a unidade está inativa/falhou, verifique
serverlogem/var/lib/pgsql/15/data/. - Verifique
pg_hba.confepostgresql.conf(listen_addresses). Para TCP local, garantalisten_addresses = 'localhost'ou'*'(apenas local está ok). - Garanta que seu
.envuse o nome de DB, usuário e senha corretos.
B) FATAL: role "ec2-user" does not exist
Significado: Laravel tentou autenticar com um nome de usuário Linux (acontece frequentemente se .env não está carregado ou o cache de env está obsoleto).
Fix:
- Verifique os valores do
.envephp artisan config:clear. - Confirme que as credenciais de DB em
config/database.phpusemenv()e não fallbacks hardcoded que apontem paraec2-user.
C) SQLSTATE[42501]: Insufficient privilege: permission denied for schema public
Significado: Seu usuário da app não é proprietário do schema ou não tem permissões.
Fix no 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
Significado: Extensão PHP Redis não carregada.
Fix:
sudo dnf -y install php-pecl-redis || sudo dnf -y install php-redis
php -m | grep -i redis
sudo systemctl restart php-fpm
Garanta que .env use REDIS_CLIENT=phpredis.
E) Redis connection refused [tcp://127.0.0.1:6379]
Significado: O serviço Redis não está rodando ou está vinculado a outro IP.
Fix:
sudo systemctl status redis --no-pager || sudo systemctl status redis6 --no-pager
redis-cli ping
ss -ltnp | grep 6379
- Se a unidade está parada,
sudo systemctl start redisesudo systemctl enable redis. - Garanta
bind 127.0.0.1em/etc/redis/redis.conf, então reinicie.
F) migrations table does not exist (quando você sabe que existe)
Significado: Cache de config apontando para DB errado, ou .env não está sendo lido.
Fix:
php artisan config:clear
php artisan cache:clear
php artisan migrate --force
Ganhos de Performance para Instâncias Pequenas
Você não precisa de um DBA para evitar erros de falta de memória.
PostgreSQL (em t3.micro/t3.small):
- Mantenha
max_connectionsmodesto (ex. 100 ou menos). - Use shared buffers sensatos:
shared_buffers = 128MB. - Defina
effective_cache_size = 1GBem máquinas pequenas (se a memória permitir).
PHP-FPM:
- Reduza
pm.max_childrenpara corresponder à RAM. Em máquinas pequenas, 5–10 geralmente é suficiente.
Redis:
- Mantenha apenas local com
bind 127.0.0.1e persistência padrão para sessões/filas.
Higiene de Segurança e Confiabilidade
-
Security Groups: Nunca exponha 5432 ou 6379 publicamente. Mantenha ambos vinculados apenas a 127.0.0.1.
-
Backups:
pg_dumpnoturno para 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.gzEntão agende com cron:
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:
Checklist de Teste de Fumaça End-to-End
sudo systemctl status postgresql-15→ active (running)PGPASSWORD='StrongPassword123!' psql -U writeflow_user -d writeflow -h 127.0.0.1 -c "select version();"→ retorna versãosudo systemctl status redis(ouredis6) → active (running)redis-cli ping→ PONGphp -m | grep -i redis→ redisphp artisan migrate --force→ DONE- Fluxos de login/registro funcionam; sessões persistem; caches aquecem.
Pontos-Chave / Resumo Rápido
- Nunca exponha DB/Redis à internet. Vincule a
127.0.0.1, proteja via security groups. - Corrija "role does not exist" garantindo que
.envesteja carregado e o usuário de DB correto esteja criado. - Corrija "permission denied for schema public" transferindo a propriedade do schema/banco de dados para seu usuário da app.
- Corrija Redis "Class not found" instalando
php-pecl-redise reiniciando PHP-FPM. - Corrija Redis "Connection refused" habilitando e iniciando o serviço Redis, e confirmando que ele ouve em 127.0.0.1:6379.
- Garanta confiabilidade: verifique com
systemctlejournalctl, e faça backup noturno para S3.
Chamada para Ação (CTA)
Quer uma checklist de deployment testada em batalha (Nginx, PHP-FPM, SSL, Postgres, Redis) que você possa colar no seu terminal? Precisa disso feito certo — rápido? 👉 Compre meu gig no Fiverr: https://www.fiverr.com/s/DBjDz4a
FAQ (Perguntas Frequentes)
P1) Devo usar PostgreSQL/Redis local ou serviços gerenciados (RDS/ElastiCache)? Se você está em estágio inicial ou é consciente dos custos, local é enxuto e rápido de configurar. À medida que seu tráfego cresce ou você precisa de failover multi-AZ, migre para RDS + ElastiCache para confiabilidade e escala gerenciadas.
P2) Preciso abrir a porta 5432 ou 6379 externamente? Não. Sua app Laravel conecta localmente. Mantenha ambas fechadas para a internet e vinculadas a 127.0.0.1.
P3) Como corrijo "SQLSTATE[08006] connection refused" mesmo com PostgreSQL rodando?
Verifique que está ouvindo em 127.0.0.1:5432 (ss -ltnp | grep 5432), confirme credenciais do .env, e verifique pg_hba.conf para regras md5 locais. Por fim, php artisan config:clear para limpar cache de env obsoleto.
P4) Redis está rodando mas as sessões ainda falham — por quê?
Confirme que php -m | grep -i redis mostra a extensão. Garanta que .env tenha REDIS_CLIENT=phpredis e reinicie php-fpm. Também verifique se storage/framework/sessions não está sendo usado por engano (verifique SESSION_DRIVER).
P5) Como evito "permission denied for schema public" em novas tabelas? Após criar o DB, defina o proprietário como seu usuário da app e execute:
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;
Isso corrige tanto objetos atuais quanto futuros.
P6) E quanto a SSL/TLS para o banco de dados? Para conexões no mesmo host (127.0.0.1), SSL não é necessário. Se você posteriormente mover o DB para uma instância separada ou RDS, habilite SSL e restrinja conexões via security groups.
Palavra Final
Você não precisa de um orçamento DevOps enorme para rodar apps Laravel rápidas e estáveis na AWS. Com este guia você:
- Configurou PostgreSQL 15 + Redis localmente,
- Conectou o Laravel para performance (Redis cache/sessões/filas),
- Resolveu os erros frustrantes que bloqueiam a maioria das equipes no primeiro dia.
Salve este guia. Reutilize-o no próximo lançamento. E quando estiver pronto para escalar, você já estará pronto para produção.