# Revisão do Container Docker (Agosto/2026)

## Resumo Executivo

Esta revisão documenta a stack de containers do projeto, verificada em **2026-08-04** via inspeção estática (`docker-compose.yml`, `docker/*`, `.env.example`, arquivos de configuração) e checagens em runtime dentro dos containers ativos (`docker compose exec`). Nenhum arquivo foi modificado durante a revisão; este documento apenas registra o estado atual da infraestrutura.

A stack ativa é composta por quatro serviços na rede `portal_sucesu_network` (projeto `portal_sucesu`): `app` (Apache + PHP-FPM), `postgres`, `redis` e `mailhog`. Todos os containers estão Up e saudáveis no momento da verificação. O stack é funcional e possui boas práticas de segurança e persistência, porém apresenta fragilidades relevantes — em especial a falta de `.dockerignore`, a imagem `app` não pinada por digest, um bind mount externo órfão, OPcache desabilitado em produção e a inexistência de um endpoint `/health` real. Diversas stacks de container mortas (`docker/nginx`, `docker/php`, `docker/recomends/`) continuam presentes no repositório e a documentação (`wiki/v2/infraestrutura.md`) reflete a stack antiga, não a ativa. As recomendações priorizadas ao final endereçam esses pontos em ordem de esforço/impacto.

## Inventário da Stack

| Serviço | Imagem | Função | Porta Host | Healthcheck |
|---------|--------|--------|------------|-------------|
| **app** (`portal_sucesu_app`) | build local de `docker/apache/Dockerfile` (base `php:7.4.33-fpm-bullseye`, sem digest) | Apache 2.4 (mpm_event + mod_http2/proxy_fcgi) + PHP-FPM, gerenciados por supervisord | `0.0.0.0:10011 → 80`, `0.0.0.0:10443 → 443` | `curl -sf http://localhost/` |
| **postgres** (`portal_sucesu_database`) | `postgres:10.23-alpine3.16@sha256:0ef5...` (pinada por digest) | Banco de dados PostgreSQL 10.23 | — (interno) | `pg_isready` |
| **redis** (`portal_sucesu_redis`) | `redis:6-alpine3.21@sha256:ec5e...` (pinada por digest) | Cache/armazenamento Redis 6 | — (interno) | `redis-cli ping` |
| **mailhog** (`portal_sucesu_mailhog`) | `mailhog/mailhog:latest` (sem pin) | Captura de e-mail em dev (SMTP) | `0.0.0.0:1025` (SMTP), `0.0.0.0:8025` (UI) | — (logging driver `none`) |

### Detalhes do serviço `app`

- **Runtime:** PHP 7.4.33 (FPM), Apache 2.4 com `mpm_event` + `mod_http2`/`ssl`/`proxy_fcgi`, proxying `.php` para PHP-FPM em `127.0.0.1:9000`; ambos gerenciados por supervisord com autorestart.
- **Rootless:** roda como `www-data` (uid/gid 1000 via ARG `WWWUSER`/`WWWGROUP`), com `setcap cap_net_bind_service` para as portas 80/443 e `APACHE_RUN_DIR`/`PID_FILE`/`LOCK_DIR` movidos para `/tmp`.
- **SSL:** cert auto-assinado gerado em build time (`openssl req`, 10 anos, CN=localhost) em `/etc/apache2/ssl`; vhost `ssl.conf` na porta 443 com `Protocols h2 h2c http/1.1`.
- **DocumentRoot:** `/var/www/html/public`.
- **Volumes:** `.:/var/www/html` (projeto inteiro bind-mounted) e `../public-v5v6-vendor/:/var/www/public-v5v6-vendor` (fora do repositório).
- **Env (compose):** `APP_ENV` default `prod`, `APP_DEBUG` default `false`, `APP_KEY` obrigatória, `DB_*` (host `postgres`, db `sucesu`), `PHP_MEMORY_LIMIT: ${PHP_MEMORY_LIMIT:-512M}`, `REDIS_HOST: redis`.
- **Dependências:** `depends_on` em `postgres` (`condition: service_healthy`) e `redis` (`service_started`). `restart: unless-stopped`.
- **Atenção:** o comentário no compose citando `mod_php (php7apache2)` está **desatualizado** — a stack real é mpm_event + proxy_fcgi + PHP-FPM.

### Stacks inativas / mortas (não referenciadas pelo `docker-compose.yml`)

- `docker/nginx/` — stack nginx + php-fpm (Dockerfile com digest pinado, `nginx.conf`, `lotep.conf.template`); referencia serviço `php:9000`, zona de cache `SELO_CACHE`, upstream `php-fpm`, rate limits de login/api, ACME, etc.
- `docker/php/` — apenas php-fpm (Dockerfile pinado por digest, `php.ini` extenso, `www.conf` para usuário `www` uid 1002).
- `docker/recomends/nginx/` — config de produção aspiracional (`nginx.conf` + `sucesu.conf`): HTTP/3 QUIC, brotli, early hints, fastcgi cache; referencia `php:9000`, PHP 7.2, endpoint `/api/v1/selo-operador`, ACME. Não conectada a nada.
- A página `wiki/v2/infraestrutura.md` documenta a **stack antiga** nginx/php e comandos como `docker compose exec php ...` — o nome atual do serviço é `app` (correção já presente no `AGENTS.md`). Essa divergência está detalhada na seção [Divergências entre Documentação e Realidade](#divergências-entre-documentação-e-realidade).

## Pontos Fortes

1. **Reprodutibilidade parcial:** `postgres` e `redis` estão pinados por SHA-256 digest, garantindo imagens reproduzíveis nesses serviços.
2. **Healthchecks reais e ordem de subida:** os três serviços críticos (app com `curl`, postgres com `pg_isready`, redis com `redis-cli ping`) possuem healthcheck e o app sobe via `depends_on: condition: service_healthy`; todos verificados saudáveis em runtime.
3. **Hardening do PHP:** `allow_url_fopen = Off`, `allow_url_include = Off`, `display_errors = Off`, `expose_php = Off`, `enable_dl = Off`, `disable_functions` com `pcntl_*`, `ServerTokens Prod` / `ServerSignature Off` no Apache, e cookies de sessão com `http_only` + `secure` default true + `same_site = lax` (config/session.php).
4. **Stack Apache moderna:** `mpm_event` (não o legado mod_php) + `mod_http2` + `proxy_fcgi` → PHP-FPM, rootless `www-data` (uid 1000) com `setcap` para portas 80/443, supervisord com autorestart para ambos os processos.
5. **HTTPS imediato em dev:** cert auto-assinado gerado em build time, vhost `ssl.conf` com h2/h2c, portas 80 e 443 mapeadas.
6. **Persistência e resiliência:** volumes nomeados para dados de PG e Redis + `restart: unless-stopped` em todos os serviços.
7. **Tuning do PHP presente:** `realpath_cache` 4096K/600s, `max_input_vars` 5000, upload 100M / post 105M, `session.gc_maxlifetime` 2880, timezone `America/Sao_Paulo`.
8. **MailHog isolado:** `logging driver none` (sem poluição de logs) e captura dedicada de SMTP em dev.
9. **Stack verificada em runtime:** PHP 7.4.33 com todas as extensões necessárias (`pdo_pgsql`, `redis`, `imagick`, `gd`, `intl`, `sodium`...), PG 10.23, Redis respondendo PONG.
10. **Padrão de store Redis explícito:** `PageCacheMiddleware` e `DashboardStatsService` usam `Cache::store('redis')` de forma deliberada, mantendo o cache de página no Redis mesmo com o driver default `file`.

## Pontos Fracos

| Severidade | Item | Evidência | Impacto |
|-----------|------|-----------|---------|
| 🔴 Crítico | OPcache desabilitado | `opcache.enable=0` no `sucesu-recomends.ini`, apesar de compilado e com tuning configurado (`memory_consumption=128`, `max_accelerated_files=16229`, `revalidate_freq=2`) | Todo bootstrap PHP-FPM sem opcache; perda de performance em produção |
| 🔴 Crítico | Sem `.dockerignore` | Arquivo inexistente; build envia o projeto inteiro (vendor/, .git/, storage/, conversations/ ~7.8MB+) | Builds lentos e risco de vazar arquivos/segredos para o daemon e camadas da imagem |
| 🔴 Crítico | Bind mount externo `../public-v5v6-vendor/` | Fora do repo, vazio e root-owned; quebra o `up` para outro dev (cria dir vazio); nenhuma referência no código | Resíduo a remover; quebra de ambiente e mount órfão |
| 🔴 Crítico | Imagem `app` não pinada + `mailhog:latest` sem pin | `FROM php:7.4.33-fpm-bullseye` sem digest; `mailhog/mailhog:latest` (upstream descontinuado desde ~2021) | Builds não-reprodutíveis e risco de supply chain |
| 🔴 Crítico | Sem endpoint `/health` real | Rota inexistente em todo o projeto; healthcheck apenas faz `curl` na homepage | Sem sonda de liveness/readiness para LB/monitoramento; healthcheck não valida DB/Redis |
| 🟠 Alto | `memory_limit` inefetivo | `PHP_MEMORY_LIMIT` (512M) declarada no compose é usada só no `docker/php/php.ini` (stack morta); o stack ativo hardcoda `memory_limit = 256M` no `sucesu-recomends.ini` | Config morta/documentada ≠ efetiva; memória real abaixo do previsto |
| 🟠 Alto | Drivers default file/sync | `SESSION_DRIVER=file`, `CACHE_DRIVER=file`, `QUEUE_DRIVER=sync`; `.env.example` ainda tem `REDIS_HOST=127.0.0.1` (divergente do compose) | Sessões voláteis em redeploy/escala; Redis subutilizado |
| 🟠 Alto | Sem scheduler/cron e sem worker de fila | `Kernel.php` sem `schedule()`, sem crontab no container, supervisord só roda apache+fpm | Tarefas agendadas (publicação, invalidação de cache, RSS, auditoria de e-mail) não executam |
| 🟠 Alto | Várias stacks convivendo | Apache ativa; `docker/nginx`, `docker/php`, `docker/recomends/` mortas; `infraestrutura.md` documenta a stack morta e comandos `docker compose exec php` | Manutenção confusa e documentação divergente |
| 🟠 Alto | Portas publicadas em `0.0.0.0` | 10011, 10443, 1025, 8025 | Serviços expostos em todas as interfaces de rede |
| 🟠 Alto | `APP_ENV` inconsistente | `prod` (compose), `production` (build arg), `dev` (.env.example) | Lógica que chaveie por `APP_ENV` pode quebrar |
| 🟡 Médio | Comentários/arquivos enganosos | compose diz "mod_php"; ini com typo "recomends"; `error_log = "error_log"` (relativo); `error_reporting = E_ALL & ~E_WARNING` (warnings ocultos); entrypoint.sh no-op (só `exec "$@"`) | Confusão de manutenção e warnings não diagnosticáveis |
| 🟡 Médio | Sem limites de recursos e rotação de logs | Sem `mem_limit`/`cpus`/`stop_grace_period`; driver json padrão sem rotação para app/postgres/redis | Consumo descontrolado de recursos e logs ilimitados |
| 🟡 Médio | Imagens EOL | PG 10.23, Redis 6 e Alpine 3.16 fora de suporte (sem atualizações de segurança); pin por digest mantém funcionando | Risco de supply chain (contexto: Laravel 5.5/PHP 7.4 também EOL — decisão conhecida do projeto) |
| ⚪ Baixo | Cert SSL auto-assinado de 10 anos embutido na imagem em build time | Sem mecanismo de rotação | Expiração silenciosa do cert |
| ⚪ Baixo | `supervisord` como PID 1 e imagem dev-centric | Sem `init: true`; sem `composer install` na imagem (depende do bind mount) | Orquestração de sinais e portabilidade limitadas |

## Recomendações Priorizadas

**Quick wins:**
- Adicionar `.dockerignore` (vendor, .git, storage, node_modules, conversations).
- Remover o bind mount `public-v5v6-vendor`.
- Corrigir o comentário "mod_php" no compose e o typo do arquivo ini.
- Habilitar OPcache em produção, deixando configurável via env (ex. `OPCACHE_ENABLE`).
- Fixar digest da imagem `app` e substituir `mailhog` por imagem pinada (ou Mailpit).

**Curto prazo:**
- Criar rota `/health` real (checagem leve de DB/Redis) e apontar o healthcheck do compose para ela.
- Padronizar `APP_ENV`.
- Alinhar `.env.example` com o compose (`REDIS_HOST=redis`) e decidir os drivers — recomenda-se session/cache em redis, queue database/redis se houver jobs.

**Médio prazo:**
- Remover as stacks mortas (`docker/nginx`, `docker/php`, `docker/recomends/`) ou uni-las num único fluxo dev/prod.
- Adicionar serviço de scheduler/worker se houver tarefas agendadas.
- Definir limites de recursos e rotação de logs.
- Plano de upgrade de PG/Redis (EOL).

**Manter:** o padrão de store Redis explícito (`PageCacheMiddleware`) documentado.

## Divergências entre Documentação e Realidade

| Fonte | Afirma | Realidade |
|-------|--------|-----------|
| `wiki/v2/infraestrutura.md` | Documenta stack nginx/php e comandos `docker compose exec php ...` | Stack ativa é Apache (`app`); serviço correto é `app` |
| `wiki/v2/infraestrutura.md` e `AGENTS.md` | Mencionam rota `/health` (GET → 200 "healthy\n") | Rota não existe; healthcheck só faz `curl` na homepage |
| Comentário no `docker-compose.yml` | "mod_php (php7apache2)" | Stack real é mpm_event + proxy_fcgi + PHP-FPM |

## Comandos de Verificação

```bash
# Status de todos os serviços
docker compose ps -a

# Listar as imagens resolvidas pela stack
docker compose config --images

# Configuração efetiva do PHP no serviço app (memória, opcache, limites)
docker compose exec app php -i | grep -Ei 'memory_limit|opcache.enable|post_max_size|upload_max_filesize|display_errors|expose_php'

# Saúde de banco e cache
docker compose exec postgres pg_isready && docker compose exec redis redis-cli ping

# Logs recentes do serviço app
docker compose logs --tail=50 app
```

---

[Voltar para o Índice](index.md)
