diff --git a/.env.example b/.env.example
index 92355a4..0bb9e72 100644
--- a/.env.example
+++ b/.env.example
@@ -4,18 +4,19 @@
# ============================================
# Demo Mode (optional)
# Set to 'true' to install demo landing page when no application exists
-DEMO_MODE=false
-
+DEMO_MODE=true
+# Mode debug (optional) 0=inactive 1=acrive
+ENABLE_XDEBUG=1
# Health Check Installation (optional)
# Set to 'true' to install health check endpoint
# Automatically enabled when DEMO_MODE=true
-HEALTH_CHECK_INSTALL=false
+HEALTH_CHECK_INSTALL=true
# ============================================
# Application
# ============================================
APP_NAME=php-api-stack
-APP_ENV=production
-APP_DEBUG=false
+APP_ENV=development
+APP_DEBUG=true
APP_PORT=8089
APP_DOMAIN=localhost
@@ -36,17 +37,23 @@ COMPOSER_VERSION=2.8.12
SYMFONY_CLI_VERSION=5.15.1
# ============================================
-# PHP / Extensions
+# PECL Extension Versions
# ============================================
+PHP_REDIS_VERSION=6.1.0
+PHP_APCU_VERSION=5.1.24
+PHP_UUID_VERSION=1.2.1
+PHP_IMAGICK_VERSION=3.7.0
+PHP_AMQP_VERSION=2.1.2
+XDEBUG_VERSION=3.4.6
-# Core PHP extensions to install (installable only — built-ins are already available)
-# Built-ins include: tokenizer, fileinfo, ctype, iconv, session, curl, etc.
-# Available installable examples: pdo pdo_mysql pdo_pgsql opcache intl zip bcmath gd mysqli mbstring xml dom simplexml sockets pcntl exif
+# ============================================
+# PHP / Extensions
+# ============================================
+# Core PHP extensions (space-separated)
+# IMPORTANTE: Use aspas duplas para evitar interpretação como comando
PHP_CORE_EXTENSIONS="pdo pdo_mysql opcache intl zip bcmath gd mbstring xml sockets"
-# PECL extensions to install
-# Available: redis apcu uuid xdebug imagick amqp swoole
-# NOTE: remove xdebug in production builds
+# PECL extensions (space-separated)
PHP_PECL_EXTENSIONS="redis apcu uuid"
# ============================================
@@ -114,11 +121,13 @@ PHP_OPCACHE_JIT_BUFFER_SIZE=128M
# Host / credentials
REDIS_HOST=redis
REDIS_PASSWORD=HmlRedis_3Qy7nFTZgW6M2bK9pX4c
+# External Redis example service (compose profile)
+REDIS_HOST_PORT=6378
# Database count used by the template
REDIS_DATABASES=16
-# Memory policy — use lowercase units for Redis (e.g., 256mb)
+# Memory policy – use lowercase units for Redis (e.g., 256mb)
REDIS_MAXMEMORY=256mb
REDIS_MAXMEMORY_POLICY=allkeys-lru
REDIS_MAXMEMORY_SAMPLES=5
@@ -128,7 +137,6 @@ REDIS_MAXCLIENTS=10000
REDIS_TIMEOUT=0
# Persistence (AOF/RDB)
-# If you keep REDIS_SAVE here, handle it in the entrypoint to expand into multiple "save ..." lines.
REDIS_APPENDONLY=yes
REDIS_APPENDFSYNC=everysec
REDIS_SAVE="900 1 300 10 60 10000"
@@ -158,17 +166,18 @@ ENABLE_HTTP2=true
# ============================================
# Development (enable only in non-production)
# ============================================
-XDEBUG_ENABLE=false
+XDEBUG_ENABLE=1
XDEBUG_MODE=develop,debug,coverage
XDEBUG_HOST=host.docker.internal
XDEBUG_PORT=9003
-XDEBUG_IDE_KEY=PHPSTORM
+XDEBUG_IDE_KEY=VSCODE
+XDEBUG_VERSION=3.4.6
# ============================================
# Build / Registry
# ============================================
BUILD_TARGET=production
-IMAGE_TAG=latest
+IMAGE_TAG=dev
REGISTRY=docker.io
REPOSITORY=kariricode/php-api-stack
@@ -206,9 +215,6 @@ DB_USERNAME=phpapi_hml
DB_PASSWORD=HmlUser_3kT8zQf
DB_PORT=3307
-# External Redis example service (compose profile)
-REDIS_HOST_PORT=6378
-
# Grafana example service (compose profile)
GRAFANA_PORT=3000
GRAFANA_PASSWORD=HmlGrafana_7uV4mRp
diff --git a/.gitignore b/.gitignore
index 5472b08..a5d2acc 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,16 +6,18 @@
!.env.dev
# Application
-app/vendor/
-app/node_modules/
-app/var/
-app/.env
-app/.env.local
+./app/vendor/
+./app/node_modules/
+./app/var/
+./app/.env
+./app/.env.local
+./app/public/health.php
+./app/public/index.php
# Logs
-logs/
+./logs/
*.log
-/var/log/
+.//var/log/
# Docker
docker-compose.override.yml
@@ -23,14 +25,13 @@ docker-compose.override.yml
*.tar.gz
# SSL certificates
-ssl/
+./ssl/
*.pem
*.key
*.crt
# IDE
.idea/
-.vscode/
*.swp
*.swo
*~
@@ -40,8 +41,8 @@ ssl/
Thumbs.db
# Build artifacts
-build/
-dist/
+./build/
+./dist/
# Test files
test-app/
diff --git a/.vscode/launch.json b/.vscode/launch.json
new file mode 100644
index 0000000..ca6b51b
--- /dev/null
+++ b/.vscode/launch.json
@@ -0,0 +1,15 @@
+{
+ "version": "0.2.0",
+ "configurations": [
+ {
+ "name": "Listen for Xdebug",
+ "type": "php",
+ "request": "launch",
+ "port": 9003,
+ "pathMappings": {
+ "/var/www/html": "${workspaceFolder}"
+ },
+ "log": true
+ }
+ ]
+}
diff --git a/DOCKER_HUB.md b/DOCKER_HUB.md
index a8fefb4..47c4ba2 100644
--- a/DOCKER_HUB.md
+++ b/DOCKER_HUB.md
@@ -1,713 +1,707 @@
-# Docker Hub Publishing Guide
+# KaririCode/php-api-stack
-**Audience**: Image publishers and maintainers
-**Purpose**: Complete guide for publishing images to Docker Hub
+Production-ready **PHP API Stack** image built on Alpine Linux with **Nginx + PHP-FPM + Redis**. Secured, fast, and fully configurable via environment variables — ideal for modern APIs, web applications, and microservices.
-## 📋 Table of Contents
+
+
+
+
+
+
+
+
+
+
-- [Overview](#overview)
-- [Prerequisites](#prerequisites)
-- [Docker Hub Setup](#docker-hub-setup)
-- [Versioning Strategy](#versioning-strategy)
-- [Build Process](#build-process)
-- [Tagging Strategy](#tagging-strategy)
-- [Publishing Process](#publishing-process)
-- [Multi-Platform Builds](#multi-platform-builds)
-- [Automated Publishing](#automated-publishing)
-- [Best Practices](#best-practices)
-- [Troubleshooting](#troubleshooting)
+---
-## 🎯 Overview
+## 🔗 Official Links
-This guide covers the complete workflow for publishing **kariricode/php-api-stack** to Docker Hub, including:
+* **Docker Hub**: [https://hub.docker.com/r/kariricode/php-api-stack](https://hub.docker.com/r/kariricode/php-api-stack)
+* **GitHub Repository**: [https://github.com/kariricode/php-api-stack](https://github.com/kariricode/php-api-stack)
+* **Documentation**: [Full guides in GitHub repository](https://github.com/kariricode/php-api-stack#documentation)
+* **Official Site**: [https://kariricode.org/](https://kariricode.org/)
+* **KaririCode Framework**: [https://github.com/KaririCode-Framework](https://github.com/KaririCode-Framework)
-- ✅ Proper versioning and tagging
-- ✅ Multi-platform builds (amd64, arm64)
-- ✅ Automated CI/CD pipelines
-- ✅ Quality gates and validation
-- ✅ Documentation synchronization
+---
-## 🔧 Prerequisites
+## ✨ Highlights
-### Required Accounts
+### Core Stack
+* **Complete Integration**: Nginx 1.27.3 + PHP-FPM 8.4.13 + Redis 7.2.11
+* **Alpine Linux 3.21**: Minimal footprint (~225MB production, ~244MB dev)
+* **Multi-platform**: Native support for amd64 and arm64
-1. **Docker Hub Account**
- - Username: `kariricode` (or your organization)
- - Repository: `php-api-stack`
- - Access: Write permissions
+### Performance
+* **OPcache + JIT**: Tracing mode enabled by default for maximum performance
+* **Optimized Configuration**: Tuned Nginx/PHP-FPM with Unix socket communication
+* **FastCGI Cache**: Built-in for accelerated response times
+* **Static Assets**: Direct Nginx serving with aggressive caching
-2. **GitHub Account** (for CI/CD)
- - Repository access
- - Secrets configuration
+### Security
+* **Non-root Services**: All services run as unprivileged users
+* **Hardened Defaults**: Security headers (CSP, HSTS, X-Frame-Options)
+* **Rate Limiting**: Built-in protection against abuse
+* **Regular Updates**: Automated security patches and vulnerability scanning
-### Required Tools
+### Developer Experience
+* **100% Configurable**: All settings via environment variables
+* **Three Specialized Makefiles**: Build, Docker Hub, and Compose operations (50+ commands)
+* **Comprehensive Health Checks**: Simple and detailed endpoints for monitoring
+* **CI/CD Ready**: GitHub Actions workflows and automated testing
+* **Development Image**: Includes Xdebug 3.4.6 and Symfony CLI 5.15.1
-```bash
-# Core tools
-docker --version # >= 20.10
-docker buildx version # >= 0.10
-make --version
-git --version
-
-# Optional but recommended
-trivy --version # Security scanning
-hadolint --version # Dockerfile linting
-```
+---
+
+## 🚀 Quick Start
-### Environment Setup
+### 30-Second Demo
+
+Pull and run the demo page:
```bash
-# Clone repository
-git clone https://github.com/kariricode/php-api-stack.git
-cd php-api-stack
+docker pull kariricode/php-api-stack:latest
+docker run -d -p 8080:80 --name my-app kariricode/php-api-stack:latest
-# Verify structure
-ls -la
-# Expected: Dockerfile, Makefile, VERSION, .env, etc.
+# Open: http://localhost:8080
```
-## 🚀 Docker Hub Setup
+You'll see a comprehensive status page showing PHP version, loaded extensions, OPcache statistics, Redis connectivity, and system resources.
-### 1. Login to Docker Hub
+### With Your Application
```bash
-# Interactive login
-docker login
+docker run -d \
+ -p 8080:80 \
+ --name my-app \
+ -e APP_ENV=production \
+ -e PHP_MEMORY_LIMIT=512M \
+ -e PHP_OPCACHE_VALIDATE_TIMESTAMPS=0 \
+ -v $(pwd)/app:/var/www/html:ro \
+ kariricode/php-api-stack:latest
+```
-# Or with credentials
-docker login -u kariricode -p YOUR_TOKEN
+**Important**: Your application's public entry point must be at `/var/www/html/public/index.php` (Symfony/Laravel standard).
-# Verify login
-docker info | grep Username
-# Expected: Username: kariricode
-```
+---
-### 2. Create Access Token (Recommended)
+## 🐳 Docker Compose
-Instead of using password:
+### Basic Setup
-1. Go to https://hub.docker.com/settings/security
-2. Click "New Access Token"
-3. Name: `php-api-stack-ci`
-4. Permissions: `Read, Write, Delete`
-5. Copy token (you won't see it again!)
+Create `docker-compose.yml`:
-```bash
-# Login with token
-echo "YOUR_TOKEN" | docker login -u kariricode --password-stdin
+```yaml
+version: '3.9'
+
+services:
+ app:
+ image: kariricode/php-api-stack:latest
+ container_name: my-app
+ ports:
+ - "8080:80"
+ environment:
+ APP_ENV: production
+ PHP_MEMORY_LIMIT: 512M
+ PHP_FPM_PM_MAX_CHILDREN: 100
+ REDIS_HOST: 127.0.0.1 # Using internal Redis
+ volumes:
+ - ./app:/var/www/html:ro
+ - ./logs:/var/log
+ healthcheck:
+ test: ["CMD", "curl", "-f", "http://localhost/health"]
+ interval: 30s
+ timeout: 3s
+ retries: 3
+ restart: unless-stopped
+```
+
+### With External Database
-# Save to environment (optional)
-export DOCKER_HUB_TOKEN="YOUR_TOKEN"
+```yaml
+version: '3.9'
+
+services:
+ app:
+ image: kariricode/php-api-stack:latest
+ container_name: my-app
+ ports:
+ - "8080:80"
+ environment:
+ APP_ENV: production
+ DATABASE_URL: mysql://user:pass@db:3306/myapp
+ REDIS_HOST: 127.0.0.1 # Internal Redis
+ volumes:
+ - ./app:/var/www/html:ro
+ depends_on:
+ db:
+ condition: service_healthy
+ healthcheck:
+ test: ["CMD", "curl", "-f", "http://localhost/health"]
+ interval: 30s
+ restart: unless-stopped
+ networks:
+ - app-network
+
+ db:
+ image: mysql:8.0
+ environment:
+ MYSQL_ROOT_PASSWORD: secret
+ MYSQL_DATABASE: myapp
+ MYSQL_USER: user
+ MYSQL_PASSWORD: pass
+ volumes:
+ - db-data:/var/lib/mysql
+ healthcheck:
+ test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
+ interval: 10s
+ timeout: 5s
+ retries: 5
+ restart: unless-stopped
+ networks:
+ - app-network
+
+volumes:
+ db-data:
+
+networks:
+ app-network:
+ driver: bridge
+```
+
+**Note about Redis**: This image includes an internal Redis instance on `127.0.0.1:6379`. For external Redis, create a separate service and set `REDIS_HOST` to the service name.
+
+Start services:
+
+```bash
+docker compose up -d
+docker compose logs -f
+docker compose ps
```
-### 3. Repository Configuration
-
-Ensure Docker Hub repository exists:
-- **Name**: `php-api-stack`
-- **Visibility**: Public
-- **Description**: "Production-ready PHP 8.4 + Nginx + Redis + Supervisor stack"
-- **README**: Synced from GitHub (see [Documentation Sync](#documentation-sync))
+---
-## 📦 Versioning Strategy
+## 🏷️ Available Tags
-The project follows [Semantic Versioning](https://semver.org/):
+| Tag | Description | Size | Use Case |
+|-----|-------------|------|----------|
+| `latest` | Latest stable release | ~225MB | General use |
+| `1.5.0` | Specific version | ~225MB | Production (pinned) |
+| `1.5` | Latest patch in v1.5.x | ~225MB | Auto-patch updates |
+| `1` | Latest minor in v1.x.x | ~225MB | Auto-minor updates |
+| `dev` | Development build | ~244MB | Local development with Xdebug |
+| `test` | With comprehensive health checks | ~216MB | Testing/monitoring |
-```
-MAJOR.MINOR.PATCH
+### Tagging Strategy
-Examples:
-1.2.1 → Patch release (bug fixes)
-1.3.0 → Minor release (new features, backward compatible)
-2.0.0 → Major release (breaking changes)
-```
+- **Production**: Always pin to specific versions (`1.5.0`) for reproducibility
+- **Development**: Use `dev` tag for debugging capabilities
+- **Staging**: Use minor version tags (`1.5`) for automatic patch updates
+- **Never** use `latest` in production
-### Version Management
+Pull a specific tag:
-#### Check Current Version
```bash
-make version
-# Output: Current version: 1.2.1
+docker pull kariricode/php-api-stack:1.5.0
+docker pull kariricode/php-api-stack:dev
```
-#### Bump Version
+---
-```bash
-# Patch (1.2.1 → 1.2.2)
-make bump-patch
+## ⚙️ Configuration
-# Minor (1.2.1 → 1.3.0)
-make bump-minor
+All configuration is done via environment variables. The image supports **100+ configuration options** covering PHP, PHP-FPM, Nginx, Redis, and application settings.
-# Major (1.2.1 → 2.0.0)
-make bump-major
-```
+### Essential Variables
-#### Manual Version Update
```bash
-# Edit VERSION file
-echo "1.3.0" > VERSION
-
-# Commit
-git add VERSION
-git commit -m "chore: bump version to 1.3.0"
-git tag v1.3.0
-git push origin main --tags
-```
+# Application
+APP_ENV=production # production|development|test
+APP_DEBUG=false # Enable debug mode
+APP_NAME=my-application # Application name
-## 🏗️ Build Process
+# PHP Runtime
+PHP_MEMORY_LIMIT=512M # Memory per request
+PHP_MAX_EXECUTION_TIME=60 # Script timeout
+PHP_UPLOAD_MAX_FILESIZE=100M # Max upload size
+PHP_POST_MAX_SIZE=100M # Max POST size
+PHP_DATE_TIMEZONE=UTC # Timezone
-### Local Build
+# PHP-FPM
+PHP_FPM_PM=static # static|dynamic|ondemand
+PHP_FPM_PM_MAX_CHILDREN=100 # Worker processes
-#### Simple Build
-```bash
-# Production build
-make build
+# OPcache
+PHP_OPCACHE_ENABLE=1 # Enable OPcache
+PHP_OPCACHE_MEMORY=256 # OPcache memory (MB)
+PHP_OPCACHE_VALIDATE_TIMESTAMPS=0 # 0 for prod, 1 for dev
+PHP_OPCACHE_JIT=tracing # JIT mode
-# Expected output:
-# Building Docker image...
-# Image: kariricode/php-api-stack:1.2.1
-# ✓ Build complete!
-```
-
-#### Build with Tests
-```bash
-# Build + quick tests
-make build-test
+# Nginx
+NGINX_WORKER_PROCESSES=auto # auto = CPU cores
+NGINX_CLIENT_MAX_BODY_SIZE=100M # Max request size
-# Build test image with comprehensive health
-make build-test-image
+# Redis (Internal)
+REDIS_HOST=127.0.0.1 # Internal Redis (standalone)
+REDIS_PASSWORD= # Optional password
```
-#### Build without Cache
-```bash
-# Force rebuild
-make build-no-cache
+**Complete reference**: See [.env.example](https://github.com/kariricode/php-api-stack/blob/main/.env.example) in the repository.
-# Or manual
-./build-from-env.sh --no-cache
-```
+### Configuration Examples
-### Build Validation
+#### High Performance Setup
```bash
-# 1. Check image exists
-docker images kariricode/php-api-stack
-
-# 2. Verify tags
-docker images kariricode/php-api-stack --format "table {{.Tag}}\t{{.Size}}\t{{.CreatedAt}}"
-
-# 3. Test image
-make test-quick
-
-# 4. Scan for vulnerabilities
-make scan
+docker run -d \
+ -p 80:80 \
+ -e PHP_MEMORY_LIMIT=512M \
+ -e PHP_FPM_PM=static \
+ -e PHP_FPM_PM_MAX_CHILDREN=200 \
+ -e PHP_OPCACHE_MEMORY=512 \
+ -e PHP_OPCACHE_VALIDATE_TIMESTAMPS=0 \
+ -e PHP_OPCACHE_JIT=tracing \
+ -e NGINX_WORKER_CONNECTIONS=4096 \
+ -v $(pwd)/app:/var/www/html:ro \
+ kariricode/php-api-stack:latest
```
-## 🏷️ Tagging Strategy
-
-### Standard Tags
+#### Memory-Constrained Environment
-For version `1.2.1`, the following tags are created:
+```bash
+docker run -d \
+ -p 80:80 \
+ --memory="512m" \
+ -e PHP_MEMORY_LIMIT=256M \
+ -e PHP_FPM_PM=dynamic \
+ -e PHP_FPM_PM_MAX_CHILDREN=25 \
+ -e PHP_OPCACHE_MEMORY=128 \
+ -v $(pwd)/app:/var/www/html:ro \
+ kariricode/php-api-stack:latest
+```
-| Tag | Description | Auto-updates |
-|-----|-------------|--------------|
-| `1.2.1` | Specific version | Never |
-| `1.2` | Minor version | Patch only |
-| `1` | Major version | Minor + Patch |
-| `latest` | Latest stable | All releases |
-| `stable` | Production | Stable releases |
+---
-### Environment-Specific Tags
+## 📊 Stack Components
-| Tag | Description | When |
-|-----|-------------|------|
-| `dev` | Development | `APP_ENV=development` |
-| `staging` | Staging | `APP_ENV=staging` |
-| `stable` | Production | `APP_ENV=production` |
-| `test` | Testing | `--test` flag |
+| Component | Version | Purpose |
+|-----------|---------|---------|
+| **PHP-FPM** | 8.4.13 | PHP processing with optimized pool |
+| **Nginx** | 1.27.3 | High-performance web server |
+| **Redis** | 7.2.11 | Cache and session management |
+| **Alpine Linux** | 3.21 | Minimal base image |
+| **Composer** | 2.8.12 | PHP dependency manager |
+| **Symfony CLI** | 5.15.1 | Symfony tools (dev only) |
+| **Xdebug** | 3.4.6 | PHP debugger (dev only) |
-### Manual Tagging
+### PHP Extensions
-```bash
-# Tag existing image
-docker tag kariricode/php-api-stack:1.2.1 kariricode/php-api-stack:latest
-docker tag kariricode/php-api-stack:1.2.1 kariricode/php-api-stack:1.2
-docker tag kariricode/php-api-stack:1.2.1 kariricode/php-api-stack:1
-docker tag kariricode/php-api-stack:1.2.1 kariricode/php-api-stack:stable
+**Core Extensions** (Pre-installed):
+```
+pdo, pdo_mysql, opcache, intl, zip, bcmath, gd, mbstring, xml, sockets
```
-### Automated Tagging
+**PECL Extensions** (Pre-installed):
+```
+redis (6.1.0), apcu (5.1.24), uuid (1.2.1), imagick (3.7.0), amqp (2.1.2)
+```
-The `build-from-env.sh` script handles tagging automatically:
+**Built-in Extensions** (Always available):
+```
+json, curl, fileinfo, ctype, iconv, session, tokenizer, filter, hash, openssl
+```
-```bash
-# Production build (creates all tags)
-./build-from-env.sh --version=1.2.1
+---
-# Test build (creates test tags)
-./build-from-env.sh --test --version=1.2.1
-```
+## 🏥 Health Checks
-## 📤 Publishing Process
+### Simple Health Check
-### Quick Publish
+Lightweight endpoint for load balancers and orchestrators:
-#### Using Makefile (Recommended)
```bash
-# Build and push
-make build
-make push
-
-# Or combined
-make release # lint + build + test + scan + push
+curl http://localhost:8080/health
+# Response: healthy
```
-#### Using Script
-```bash
-# Build with push
-./build-from-env.sh --push
+HTTP 200 if healthy, 503 if unhealthy.
-# Or separate
-./build-from-env.sh
-docker push kariricode/php-api-stack:1.2.1
-docker push kariricode/php-api-stack:latest
-```
+### Comprehensive Health Check
-### Complete Release Workflow
+Detailed diagnostics with component-level checks:
```bash
-# 1. Update version
-make bump-patch # or bump-minor, bump-major
-
-# 2. Quality gates
-make lint # Dockerfile validation
-make build # Build image
-make test # Run tests
-make scan # Security scan
-
-# 3. Push to registry
-make push
-
-# 4. Tag in git
-git tag v$(cat VERSION)
-git push origin main --tags
-
-# 5. Create GitHub release (optional)
-gh release create v$(cat VERSION) \
- --title "Release $(cat VERSION)" \
- --notes "See CHANGELOG.md"
+curl http://localhost:8080/health.php | jq
```
-### Push Individual Tags
+Returns JSON with:
+- Overall status (healthy/degraded/unhealthy)
+- PHP runtime details (version, memory, extensions)
+- OPcache statistics (hit rate, memory, JIT status)
+- Redis connectivity (latency, memory, persistence)
+- System resources (disk, CPU, memory)
+- Application directories (permissions, accessibility)
-```bash
-VERSION=$(cat VERSION)
+**Docker Healthcheck**:
-# Push specific version
-docker push kariricode/php-api-stack:$VERSION
+```yaml
+healthcheck:
+ test: ["CMD", "curl", "-f", "http://localhost/health"]
+ interval: 30s
+ timeout: 3s
+ retries: 3
+ start_period: 10s
+```
-# Push semantic versions
-docker push kariricode/php-api-stack:${VERSION%.*} # 1.2
-docker push kariricode/php-api-stack:${VERSION%%.*} # 1
+**Kubernetes Probes**:
-# Push latest
-docker push kariricode/php-api-stack:latest
+```yaml
+livenessProbe:
+ httpGet:
+ path: /health
+ port: 80
+ initialDelaySeconds: 30
+ periodSeconds: 10
-# Push environment-specific
-docker push kariricode/php-api-stack:stable
+readinessProbe:
+ httpGet:
+ path: /health.php
+ port: 80
+ initialDelaySeconds: 10
+ periodSeconds: 5
```
-## 🌍 Multi-Platform Builds
+---
-Build for multiple architectures (amd64, arm64):
+## 🎭 Framework Integration
-### Setup Buildx
+### Symfony
```bash
-# Create builder
-docker buildx create --name php-api-stack-builder --use
-
-# Verify
-docker buildx ls
-# Expected: php-api-stack-builder running
+docker run -d \
+ -p 8080:80 \
+ -e APP_ENV=prod \
+ -e APP_SECRET=$(openssl rand -hex 16) \
+ -e DATABASE_URL=mysql://user:pass@db:3306/symfony \
+ -v $(pwd):/var/www/html:ro \
+ kariricode/php-api-stack:latest
```
-### Multi-Platform Build
+### Laravel
-#### Using Script
```bash
-# Build and push for multiple platforms
-./build-from-env.sh --multi-platform --push
-
-# Platforms: linux/amd64, linux/arm64
+docker run -d \
+ -p 8080:80 \
+ -e APP_ENV=production \
+ -e APP_KEY=base64:your-key-here \
+ -e DB_CONNECTION=mysql \
+ -e DB_HOST=db \
+ -e DB_DATABASE=laravel \
+ -v $(pwd):/var/www/html:ro \
+ kariricode/php-api-stack:latest
```
-#### Manual Build
-```bash
-docker buildx build \
- --platform linux/amd64,linux/arm64 \
- --tag kariricode/php-api-stack:1.2.1 \
- --tag kariricode/php-api-stack:latest \
- --push \
- .
-```
-
-### Verify Multi-Platform
+### Custom PHP App
```bash
-# Check manifest
-docker manifest inspect kariricode/php-api-stack:latest
-
-# Expected output shows:
-# - linux/amd64
-# - linux/arm64
+docker run -d \
+ -p 8080:80 \
+ -e APP_ENV=production \
+ -v $(pwd)/my-app:/var/www/html:ro \
+ kariricode/php-api-stack:latest
```
-## 🤖 Automated Publishing
+**Directory Structure**: Your app must have `public/index.php` as the entry point.
-### GitHub Actions
+---
-Create `.github/workflows/publish.yml`:
+## 🔐 Security Features
-```yaml
-name: Publish to Docker Hub
-
-on:
- push:
- tags:
- - 'v*'
-
-jobs:
- publish:
- runs-on: ubuntu-latest
- steps:
- - name: Checkout
- uses: actions/checkout@v3
-
- - name: Set up Docker Buildx
- uses: docker/setup-buildx-action@v2
-
- - name: Login to Docker Hub
- uses: docker/login-action@v2
- with:
- username: ${{ secrets.DOCKER_HUB_USERNAME }}
- password: ${{ secrets.DOCKER_HUB_TOKEN }}
-
- - name: Extract version
- id: version
- run: echo "VERSION=${GITHUB_REF#refs/tags/v}" >> $GITHUB_OUTPUT
-
- - name: Build and test
- run: |
- make build
- make test
- make scan
-
- - name: Build and push multi-platform
- run: |
- ./build-from-env.sh \
- --version=${{ steps.version.outputs.VERSION }} \
- --multi-platform \
- --push
-
- - name: Update Docker Hub description
- uses: peter-evans/dockerhub-description@v3
- with:
- username: ${{ secrets.DOCKER_HUB_USERNAME }}
- password: ${{ secrets.DOCKER_HUB_TOKEN }}
- repository: kariricode/php-api-stack
- readme-filepath: ./IMAGE_USAGE_GUIDE.md
-```
+### Built-in Security
-### Required GitHub Secrets
+- ✅ **Non-root Services**: All services run as unprivileged users (`www-data`, `nginx`, `redis`)
+- ✅ **Security Headers**: X-Frame-Options, X-Content-Type-Options, CSP, HSTS
+- ✅ **Rate Limiting**: 10 req/s (general), 100 req/s (API endpoints)
+- ✅ **Disabled Functions**: Dangerous PHP functions blocked
+- ✅ **Open Basedir**: Restricted to `/var/www/html` and `/tmp`
+- ✅ **Hidden Tokens**: Server version and PHP version hidden
-Add these secrets to your GitHub repository:
-- `DOCKER_HUB_USERNAME`: Your Docker Hub username
-- `DOCKER_HUB_TOKEN`: Your Docker Hub access token
+### Best Practices
-Settings → Secrets and variables → Actions → New repository secret
+- Always use specific version tags in production
+- Mount application code as read-only (`:ro`)
+- Store secrets in environment variables
+- Enable HTTPS via reverse proxy (Nginx, Traefik, etc.)
+- Regularly update to latest patch versions
+- Use vulnerability scanning (Trivy included in CI/CD)
-### Trigger Automated Publish
+---
-```bash
-# 1. Bump version
-make bump-patch
+## 🧪 Testing & Development
-# 2. Commit and tag
-git add VERSION
-git commit -m "chore: bump version to $(cat VERSION)"
-git tag v$(cat VERSION)
+### Development Image
-# 3. Push (triggers GitHub Action)
-git push origin main --tags
+The `dev` tag includes additional tools for local development:
-# 4. Monitor workflow
-# Visit: https://github.com/kariricode/php-api-stack/actions
+```bash
+docker run -d \
+ -p 8001:80 \
+ -p 9003:9003 \
+ -e APP_ENV=development \
+ -e APP_DEBUG=true \
+ -e PHP_DISPLAY_ERRORS=On \
+ -e XDEBUG_ENABLE=1 \
+ -v $(pwd)/app:/var/www/html \
+ kariricode/php-api-stack:dev
```
-## 🎯 Best Practices
+**Includes**:
+- Xdebug 3.4.6 (configurable)
+- Symfony CLI 5.15.1
+- Extended error reporting
+- File change detection (OPcache revalidation)
-### Pre-Publish Checklist
+### Running Tests
```bash
-# ✅ 1. Code quality
-make lint
+# Quick version check
+docker run --rm kariricode/php-api-stack:latest php -v
-# ✅ 2. Build succeeds
-make build-no-cache
+# Full test suite
+docker run --rm kariricode/php-api-stack:test /usr/local/bin/health-check.sh
+```
-# ✅ 3. All tests pass
-make test
+---
-# ✅ 4. No vulnerabilities
-make scan
+## 🛠 Makefile Commands
-# ✅ 5. Health check works
-make run-test
-make test-health
-make stop-test
+The repository includes **three specialized Makefiles** with 50+ commands:
-# ✅ 6. Documentation updated
-# Review: README.md, TESTING.md, DOCKER_HUB.md, IMAGE_USAGE_GUIDE.md
+### Main Makefile (Build & Runtime)
-# ✅ 7. Changelog updated
-# Add entry to CHANGELOG.md
+```bash
+make build # Build production image
+make build-dev # Build dev image
+make run # Run production container
+make run-dev # Run dev container
+make test # Run test suite
+make lint # Lint Dockerfile
+make scan # Security scan
+```
-# ✅ 8. Version bumped
-cat VERSION # Should be new version
+### Makefile.dockerhub (Publishing)
-# ✅ 9. Git tagged
-git tag | tail -1 # Should match VERSION
+```bash
+make hub-help # Show Docker Hub commands
+make version # Show current version
+make bump-patch # Bump patch version
+make tag-production # Tag production image
+make push-production # Push to Docker Hub
+make release-production # Full release pipeline
```
-### Image Size Optimization
+### Makefile.compose (Orchestration)
```bash
-# Check current size
-docker images kariricode/php-api-stack:latest
-
-# Optimize .dockerignore
-cat > .dockerignore << EOF
-.git
-.github
-.gitignore
-tests/
-docs/
-*.md
-!README.md
-.env
-.env.*
-!.env.example
-EOF
-
-# Rebuild
-make build-no-cache
-
-# Compare
-docker images kariricode/php-api-stack
+make compose-help # Show Compose commands
+make compose-up # Start services
+make compose-logs # View logs
+make compose-shell # Access container
```
-### Security Best Practices
+**Get the source**: [https://github.com/kariricode/php-api-stack](https://github.com/kariricode/php-api-stack)
-1. **Use Access Tokens**: Never use passwords directly
-2. **Scan Before Push**: Always run `make scan`
-3. **Pin Base Images**: Use specific versions (already done)
-4. **Regular Updates**: Update base images monthly
-5. **Minimal Privileges**: Use non-root user when possible
-
-### Documentation Sync
+---
-#### Docker Hub Description
+## 🐛 Troubleshooting
-Option 1: Automated (GitHub Actions)
-```yaml
-# Already in publish.yml workflow
-- name: Update Docker Hub description
- uses: peter-evans/dockerhub-description@v3
- with:
- readme-filepath: ./IMAGE_USAGE_GUIDE.md
-```
+### Container Won't Start
-Option 2: Manual
```bash
-# Install tool
-npm install -g dockerhub-description
-
-# Update description
-dockerhub-description \
- kariricode/php-api-stack \
- ./IMAGE_USAGE_GUIDE.md \
- --username kariricode \
- --password "$DOCKER_HUB_TOKEN"
-```
+# Check logs
+docker logs
-## 🐛 Troubleshooting
+# Common causes:
+# - Port already in use → Change port: -p 8081:80
+# - Volume permissions → Fix: chmod -R 755 app/
+# - Memory limits → Increase: --memory="1g"
+```
-### Build Issues
+### 502 Bad Gateway
-#### Error: Cannot connect to Docker daemon
```bash
-# Check Docker is running
-docker ps
+# Check PHP-FPM
+docker exec ps aux | grep php-fpm
+
+# Check logs
+docker exec tail -f /var/log/php/fpm-error.log
+docker exec tail -f /var/log/nginx/error.log
-# Restart Docker
-sudo systemctl restart docker # Linux
-# or restart Docker Desktop
+# Restart PHP-FPM
+docker exec kill -USR2 $(cat /var/run/php/php-fpm.pid)
```
-#### Error: Build fails with "no space left on device"
+### Redis Connection Issues
+
```bash
-# Clean up
-docker system prune -af --volumes
+# Check Redis (internal)
+docker exec redis-cli -h 127.0.0.1 ping
+# Should return: PONG
+
+# Check REDIS_HOST variable
+docker exec env | grep REDIS_HOST
+# Should be: 127.0.0.1 (standalone) or redis (compose)
-# Check space
-df -h
+# Test with password (if configured)
+docker exec redis-cli -h 127.0.0.1 -a "password" ping
```
-### Push Issues
+### Slow Performance
-#### Error: Authentication required
```bash
-# Re-login
-docker logout
-docker login
+# Check OPcache hit rate (should be >95%)
+docker exec php -r "
+\$stats = opcache_get_status()['opcache_statistics'];
+echo 'Hit Rate: ' . \$stats['opcache_hit_rate'] . '%' . PHP_EOL;
+"
-# Verify
-docker info | grep Username
+# Check resource usage
+docker stats
+
+# Solutions:
+# - Increase OPcache memory: -e PHP_OPCACHE_MEMORY=512
+# - Increase FPM workers: -e PHP_FPM_PM_MAX_CHILDREN=100
+# - Add container resources: --memory="2g" --cpus="4"
```
-#### Error: Denied: requested access to the resource is denied
-```bash
-# Check repository name
-docker images | grep php-api-stack
+**Complete troubleshooting guide**: [IMAGE_USAGE_GUIDE.md](https://github.com/kariricode/php-api-stack/blob/main/IMAGE_USAGE_GUIDE.md#troubleshooting)
-# Should be: kariricode/php-api-stack
-# Not: php-api-stack
+---
-# Retag if needed
-docker tag php-api-stack:latest kariricode/php-api-stack:latest
-```
+## 📚 Documentation
-#### Error: Image push failed
-```bash
-# Check network
-ping registry-1.docker.io
+| Document | Description |
+|----------|-------------|
+| **[README.md](https://github.com/kariricode/php-api-stack)** | Project overview and quick start |
+| **[IMAGE_USAGE_GUIDE.md](https://github.com/kariricode/php-api-stack/blob/main/IMAGE_USAGE_GUIDE.md)** | Complete usage guide for end users |
+| **[DOCKER_COMPOSE_GUIDE.md](https://github.com/kariricode/php-api-stack/blob/main/DOCKER_COMPOSE_GUIDE.md)** | Docker Compose orchestration |
+| **[TESTING.md](https://github.com/kariricode/php-api-stack/blob/main/TESTING.md)** | Testing procedures for maintainers |
-# Check size (max 10GB per layer)
-docker images kariricode/php-api-stack:latest
+---
-# Try again with retry
-for i in {1..3}; do
- docker push kariricode/php-api-stack:latest && break
- sleep 5
-done
-```
+## 🔄 Version History
-### Multi-Platform Issues
+### v1.5.0 (2025-10-24) - Latest
-#### Error: Multiple platforms feature is currently not supported
-```bash
-# Enable experimental features
-export DOCKER_CLI_EXPERIMENTAL=enabled
+**Docker Hub Integration**:
+- Fixed `hub-check` command display bug
+- Simplified dev tagging: only `dev` tag (removed `dev-X.Y.Z`)
+- Fixed version bump commands (`bump-patch`, `bump-minor`, `bump-major`)
+- Improved `hub-check` output with checkmarks (✓/✗)
+- Added comprehensive Docker Hub utilities
-# Or in ~/.docker/config.json:
-{
- "experimental": "enabled"
-}
-```
+**Breaking Changes**:
+- Dev versioned tags (`dev-X.Y.Z`) are no longer created
-#### Error: Buildx builder not found
-```bash
-# Create builder
-docker buildx create --name php-api-stack-builder --use
+### v1.4.5 (2025-10-24)
-# Bootstrap
-docker buildx inspect --bootstrap
-```
+**Build System**:
+- Fixed PHP extension quoting in Makefile
+- Secure `.env` parsing to prevent command execution
+- Proper escaping for build args
-## 📊 Monitoring Published Images
+**Redis Integration**:
+- Automatic `REDIS_HOST` override for standalone containers
+- Smart DNS fallback in health checks
+- Documentation improvements
-### Docker Hub Stats
+**Dockerfile**:
+- Fixed OPcache validation (Zend extension check)
+- Added `util-linux` for UUID extension
+- Fixed shellcheck errors
-```bash
-# View on Docker Hub
-open https://hub.docker.com/r/kariricode/php-api-stack
+### v1.4.3 (2025-10-20)
-# Check pulls (requires Docker Hub account)
-curl -s "https://hub.docker.com/v2/repositories/kariricode/php-api-stack/" | jq '.pull_count'
-```
+- Refactored Makefile with 50+ organized commands
+- Enhanced development workflow
+- Improved health check monitoring
+- Updated documentation
-### Image Verification
+### v1.2.1 (2025-10-18)
-```bash
-# Pull and verify
-docker pull kariricode/php-api-stack:latest
+- Added comprehensive Makefile
+- Docker Compose integration
+- Multiple service profiles
+- Enhanced health checks
-# Check digest
-docker inspect kariricode/php-api-stack:latest | jq '.[0].RepoDigests'
+### v1.2.0 (2025-10-15)
-# Verify signature (if signed)
-docker trust inspect kariricode/php-api-stack:latest
-```
+- PHP 8.4, Nginx 1.27.3, Redis 7.2
+- OPcache + JIT optimization
+- Socket-based PHP-FPM
+- Environment variable configuration
-## 📅 Release Schedule
+**Full changelog**: [GitHub Releases](https://github.com/kariricode/php-api-stack/releases)
-### Recommended Schedule
+---
-- **Patch releases**: As needed (bug fixes)
-- **Minor releases**: Monthly (new features)
-- **Major releases**: Quarterly (breaking changes)
-- **Security patches**: Immediately (critical vulnerabilities)
+## 🧭 Related Projects
-### Release Process Timeline
+This image is part of the **KaririCode** ecosystem:
-```
-Week 1: Development
- - Feature development
- - Bug fixes
- - Testing
-
-Week 2: Pre-release
- - Code freeze
- - Final testing
- - Documentation update
-
-Week 3: Release
- - Version bump
- - Build and test
- - Publish to Docker Hub
- - GitHub release
-
-Week 4: Monitoring
- - Monitor issues
- - Quick patches if needed
- - Plan next release
-```
+### KaririCode Framework
-## 📞 Support
+Modern PHP framework with advanced features:
+- **Repository**: [KaririCode-Framework](https://github.com/KaririCode-Framework)
+- **30+ Components**: DI, Router, Auth, Cache, EventDispatcher, etc.
+- **ARFA Architecture**: Adaptive Reactive Flow Architecture
-### For Publishing Issues
+### KaririCode DevKit
-- **GitHub Issues**: [Report issues](https://github.com/kariricode/php-api-stack/issues)
-- **Docker Hub**: [Repository page](https://hub.docker.com/r/kariricode/php-api-stack)
-- **Discussions**: [GitHub Discussions](https://github.com/kariricode/php-api-stack/discussions)
+Development environment automation:
+- **Repository**: [kariricode/devkit](https://github.com/kariricode/devkit)
+- **Features**: Docker, Compose, quality tools, CI/CD
+- **Integration**: Uses this Docker image
-### Quick Reference
+---
-```bash
-# Complete release
-make version # Check version
-make bump-patch # Bump version
-make release # Full pipeline
-git tag v$(cat VERSION)
-git push origin main --tags
-
-# Emergency hotfix
-make build-no-cache
-make test
-make push
-```
+## 🤝 Contributing
+
+Contributions are welcome! Please:
+
+1. Fork the repository: [https://github.com/kariricode/php-api-stack](https://github.com/kariricode/php-api-stack)
+2. Create a feature branch
+3. Make your changes
+4. Submit a Pull Request
+
+**Standards**:
+- Follow PSR-12 for PHP
+- Use Conventional Commits
+- Add tests for new features
+- Update documentation
+
+---
+
+## 📄 License
+
+This project is licensed under the MIT License.
+See [LICENSE](https://github.com/kariricode/php-api-stack/blob/main/LICENSE) in the repository.
+
+---
+
+## 🙌 Support
+
+- **Issues**: [GitHub Issues](https://github.com/kariricode/php-api-stack/issues)
+- **Discussions**: [GitHub Discussions](https://github.com/kariricode/php-api-stack/discussions)
+- **Docker Hub**: [kariricode/php-api-stack](https://hub.docker.com/r/kariricode/php-api-stack)
---
-**Next**: [IMAGE_USAGE_GUIDE.md](IMAGE_USAGE_GUIDE.md) - Learn how to use the published image
\ No newline at end of file
+
+
+**Made with 💚 by [KaririCode](https://kariricode.org)**
+
+[](https://kariricode.org)
+[](https://github.com/KaririCode-Framework)
+
+
\ No newline at end of file
diff --git a/DOCKER_HUB_OVERVIEW.md b/DOCKER_HUB_OVERVIEW.md
deleted file mode 100644
index a59d1c5..0000000
--- a/DOCKER_HUB_OVERVIEW.md
+++ /dev/null
@@ -1,234 +0,0 @@
-# KaririCode/php-api-stack
-
-Production‑ready **PHP API Stack** image built on Alpine Linux with **Nginx + PHP‑FPM + Redis**. Secured, fast, and configurable via environment variables — ideal for modern APIs and web apps.
-
-
-
-
-
-
-
-
-
-
----
-
-## 🔗 Official Links
-
-* **Repository (source)**: [https://github.com/kariricode/php-api-stack](https://github.com/kariricode/php-api-stack)
-* **Official site**: [https://kariricode.org/](https://kariricode.org/)
-* **KaririCode Framework (org)**: [https://github.com/KaririCode-Framework](https://github.com/KaririCode-Framework)
-
----
-
-## ✨ Highlights
-
-* **Complete Stack**: Nginx (1.27.3) + PHP‑FPM (8.4) + Redis (7.2)
-* **Performance**: OPcache + JIT, tuned Nginx/PHP‑FPM, socket‑based FPM
-* **Security‑first**: non‑root services, hardened defaults, CSP/HSTS headers
-* **Easy Config**: 100% via environment variables or `.env`
-* **Healthcheck**: `/health.php` (simple or comprehensive build)
-* **CI/CD‑ready**: multi‑platform builds; Makefile targets; scan/lint helpers
-
-> Image tag `1.2.0` is the latest stable at publication time. Use `:latest` for automatic updates or pin to exact versions for reproducibility.
-
----
-
-## 🚀 Quick Start
-
-Pull and run the demo page:
-
-```bash
-docker run -d \
- -p 8080:80 \
- --name php-api-stack-demo \
- kariricode/php-api-stack:latest
-
-# open: http://localhost:8080
-```
-
-Run with your application mounted:
-
-```bash
-docker run -d \
- -p 8080:80 \
- --name my-php-app \
- -e APP_ENV=production \
- -e PHP_MEMORY_LIMIT=512M \
- -v $(pwd)/app:/var/www/html \
- kariricode/php-api-stack:latest
-```
-
-> **Note**: Your app’s public entry point must be at `/var/www/html/public/index.php`.
-
----
-
-## 🐳 Docker Compose
-
-Minimal `docker-compose.yml`:
-
-```yaml
-version: '3.9'
-services:
- app:
- image: kariricode/php-api-stack:latest
- container_name: php-api-stack
- ports:
- - "8080:80"
- env_file: .env
- volumes:
- - ./app:/var/www/html
- - ./logs:/var/log
- depends_on:
- - redis
- redis:
- image: redis:7.2-alpine
- command: ["redis-server", "/etc/redis/redis.conf", "--appendonly", "yes"]
- volumes:
- - redis_data:/data
-volumes:
- redis_data:
-```
-
-Start services:
-
-```bash
-docker compose up -d
-```
-
-**Production tips**
-
-* Mount app read‑only: `./app:/var/www/html:ro`
-* Add healthcheck:
-
-```yaml
-healthcheck:
- test: ["CMD-SHELL", "curl -fsS http://localhost/health.php || exit 1"]
- interval: 30s
- timeout: 3s
- retries: 3
- start_period: 10s
-```
-
----
-
-## ⚙️ Configuration (most used)
-
-Set via `-e` or `.env`:
-
-| Variable | Default | Description |
-| ---------------------------- | ----------------------- | ------------------------------------------------------- |
-| `APP_ENV` | `production` | `production`, `staging`, `development` |
-| `APP_DEBUG` | `false` | Verbose errors (use only in dev) |
-| `PHP_MEMORY_LIMIT` | `256M` | Per‑request memory limit |
-| `PHP_UPLOAD_MAX_FILESIZE` | `100M` | Upload size |
-| `PHP_DATE_TIMEZONE` | `UTC/America/Sao_Paulo` | Server timezone |
-| `PHP_FPM_PM` | `dynamic`* | FPM process manager (*auto‑forced to `static` in prod*) |
-| `PHP_FPM_PM_MAX_CHILDREN` | `60` | FPM workers (increase in prod) |
-| `NGINX_CLIENT_MAX_BODY_SIZE` | `100M` | Request body limit |
-| `PHP_SESSION_SAVE_HANDLER` | `redis` | `redis` or `files` |
-| `PHP_SESSION_SAVE_PATH` | `tcp://redis:6379` | Session DSN |
-| `PHP_OPCACHE_ENABLE` | `1` | Enable OPcache |
-| `PHP_OPCACHE_JIT` | `tracing` | JIT mode |
-
-> See the **full matrix** in the GitHub repo’s `.env.example` and docs.
-
----
-
-## 🏷️ Tags
-
-* `1.2.0` – latest stable
-* `latest` – tracks the most recent stable release
-* `X.Y` (e.g., `1.2`) – latest patch within the minor series
-* `X` (e.g., `1`) – latest minor within the major series
-* `test` – image variant with extended health checks for testing
-
-Pull a specific tag:
-
-```bash
-docker pull kariricode/php-api-stack:latest
-```
-
----
-
-## 🔍 Health Check
-
-* **Simple**: lightweight JSON at `/health.php`
-* **Comprehensive**: build with `HEALTH_CHECK_TYPE=comprehensive` for deeper PHP/Redis checks.
-
-Runtime verification:
-
-```bash
-curl -fsS http://localhost:8080/health.php | jq
-```
-
----
-
-## 🧪 Troubleshooting
-
-**Container fails to start**
-
-```bash
-docker logs
-```
-
-**502 Bad Gateway** (FPM not responding)
-
-```bash
-docker exec ps aux | grep php-fpm
-
-docker exec tail -f /var/log/php/fpm-error.log
-```
-
-**Slow performance**
-
-```bash
-docker stats
-
-docker exec php -r "print_r(opcache_get_status()['opcache_statistics']['opcache_hit_rate']);"
-```
-
----
-
-## 🔐 Security Notes
-
-* Runs services without root privileges; hardened defaults
-* Enable/add headers via `SECURITY_HEADERS`, `SECURITY_CSP`, `SECURITY_HSTS_MAX_AGE`
-* Prefer immutable deployments in prod (`PHP_OPCACHE_VALIDATE_TIMESTAMPS=0`)
-
----
-
-## 🧭 Roadmap & Contributing
-
-Feature requests and PRs are welcome in the source repository:
-
-* GitHub: [https://github.com/kariricode/php-api-stack](https://github.com/kariricode/php-api-stack)
-
-For broader ecosystem projects, visit:
-
-* KaririCode Framework: [https://github.com/KaririCode-Framework](https://github.com/KaririCode-Framework)
-
----
-
-## 📝 Changelog (excerpt)
-
-**1.2.0**
-
-* PHP 8.4, Nginx 1.27.3, Redis 7.2
-* Socket‑based PHP‑FPM; OPcache + JIT optimized
-* `/health.php` endpoint; improved entrypoint & config processor
-* Extensive env‑var configuration for Nginx/PHP/Redis
-
-> Full release notes are available in the GitHub repository.
-
----
-
-## 📄 License
-
-See `LICENSE` in the source repository.
-
----
-
-## 🙌 Credits
-
-Made with 💚 by **KaririCode** — [https://kariricode.org/](https://kariricode.org/)
diff --git a/Dockerfile b/Dockerfile
index 554d0c2..da8a367 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,12 +1,16 @@
-# Multi-stage build for kariricode/php-api-stack — FINAL (lint‑clean, syntax‑fixed)
+# ============================================================================
+# Multi-stage build for kariricode/php-api-stack
# Production-ready PHP + Nginx + Redis (+ optional Dev tooling)
-# Notes:
-# - Avoid inline comments after a trailing backslash (\). They break line continuation.
-# - Hadolint DL3018 is selectively ignored where pinning is impractical; see comments.
+# Architecture: Base → Production | Dev
+# ============================================================================
+# Author: KaririCode
+# License: MIT
+# Repository: https://github.com/kariricode/php-api-stack
+# ============================================================================
-# ------------------------------------------------------------
-# Versions (overridable via build args)
-# ------------------------------------------------------------
+# ----------------------------------------------------------------------------
+# Build Arguments - Component Versions
+# ----------------------------------------------------------------------------
ARG PHP_VERSION=8.4
ARG NGINX_VERSION=1.27.3
ARG REDIS_VERSION=7.2
@@ -19,42 +23,62 @@ ARG VERSION=1.2.0
ARG BUILD_DATE
ARG VCS_REF
-# ------------------------------------------------------------
-# Stage: Redis binaries (from official image)
-# ------------------------------------------------------------
+# ----------------------------------------------------------------------------
+# PECL Extension Versions
+# Reference: https://pecl.php.net/
+# ----------------------------------------------------------------------------
+ARG PHP_REDIS_VERSION=6.1.0
+ARG PHP_APCU_VERSION=5.1.24
+ARG PHP_UUID_VERSION=1.2.1
+ARG PHP_IMAGICK_VERSION=3.7.0
+ARG PHP_AMQP_VERSION=2.1.2
+ARG XDEBUG_VERSION=3.4.6
+
+# ============================================================================
+# Stage: Redis Binaries (from official image)
+# ============================================================================
FROM redis:${REDIS_VERSION}-alpine AS redis-build
-# ------------------------------------------------------------
-# Stage: Nginx binaries (from official image)
-# ------------------------------------------------------------
+# ============================================================================
+# Stage: Nginx Binaries (from official image)
+# ============================================================================
FROM nginx:${NGINX_VERSION}-alpine AS nginx-build
-# ------------------------------------------------------------
-# Stage: Base (production runtime)
-# ------------------------------------------------------------
+# ============================================================================
+# Stage: Base - Common runtime foundation (used by both production and dev)
+# ============================================================================
FROM php:${PHP_VERSION}-fpm-alpine${ALPINE_VERSION} AS base
SHELL ["/bin/ash", "-o", "pipefail", "-c"]
+# ----------------------------------------------------------------------------
# Propagate build args to this stage
+# CRITICAL: Must be declared BEFORE any RUN that uses them
+# ----------------------------------------------------------------------------
ARG PHP_VERSION
-ARG PHP_OPCACHE_VALIDATE_TIMESTAMPS
-ARG PHP_OPCACHE_MAX_ACCELERATED_FILES
-ARG PHP_OPCACHE_ENABLE
-ARG PHP_OPCACHE_MEMORY_CONSUMPTION
ARG NGINX_VERSION
ARG REDIS_VERSION
ARG ALPINE_VERSION
ARG COMPOSER_VERSION
-ARG SYMFONY_CLI_VERSION
ARG VERSION
ARG BUILD_DATE
ARG VCS_REF
-# Feature flags
-ARG HEALTH_CHECK_TYPE=simple
+# PECL extension versions
+ARG PHP_REDIS_VERSION
+ARG PHP_APCU_VERSION
+ARG PHP_UUID_VERSION
+ARG PHP_IMAGICK_VERSION
+ARG PHP_AMQP_VERSION
+
+# Extension lists (defaults can be overridden by --build-arg)
+ARG PHP_CORE_EXTENSIONS="pdo pdo_mysql opcache intl zip bcmath gd mbstring xml sockets"
+ARG PHP_PECL_EXTENSIONS="redis apcu uuid"
+# ----------------------------------------------------------------------------
# Labels for OCI metadata and traceability
+# Reference: https://github.com/opencontainers/image-spec/blob/main/annotations.md
+# ----------------------------------------------------------------------------
LABEL maintainer="KaririCode " \
org.opencontainers.image.title="PHP API Stack" \
org.opencontainers.image.description="Production-ready PHP + Nginx + Redis + Symfony stack" \
@@ -62,46 +86,69 @@ LABEL maintainer="KaririCode " \
org.opencontainers.image.created="${BUILD_DATE}" \
org.opencontainers.image.revision="${VCS_REF}" \
org.opencontainers.image.source="https://github.com/kariricode/php-api-stack" \
+ org.opencontainers.image.licenses="MIT" \
+ stack.version="${VERSION}" \
stack.php.version="${PHP_VERSION}" \
stack.nginx.version="${NGINX_VERSION}" \
stack.redis.version="${REDIS_VERSION}" \
- stack.version="${VERSION}"
+ stack.alpine.version="${ALPINE_VERSION}" \
+ stack.composer.version="${COMPOSER_VERSION}" \
+ stack.php.redis.version="${PHP_REDIS_VERSION}" \
+ stack.php.apcu.version="${PHP_APCU_VERSION}" \
+ stack.php.uuid.version="${PHP_UUID_VERSION}"
+# ----------------------------------------------------------------------------
# Environment defaults for Composer, PHP, and stack
+# ----------------------------------------------------------------------------
ENV COMPOSER_ALLOW_SUPERUSER=1 \
COMPOSER_HOME=/composer \
- PATH="/composer/vendor/bin:/symfony/bin:/usr/local/bin:/usr/sbin:/sbin:$PATH" \
- PHP_OPCACHE_VALIDATE_TIMESTAMPS=${PHP_OPCACHE_VALIDATE_TIMESTAMPS} \
- PHP_OPCACHE_MAX_ACCELERATED_FILES=${PHP_OPCACHE_MAX_ACCELERATED_FILES} \
- PHP_OPCACHE_MEMORY_CONSUMPTION=${PHP_OPCACHE_MEMORY_CONSUMPTION} \
- PHP_OPCACHE_ENABLE=${PHP_OPCACHE_ENABLE} \
+ PATH="/usr/bin:/usr/local/bin:/composer/vendor/bin:/symfony/bin:/usr/sbin:/sbin:$PATH" \
STACK_VERSION=${VERSION}
-# ------------------------------------------------------------
+# ----------------------------------------------------------------------------
# System runtime dependencies
+# Reference: https://pkgs.alpinelinux.org/packages
# hadolint ignore=DL3018
+# ----------------------------------------------------------------------------
RUN set -eux; \
apk update; \
- # process control and init
+ \
+ # Process control and init
apk add --no-cache bash shadow su-exec tini; \
- # network / TLS
+ \
+ # Network / TLS
apk add --no-cache git curl wget ca-certificates openssl; \
- # misc runtime libs
- apk add --no-cache gettext tzdata pcre2 zlib unzip; \
- # image/zip/xml/icu
+ \
+ # Misc runtime libs
+ apk add --no-cache gettext tzdata pcre2 zlib p7zip; \
+ \
+ # Image/zip/xml/icu runtime libs
apk add --no-cache icu-libs libzip libpng libjpeg-turbo freetype libxml2; \
- update-ca-certificates \
\
- git config --global --add safe.directory /var/www/html
+ # Security: Remove tar to mitigate CVE-2025-45582
+ apk del tar 2>/dev/null || true; \
+ \
+ update-ca-certificates; \
+ \
+ # Git safe directory for mounted volumes
+ git config --global --add safe.directory /var/www/html; \
+ \
+ echo "✓ System dependencies installed successfully"
-# ------------------------------------------------------------
-# Users & directories (least privilege)
-# SC3009-safe: no brace expansion; explicit dirs listed
+# ----------------------------------------------------------------------------
+# Users & directories (least privilege principle)
+# Reference: https://docs.docker.com/develop/develop-images/dockerfile_best-practices/#user
+# ----------------------------------------------------------------------------
RUN set -eux; \
- addgroup -g 101 -S nginx || true; \
- adduser -u 101 -S -D -H -h /var/cache/nginx -s /sbin/nologin -G nginx -g nginx nginx || true; \
- addgroup -S redis || true; \
- adduser -S -D -h /var/lib/redis -s /sbin/nologin -G redis redis || true; \
+ # Create nginx user/group (idempotent)
+ addgroup -g 101 -S nginx 2>/dev/null || true; \
+ adduser -u 101 -S -D -H -h /var/cache/nginx -s /sbin/nologin -G nginx -g nginx nginx 2>/dev/null || true; \
+ \
+ # Create redis user/group (idempotent)
+ addgroup -S redis 2>/dev/null || true; \
+ adduser -S -D -h /var/lib/redis -s /sbin/nologin -G redis redis 2>/dev/null || true; \
+ \
+ # Create directory structure
install -d -m 0755 \
/var/www/html/public \
/var/www/html/var \
@@ -114,7 +161,6 @@ RUN set -eux; \
/run/nginx \
/etc/nginx \
/etc/nginx/conf.d \
- /var/cache/nginx \
/var/cache/nginx/fastcgi \
/var/cache/nginx/proxy \
/var/cache/nginx/client_temp \
@@ -124,47 +170,176 @@ RUN set -eux; \
/var/cache/nginx/scgi_temp \
/var/lib/redis \
/tmp/symfony; \
- chown -R nginx:nginx /var/www/html /var/log/nginx /var/cache/nginx /run/nginx /var/run/php /var/log/symfony; \
+ \
+ # Set ownership
+ chown -R nginx:nginx \
+ /var/www/html \
+ /var/log/nginx \
+ /var/log/symfony \
+ /var/cache/nginx \
+ /run/nginx \
+ /var/run/php; \
chown -R redis:redis /var/lib/redis /var/log/redis; \
+ \
+ # Ensure correct permissions
chmod 0755 /tmp/symfony; \
- chmod -R 0755 /var/www/html
+ chmod -R 0755 /var/www/html; \
+ \
+ echo "✓ Users and directories configured successfully"
-# ------------------------------------------------------------
-# PHP core extensions (built from source with temporary deps)
+# ----------------------------------------------------------------------------
+# PHP Core Extensions (built from source)
+# Reference: https://github.com/docker-library/docs/blob/master/php/README.md
# hadolint ignore=DL3018
-ARG PHP_CORE_EXTENSIONS="pdo pdo_mysql opcache intl zip bcmath gd mbstring xml"
+# ----------------------------------------------------------------------------
RUN set -eux; \
- apk add --no-cache --virtual .build-deps \
- $PHPIZE_DEPS \
- icu-dev libzip-dev libpng-dev libjpeg-turbo-dev freetype-dev \
- libxml2-dev curl-dev oniguruma-dev postgresql-dev linux-headers; \
+ echo "Installing PHP core extensions: ${PHP_CORE_EXTENSIONS}"; \
+ \
+ # Install build dependencies as virtual package
+ apk add --no-cache --virtual .build-deps \
+ $PHPIZE_DEPS \
+ icu-dev \
+ libzip-dev \
+ libpng-dev \
+ libjpeg-turbo-dev \
+ freetype-dev \
+ libxml2-dev \
+ curl-dev \
+ oniguruma-dev \
+ postgresql-dev \
+ linux-headers; \
+ \
+ # Configure GD if present in extension list
if echo " ${PHP_CORE_EXTENSIONS} " | grep -q " gd "; then \
+ echo "Configuring GD with FreeType and JPEG support..."; \
docker-php-ext-configure gd --with-freetype --with-jpeg; \
fi; \
- # Intentional word splitting for extension list
+ \
+ # Install extensions (intentional word splitting)
# shellcheck disable=SC2086
docker-php-ext-install -j"$(nproc)" ${PHP_CORE_EXTENSIONS}; \
+ \
+ # Verify installation
+ echo "Verifying core extensions..."; \
+ for ext in ${PHP_CORE_EXTENSIONS}; do \
+ # OPcache is a Zend extension, check differently
+ if [ "${ext}" = "opcache" ]; then \
+ if ! php -v | grep -qi "Zend OPcache"; then \
+ echo "ERROR: Extension ${ext} not loaded!" >&2; \
+ php -v; \
+ exit 1; \
+ fi; \
+ elif ! php -m | grep -qi "^${ext}$"; then \
+ echo "ERROR: Extension ${ext} not loaded!" >&2; \
+ echo "Available extensions:"; \
+ php -m; \
+ exit 1; \
+ fi; \
+ echo " ✓ ${ext}"; \
+ done; \
+ \
+ # Cleanup
apk del .build-deps; \
- php -m | sed 's/^/ -> /'
+ rm -rf /tmp/*; \
+ \
+ echo "✓ Core extensions installed successfully"; \
+ php -m | sed 's/^/ → /'
-# ------------------------------------------------------------
-# PECL extensions (prod-safe)
+# ----------------------------------------------------------------------------
+# PECL Extensions (production-safe)
+# Reference: https://pecl.php.net/
# hadolint ignore=DL3018
-ARG PHP_PECL_EXTENSIONS="redis apcu uuid"
+# ----------------------------------------------------------------------------
RUN set -eux; \
- apk add --no-cache --virtual .pecl-build-deps "$PHPIZE_DEPS" util-linux-dev || true; \
+ echo "Installing PECL extensions: ${PHP_PECL_EXTENSIONS}"; \
+ echo "Extension versions:"; \
+ echo " - Redis: ${PHP_REDIS_VERSION}"; \
+ echo " - APCu: ${PHP_APCU_VERSION}"; \
+ echo " - UUID: ${PHP_UUID_VERSION}"; \
+ echo " - ImageMagick: ${PHP_IMAGICK_VERSION}"; \
+ echo " - AMQP: ${PHP_AMQP_VERSION}"; \
+ \
+ # Install build dependencies as virtual package
+ apk add --no-cache --virtual .pecl-build-deps \
+ $PHPIZE_DEPS \
+ util-linux-dev; \
+ \
+ # Install runtime dependencies (kept after build)
+ apk add --no-cache \
+ util-linux; \
+ \
+ # Install each extension with version pinning
for ext in ${PHP_PECL_EXTENSIONS}; do \
- case "$ext" in \
- imagick) apk add --no-cache imagemagick-dev ;; \
- amqp) apk add --no-cache rabbitmq-c-dev ;; \
+ echo "Processing PECL extension: ${ext}"; \
+ \
+ case "${ext}" in \
+ redis) \
+ pecl install "redis-${PHP_REDIS_VERSION}" && \
+ docker-php-ext-enable redis \
+ ;; \
+ apcu) \
+ pecl install "apcu-${PHP_APCU_VERSION}" && \
+ docker-php-ext-enable apcu \
+ ;; \
+ uuid) \
+ pecl install "uuid-${PHP_UUID_VERSION}" && \
+ docker-php-ext-enable uuid \
+ ;; \
+ imagick) \
+ apk add --no-cache imagemagick-dev imagemagick && \
+ pecl install "imagick-${PHP_IMAGICK_VERSION}" && \
+ docker-php-ext-enable imagick \
+ ;; \
+ amqp) \
+ apk add --no-cache rabbitmq-c-dev rabbitmq-c && \
+ pecl install "amqp-${PHP_AMQP_VERSION}" && \
+ docker-php-ext-enable amqp \
+ ;; \
+ xdebug) \
+ echo "Skipping xdebug in base stage (handled in dev stage)" \
+ ;; \
+ *) \
+ echo "ERROR: Unknown PECL extension: ${ext}" >&2; \
+ echo "Available: redis, apcu, uuid, imagick, amqp, xdebug" >&2; \
+ exit 1 \
+ ;; \
+ esac; \
+ done; \
+ \
+ # Verify installation
+ echo "Verifying PECL extensions..."; \
+ for ext in ${PHP_PECL_EXTENSIONS}; do \
+ # Skip xdebug verification in base stage
+ if [ "${ext}" = "xdebug" ]; then continue; fi; \
+ \
+ if ! php -m | grep -qi "^${ext}$"; then \
+ echo "ERROR: Extension ${ext} not loaded!" >&2; \
+ echo "Available extensions:"; \
+ php -m; \
+ exit 1; \
+ fi; \
+ \
+ # Show installed version with proper error handling
+ case "${ext}" in \
+ redis|apcu|uuid|imagick) \
+ php -r "echo ' ✓ ${ext} ' . phpversion('${ext}') . \"\n\";" || echo " ✓ ${ext} (version check failed)" \
+ ;; \
+ *) \
+ echo " ✓ ${ext}" \
+ ;; \
esac; \
- if pecl install "$ext"; then docker-php-ext-enable "$ext"; else echo "[warn] PECL $ext failed (non-fatal)"; fi; \
done; \
- pecl clear-cache; rm -rf /tmp/pear
+ \
+ # Cleanup
+ pecl clear-cache; \
+ rm -rf /tmp/pear ~/.pearrc /tmp/*; \
+ apk del .pecl-build-deps; \
+ \
+ echo "✓ PECL extensions installed successfully"
-# ------------------------------------------------------------
+# ----------------------------------------------------------------------------
# Copy Nginx & Redis binaries from official builds
-# ------------------------------------------------------------
+# ----------------------------------------------------------------------------
COPY --from=nginx-build /usr/sbin/nginx /usr/sbin/nginx
COPY --from=nginx-build /usr/lib/nginx /usr/lib/nginx
COPY --from=nginx-build /etc/nginx/mime.types /etc/nginx/mime.types
@@ -174,23 +349,51 @@ COPY --from=nginx-build /etc/nginx/scgi_params /etc/nginx/scgi_params
COPY --from=nginx-build /etc/nginx/uwsgi_params /etc/nginx/uwsgi_params
COPY --from=redis-build /usr/local/bin/redis-* /usr/local/bin/
-# ------------------------------------------------------------
+# Verify binaries
+RUN set -eux; \
+ nginx -v 2>&1 | sed 's/^/ → /'; \
+ redis-server --version | sed 's/^/ → /'; \
+ echo "✓ Nginx and Redis binaries copied successfully"
+
+# ----------------------------------------------------------------------------
# Composer (signature verified)
-# ------------------------------------------------------------
+# Reference: https://getcomposer.org/doc/faqs/how-to-install-composer-programmatically.md
+# ----------------------------------------------------------------------------
RUN set -eux; \
+ echo "Installing Composer ${COMPOSER_VERSION}..."; \
mkdir -p /composer; \
+ \
+ # Download and verify installer
EXPECTED_CHECKSUM="$(wget -q -O - https://composer.github.io/installer.sig)"; \
wget -q -O composer-setup.php https://getcomposer.org/installer; \
ACTUAL_CHECKSUM="$(php -r "echo hash_file('sha384','composer-setup.php');")"; \
- if [ "$EXPECTED_CHECKSUM" != "$ACTUAL_CHECKSUM" ]; then echo "Composer installer checksum mismatch" >&2; exit 1; fi; \
- php composer-setup.php --install-dir=/usr/local/bin --filename=composer --version="${COMPOSER_VERSION}"; \
+ \
+ # Fail-fast on checksum mismatch
+ if [ "${EXPECTED_CHECKSUM}" != "${ACTUAL_CHECKSUM}" ]; then \
+ echo "ERROR: Composer installer checksum mismatch!" >&2; \
+ echo "Expected: ${EXPECTED_CHECKSUM}" >&2; \
+ echo "Got: ${ACTUAL_CHECKSUM}" >&2; \
+ exit 1; \
+ fi; \
+ \
+ # Install Composer
+ php composer-setup.php \
+ --install-dir=/usr/local/bin \
+ --filename=composer \
+ --version="${COMPOSER_VERSION}"; \
+ \
+ # Cleanup
rm -f composer-setup.php; \
- composer --version; \
- chmod 0775 /composer
+ chmod 0775 /composer; \
+ \
+ # Verify installation
+ composer --version | sed 's/^/ → /'; \
+ echo "✓ Composer installed successfully"
-# ------------------------------------------------------------
+# ----------------------------------------------------------------------------
# PHP-FPM socket tuning (for Nginx integration)
-# ------------------------------------------------------------
+# Reference: https://www.php.net/manual/en/install.fpm.configuration.php
+# ----------------------------------------------------------------------------
RUN set -eux; \
{ \
echo '[www]'; \
@@ -199,11 +402,14 @@ RUN set -eux; \
echo 'listen.group = nginx'; \
echo 'listen.mode = 0660'; \
echo 'listen.backlog = 511'; \
- } > /usr/local/etc/php-fpm.d/zzz-socket-override.conf
+ } > /usr/local/etc/php-fpm.d/zzz-socket-override.conf; \
+ \
+ echo "✓ PHP-FPM socket configuration applied"; \
+ cat /usr/local/etc/php-fpm.d/zzz-socket-override.conf | sed 's/^/ → /'
-# ------------------------------------------------------------
+# ----------------------------------------------------------------------------
# Config templates & helper scripts (processed by entrypoint)
-# ------------------------------------------------------------
+# ----------------------------------------------------------------------------
COPY nginx/nginx.conf /etc/nginx/nginx.conf.template
COPY nginx/default.conf /etc/nginx/conf.d/default.conf.template
COPY php/php.ini /usr/local/etc/php/php.ini.template
@@ -211,29 +417,50 @@ COPY php/php-fpm.conf /usr/local/etc/php-fpm.conf.template
COPY php/www.conf /usr/local/etc/php-fpm.d/www.conf.template
COPY php/monitoring.conf /usr/local/etc/php-fpm.d/monitoring.conf.template
COPY redis/redis.conf /etc/redis/redis.conf.template
+COPY php/xdebug.ini /usr/local/etc/php/conf.d/xdebug.ini.template
COPY docker-entrypoint.sh /usr/local/bin/docker-entrypoint
COPY scripts/process-configs.sh /usr/local/bin/process-configs
COPY scripts/quick-start.sh /usr/local/bin/quick-start
+
RUN chmod +x /usr/local/bin/docker-entrypoint /usr/local/bin/process-configs /usr/local/bin/quick-start
-# ------------------------------------------------------------
-# Healthcheck templates (simple/comprehensive)
-# ------------------------------------------------------------
-COPY --chown=nginx:nginx index.php /usr/local/share/php-api-stack/index.php
-COPY --chown=nginx:nginx health.php /usr/local/share/php-api-stack/health-comprehensive.php
+# ----------------------------------------------------------------------------
+# Healthcheck templates (staged, published at runtime)
+# ----------------------------------------------------------------------------
+RUN install -d -m 0755 /opt/php-api-stack-templates
+COPY --chown=nginx:nginx php/index.php /opt/php-api-stack-templates/index.php
+COPY --chown=nginx:nginx php/health.php /opt/php-api-stack-templates/health.php
+
+# Validate syntax at build time
RUN set -eux; \
- install -d -m 0755 /usr/local/share/php-api-stack; \
- if [ "$HEALTH_CHECK_TYPE" = "comprehensive" ]; then \
- cp /usr/local/share/php-api-stack/health-comprehensive.php /usr/local/share/php-api-stack/health.php; \
- else \
- printf "'healthy','timestamp'=>date('c')]);\\n" > /usr/local/share/php-api-stack/health.php; \
- fi; \
- php -l /usr/local/share/php-api-stack/health.php; \
- php -l /usr/local/share/php-api-stack/index.php
+ php -l /opt/php-api-stack-templates/index.php; \
+ php -l /opt/php-api-stack-templates/health.php; \
+ echo "✓ Healthcheck templates validated successfully"
+
+WORKDIR /var/www/html
+
+# ============================================================================
+# Stage: Production - Optimized production runtime
+# ============================================================================
+FROM base AS production
+
+# Propagate production-specific build args
+ARG PHP_OPCACHE_VALIDATE_TIMESTAMPS=0
+ARG PHP_OPCACHE_MAX_ACCELERATED_FILES=20000
+ARG PHP_OPCACHE_ENABLE=1
+ARG PHP_OPCACHE_MEMORY_CONSUMPTION=256
+
+# Production environment variables
+ENV APP_ENV=production \
+ APP_DEBUG=false \
+ PHP_OPCACHE_VALIDATE_TIMESTAMPS=${PHP_OPCACHE_VALIDATE_TIMESTAMPS} \
+ PHP_OPCACHE_MAX_ACCELERATED_FILES=${PHP_OPCACHE_MAX_ACCELERATED_FILES} \
+ PHP_OPCACHE_MEMORY_CONSUMPTION=${PHP_OPCACHE_MEMORY_CONSUMPTION} \
+ PHP_OPCACHE_ENABLE=${PHP_OPCACHE_ENABLE}
-# Materialize configs at build time (useful for some CIs)
+# Materialize configs at build time for production
RUN /usr/local/bin/process-configs
# Healthcheck script used by Docker HEALTHCHECK
@@ -245,36 +472,95 @@ RUN set -eux; \
HEALTHCHECK --interval=30s --timeout=3s --start-period=10s --retries=3 \
CMD /usr/local/bin/healthcheck
-WORKDIR /var/www/html
EXPOSE 80 443
VOLUME ["/var/www/html", "/var/log", "/var/lib/redis"]
ENTRYPOINT ["/sbin/tini", "--", "docker-entrypoint"]
CMD ["start"]
-# ======================================================================
-# Stage: dev — extra tools only for development
-# ======================================================================
+# ============================================================================
+# Stage: Dev - Development with additional tooling
+# ============================================================================
FROM base AS dev
+# Propagate dev-specific build args
ARG SYMFONY_CLI_VERSION
-ARG ENABLE_XDEBUG=0
+ARG XDEBUG_VERSION
+ARG XDEBUG_ENABLE=1
+ARG APP_ENV=development
+
+# Development environment variables
+ENV APP_ENV=development \
+ APP_DEBUG=true \
+ XDEBUG_ENABLE=${XDEBUG_ENABLE} \
+ PHP_OPCACHE_VALIDATE_TIMESTAMPS=1 \
+ PHP_OPCACHE_REVALIDATE_FREQ=0
+# ----------------------------------------------------------------------------
+# Install development tools and build dependencies
+# Consolidate all build-time dependencies in a single layer
# hadolint ignore=DL3018
+# ----------------------------------------------------------------------------
RUN set -eux; \
+ echo "Setting up development environment..."; \
+ \
+ # 1. Install runtime dev tools (kept after build)
apk add --no-cache procps htop; \
- wget -q -O /tmp/symfony-installer https://get.symfony.com/cli/installer; \
- bash /tmp/symfony-installer; \
- mv /root/.symfony*/bin/symfony /usr/local/bin/symfony; \
+ \
+ # 2. Install ALL build-time dependencies as a single virtual package
+ apk add --no-cache --virtual .dev-build-deps \
+ $PHPIZE_DEPS \
+ build-base \
+ curl \
+ tar \
+ linux-headers; \
+ \
+ # 3. Install Symfony CLI
+ if [ "${APP_ENV}" = "development" ]; then \
+ echo "Installing Symfony CLI v${SYMFONY_CLI_VERSION}..."; \
+ wget -q -O /tmp/symfony.tar.gz \
+ "https://github.com/symfony-cli/symfony-cli/releases/download/v${SYMFONY_CLI_VERSION}/symfony-cli_linux_amd64.tar.gz"; \
+ tar -xzf /tmp/symfony.tar.gz -C /usr/local/bin symfony; \
chmod +x /usr/local/bin/symfony; \
- rm -rf /root/.symfony* /tmp/symfony-installer; \
- symfony version || true
+ rm /tmp/symfony.tar.gz; \
+ symfony version | sed 's/^/ → /'; \
+ fi; \
+ \
+ # 4. Install Xdebug (manual compile with version pinning)
+ if [ "${APP_ENV}" = "development" ] && [ "${XDEBUG_ENABLE}" = "1" ]; then \
+ echo "Installing Xdebug v${XDEBUG_VERSION}..."; \
+ curl -fsSL "https://pecl.php.net/get/xdebug-${XDEBUG_VERSION}.tgz" -o xdebug.tgz; \
+ tar -xzf xdebug.tgz; \
+ cd "xdebug-${XDEBUG_VERSION}"; \
+ phpize; \
+ ./configure; \
+ make -j"$(nproc)"; \
+ make install; \
+ cd ..; \
+ rm -rf xdebug.tgz "xdebug-${XDEBUG_VERSION}"; \
+ echo " ✓ Xdebug ${XDEBUG_VERSION} installed successfully"; \
+ else \
+ echo "Skipping Xdebug install (APP_ENV=${APP_ENV} XDEBUG_ENABLE=${XDEBUG_ENABLE})"; \
+ fi; \
+ \
+ # 5. Cleanup all build dependencies
+ apk del .dev-build-deps; \
+ rm -rf /tmp/*; \
+ \
+ echo "✓ Development environment setup complete"
-# Optional Xdebug installation for local debugging
-# hadolint ignore=DL3018
+# Materialize configs for development
+RUN /usr/local/bin/process-configs
+
+# Development healthcheck (more verbose for debugging)
RUN set -eux; \
- if [ "${ENABLE_XDEBUG}" = "1" ]; then \
- apk add --no-cache --virtual .xd-build "$PHPIZE_DEPS"; \
- pecl install xdebug; \
- docker-php-ext-enable xdebug; \
- apk del --no-cache .xd-build || true; \
- fi
+ printf '#!/bin/sh\n' > /usr/local/bin/healthcheck; \
+ printf 'curl -f http://localhost/health || exit 1\n' >> /usr/local/bin/healthcheck; \
+ chmod +x /usr/local/bin/healthcheck
+
+HEALTHCHECK --interval=30s --timeout=5s --start-period=15s --retries=3 \
+ CMD /usr/local/bin/healthcheck
+
+EXPOSE 80 443 9003
+VOLUME ["/var/www/html", "/var/log", "/var/lib/redis"]
+ENTRYPOINT ["/sbin/tini", "--", "docker-entrypoint"]
+CMD ["start"]
\ No newline at end of file
diff --git a/IMAGE_USAGE_GUIDE.md b/IMAGE_USAGE_GUIDE.md
index 2dd8a81..4187034 100644
--- a/IMAGE_USAGE_GUIDE.md
+++ b/IMAGE_USAGE_GUIDE.md
@@ -1,22 +1,28 @@
# Image Usage Guide - PHP API Stack
**Audience**: End users and developers
-**Purpose**: Complete guide for using the published Docker image
+**Purpose**: Complete guide for using the published Docker image
+**Version**: 1.5.0
+
+---
## 📋 Table of Contents
-- [Quick Start](#quick-start)
-- [Installation](#installation)
-- [Basic Usage](#basic-usage)
-- [Configuration](#configuration)
-- [Framework Integration](#framework-integration)
-- [Docker Compose](#docker-compose)
-- [Kubernetes Deployment](#kubernetes-deployment)
-- [Production Setup](#production-setup)
-- [Monitoring and Health](#monitoring-and-health)
-- [Performance Tuning](#performance-tuning)
-- [Troubleshooting](#troubleshooting)
-- [Examples](#examples)
+- [Quick Start](#-quick-start)
+- [Installation](#-installation)
+- [Basic Usage](#-basic-usage)
+- [Configuration](#%EF%B8%8F-configuration)
+- [Framework Integration](#-framework-integration)
+- [Docker Compose](#-docker-compose)
+- [Kubernetes Deployment](#%EF%B8%8F-kubernetes-deployment)
+- [Production Setup](#-production-setup)
+- [Monitoring and Health](#-monitoring-and-health)
+- [Performance Tuning](#-performance-tuning)
+- [Troubleshooting](#-troubleshooting)
+- [Examples](#-examples)
+- [Best Practices](#-best-practices)
+
+---
## 🚀 Quick Start
@@ -25,13 +31,13 @@
```bash
# Pull and run
docker pull kariricode/php-api-stack:latest
-docker run -d -p 8080:80 kariricode/php-api-stack:latest
+docker run -d -p 8080:80 --name my-app kariricode/php-api-stack:latest
# Test
curl http://localhost:8080
```
-**That's it!** You'll see the demo landing page showing stack status.
+**That's it!** You'll see the demo landing page showing stack status and component information.
### With Your Application
@@ -47,21 +53,27 @@ docker run -d \
curl http://localhost:8080
```
+---
+
## 📥 Installation
### Prerequisites
- **Docker**: Version 20.10 or higher
-- **Docker Compose**: Version 2.0 or higher (optional)
+- **Docker Compose**: Version 2.0 or higher (optional, for multi-service setups)
+- **Disk Space**: Minimum 300MB for the image
### Pull Image
```bash
-# Latest version
+# Latest stable version
docker pull kariricode/php-api-stack:latest
-# Specific version
-docker pull kariricode/php-api-stack:1.2.1
+# Specific version (recommended for production)
+docker pull kariricode/php-api-stack:1.5.0
+
+# Development version (with Xdebug, Symfony CLI)
+docker pull kariricode/php-api-stack:dev
# Verify
docker images kariricode/php-api-stack
@@ -69,40 +81,55 @@ docker images kariricode/php-api-stack
### Available Tags
-| Tag | Description | When to Use |
-|-----|-------------|-------------|
-| `latest` | Latest stable | Most cases |
-| `stable` | Production release | Production |
-| `1.2.1` | Specific version | Version pinning |
-| `1.2` | Minor version | Auto-patch updates |
-| `1` | Major version | Auto-minor updates |
-| `test` | With comprehensive health | Testing/Monitoring |
+| Tag | Description | Size | When to Use |
+|-----|-------------|------|-------------|
+| `latest` | Latest stable production | ~225MB | Most cases |
+| `1.5.0` | Specific version | ~225MB | Version pinning |
+| `1.5` | Minor version | ~225MB | Auto-patch updates |
+| `1` | Major version | ~225MB | Auto-minor updates |
+| `dev` | Development build | ~244MB | Local development with debugging |
+| `test` | With comprehensive health | ~216MB | Testing/Monitoring |
+
+**Recommendation**: Use specific version tags (`1.5.0`) in production for predictability.
+
+---
## 🎯 Basic Usage
### Standalone Container
+#### Basic Run
+
```bash
-# Basic run
docker run -d \
--name my-php-app \
-p 8080:80 \
kariricode/php-api-stack:latest
+```
+
+#### With Application Volume
-# With volume
+```bash
docker run -d \
--name my-php-app \
-p 8080:80 \
-v $(pwd)/app:/var/www/html \
kariricode/php-api-stack:latest
+```
-# With environment variables
+#### Production Setup
+
+```bash
docker run -d \
--name my-php-app \
- -p 8080:80 \
+ -p 80:80 \
+ -v $(pwd)/app:/var/www/html:ro \
+ -v $(pwd)/logs:/var/log \
-e APP_ENV=production \
+ -e APP_DEBUG=false \
-e PHP_MEMORY_LIMIT=512M \
- -v $(pwd)/app:/var/www/html \
+ -e PHP_OPCACHE_VALIDATE_TIMESTAMPS=0 \
+ --restart unless-stopped \
kariricode/php-api-stack:latest
```
@@ -115,203 +142,396 @@ docker logs -f my-php-app
# Access shell
docker exec -it my-php-app bash
-# Restart services
-docker exec my-php-app supervisorctl restart all
+# Check running processes
+docker exec my-php-app ps aux
+
+# Check PHP version
+docker exec my-php-app php -v
+
+# Check PHP extensions
+docker exec my-php-app php -m
# Stop container
docker stop my-php-app
# Remove container
docker rm my-php-app
+
+# Restart container
+docker restart my-php-app
```
+### Service Management
+
+Services are managed by the custom entrypoint script:
+
+```bash
+# View all processes
+docker exec my-php-app ps aux | grep -E "(nginx|php-fpm|redis)"
+
+# Nginx
+docker exec my-php-app nginx -t # Test config
+docker exec my-php-app nginx -s reload # Reload config
+
+# PHP-FPM
+docker exec my-php-app php-fpm -t # Test config
+docker exec my-php-app kill -USR2 $(cat /var/run/php/php-fpm.pid) # Reload
+
+# Redis
+docker exec my-php-app redis-cli ping # Test connection
+docker exec my-php-app redis-cli info # Get info
+```
+
+---
+
## ⚙️ Configuration
### Environment Variables
-#### PHP Settings
+#### Application Settings
+
+```bash
+# Environment and debug
+-e APP_ENV=production # production|development|test
+-e APP_DEBUG=false # Enable/disable debug mode
+-e APP_NAME=my-application # Application name
+```
+
+#### PHP Runtime Settings
+
```bash
# Memory and execution
--e PHP_MEMORY_LIMIT=512M
--e PHP_MAX_EXECUTION_TIME=60
--e PHP_MAX_INPUT_TIME=60
--e PHP_POST_MAX_SIZE=100M
--e PHP_UPLOAD_MAX_FILESIZE=100M
+-e PHP_MEMORY_LIMIT=512M # Memory limit per request
+-e PHP_MAX_EXECUTION_TIME=60 # Script execution timeout
+-e PHP_MAX_INPUT_TIME=60 # Input parsing timeout
+-e PHP_POST_MAX_SIZE=100M # Max POST data size
+-e PHP_UPLOAD_MAX_FILESIZE=100M # Max file upload size
# Error handling
--e PHP_DISPLAY_ERRORS=Off
--e PHP_ERROR_LOG=/var/log/php/error.log
+-e PHP_DISPLAY_ERRORS=Off # Display errors (On for dev)
+-e PHP_ERROR_LOG=/var/log/php/error.log # Error log path
+-e PHP_LOG_ERRORS=On # Enable error logging
# Timezone
--e PHP_DATE_TIMEZONE=America/New_York
+-e PHP_DATE_TIMEZONE=UTC # Default timezone
```
-#### PHP-FPM Pool
+#### PHP-FPM Pool Configuration
+
```bash
-# Process manager
--e PHP_FPM_PM=dynamic
--e PHP_FPM_PM_MAX_CHILDREN=50
--e PHP_FPM_PM_START_SERVERS=5
--e PHP_FPM_PM_MIN_SPARE_SERVERS=5
--e PHP_FPM_PM_MAX_SPARE_SERVERS=10
--e PHP_FPM_PM_MAX_REQUESTS=500
+# Process manager mode
+-e PHP_FPM_PM=dynamic # static|dynamic|ondemand
+
+# Dynamic mode settings
+-e PHP_FPM_PM_MAX_CHILDREN=60 # Maximum child processes
+-e PHP_FPM_PM_START_SERVERS=10 # Initial server count
+-e PHP_FPM_PM_MIN_SPARE_SERVERS=5 # Minimum idle servers
+-e PHP_FPM_PM_MAX_SPARE_SERVERS=20 # Maximum idle servers
+-e PHP_FPM_PM_MAX_REQUESTS=500 # Max requests per child
+
+# Static mode (for production with predictable load)
+-e PHP_FPM_PM=static
+-e PHP_FPM_PM_MAX_CHILDREN=100 # Fixed number of children
```
-#### OPcache
+#### OPcache Configuration
+
```bash
-# OPcache configuration
--e PHP_OPCACHE_ENABLE=1
--e PHP_OPCACHE_MEMORY=256
--e PHP_OPCACHE_MAX_FILES=20000
--e PHP_OPCACHE_VALIDATE_TIMESTAMPS=0
--e PHP_OPCACHE_JIT=tracing
--e PHP_OPCACHE_JIT_BUFFER_SIZE=128M
+# Enable/disable
+-e PHP_OPCACHE_ENABLE=1 # 1=enabled, 0=disabled
+
+# Memory settings
+-e PHP_OPCACHE_MEMORY=256 # Memory consumption (MB)
+-e PHP_OPCACHE_MAX_ACCELERATED_FILES=20000 # Max cached files
+
+# Validation (0 for production, 1 for development)
+-e PHP_OPCACHE_VALIDATE_TIMESTAMPS=0 # 0=never revalidate, 1=check mtimes
+-e PHP_OPCACHE_REVALIDATE_FREQ=0 # Revalidation frequency (seconds)
+
+# JIT configuration
+-e PHP_OPCACHE_JIT=tracing # off|tracing|function
+-e PHP_OPCACHE_JIT_BUFFER_SIZE=128M # JIT buffer size
```
-#### Application
+#### Nginx Configuration
+
```bash
-# Application settings
--e APP_ENV=production
--e APP_DEBUG=false
--e APP_NAME=my-api
+# Worker configuration
+-e NGINX_WORKER_PROCESSES=auto # auto = CPU cores
+-e NGINX_WORKER_CONNECTIONS=2048 # Connections per worker
+
+# Timeouts
+-e NGINX_KEEPALIVE_TIMEOUT=65 # Keep-alive timeout (seconds)
+-e NGINX_CLIENT_BODY_TIMEOUT=60 # Client body timeout
+-e NGINX_SEND_TIMEOUT=60 # Send timeout
+
+# Limits
+-e NGINX_CLIENT_MAX_BODY_SIZE=20M # Max upload size
+```
+
+#### Redis Configuration
+
+```bash
+# Connection (standalone mode)
+-e REDIS_HOST=127.0.0.1 # Redis host
+-e REDIS_PASSWORD=your-password # Redis password
+-e REDIS_DATABASES=16 # Number of databases
+
+# Memory settings
+-e REDIS_MAXMEMORY=256mb # Max memory usage
+-e REDIS_MAXMEMORY_POLICY=allkeys-lru # Eviction policy
+
+# Persistence
+-e REDIS_APPENDONLY=yes # Enable AOF persistence
+-e REDIS_APPENDFSYNC=everysec # Fsync frequency
```
+**Note**: When using Docker Compose, `REDIS_HOST` should be set to the service name (e.g., `redis`). For standalone containers, it uses `127.0.0.1` (internal Redis).
+
### Volume Mounts
+#### Application Code
+
```bash
-# Application code (read-only in production)
+# Development (read-write)
+-v $(pwd)/app:/var/www/html
+
+# Production (read-only)
-v $(pwd)/app:/var/www/html:ro
+```
-# Logs (for external monitoring)
+#### Logs
+
+```bash
+# Export logs to host
-v $(pwd)/logs:/var/log
-# Custom configurations
--v $(pwd)/php.ini:/usr/local/etc/php/php.ini:ro
--v $(pwd)/nginx.conf:/etc/nginx/nginx.conf:ro
+# Then access:
+# - $(pwd)/logs/nginx/access.log
+# - $(pwd)/logs/nginx/error.log
+# - $(pwd)/logs/php/error.log
+# - $(pwd)/logs/redis/redis.log
+```
+
+#### Custom Configurations
+
+```bash
+# Custom PHP configuration
+-v $(pwd)/custom-php.ini:/usr/local/etc/php/conf.d/99-custom.ini:ro
+
+# Custom Nginx configuration
+-v $(pwd)/custom-nginx.conf:/etc/nginx/conf.d/custom.conf:ro
+
+# Custom Redis configuration (if needed)
+-v $(pwd)/custom-redis.conf:/etc/redis/redis-custom.conf:ro
```
### Network Configuration
```bash
-# Custom network
-docker network create my-network
+# Create custom network
+docker network create my-app-network
-# Run with network
+# Run container in network
docker run -d \
--name my-php-app \
- --network my-network \
+ --network my-app-network \
-p 8080:80 \
kariricode/php-api-stack:latest
+
+# Connect another container
+docker run -d \
+ --name mysql \
+ --network my-app-network \
+ -e MYSQL_ROOT_PASSWORD=secret \
+ mysql:8.0
```
+---
+
## 🚀 Framework Integration
### Symfony Application
#### Project Structure
+
```
my-symfony-app/
├── bin/
├── config/
-├── public/ ← Mount point
+├── public/ ← Document root (Nginx serves from here)
│ └── index.php
├── src/
├── var/
-└── vendor/
+│ ├── cache/
+│ └── log/
+├── vendor/
+├── .env
+└── composer.json
```
#### Docker Run
+
```bash
docker run -d \
--name symfony-app \
-p 8080:80 \
-v $(pwd):/var/www/html \
-e APP_ENV=prod \
- -e APP_SECRET=your-secret-here \
- -e DATABASE_URL=mysql://user:pass@db:3306/dbname \
+ -e APP_SECRET=$(openssl rand -hex 16) \
+ -e DATABASE_URL="mysql://user:pass@db:3306/symfony" \
kariricode/php-api-stack:latest
```
#### With Database
+
```bash
# Create network
-docker network create symfony-net
+docker network create symfony-network
# Run MySQL
docker run -d \
--name symfony-db \
- --network symfony-net \
+ --network symfony-network \
-e MYSQL_ROOT_PASSWORD=root \
-e MYSQL_DATABASE=symfony \
+ -e MYSQL_USER=symfony \
+ -e MYSQL_PASSWORD=symfony \
mysql:8.0
# Run Symfony app
docker run -d \
--name symfony-app \
- --network symfony-net \
+ --network symfony-network \
-p 8080:80 \
-v $(pwd):/var/www/html \
-e APP_ENV=prod \
- -e DATABASE_URL=mysql://root:root@symfony-db:3306/symfony \
+ -e DATABASE_URL="mysql://symfony:symfony@symfony-db:3306/symfony" \
kariricode/php-api-stack:latest
```
-#### Symfony Commands
+#### Common Symfony Commands
+
```bash
-# Cache clear
+# Cache operations
docker exec symfony-app php bin/console cache:clear
+docker exec symfony-app php bin/console cache:warmup
-# Database migrations
+# Database
+docker exec symfony-app php bin/console doctrine:database:create
docker exec symfony-app php bin/console doctrine:migrations:migrate --no-interaction
-# Assets install
+# Assets
docker exec symfony-app php bin/console assets:install public --symlink
+
+# Debug
+docker exec symfony-app php bin/console debug:router
+docker exec symfony-app php bin/console debug:container
```
### Laravel Application
#### Project Structure
+
```
my-laravel-app/
├── app/
├── bootstrap/
├── config/
-├── public/ ← Mount point
+├── database/
+├── public/ ← Document root (Nginx serves from here)
│ └── index.php
├── resources/
├── routes/
-└── storage/
+├── storage/
+│ ├── app/
+│ ├── framework/
+│ └── logs/
+├── vendor/
+├── .env
+└── composer.json
```
#### Docker Run
+
```bash
docker run -d \
--name laravel-app \
-p 8080:80 \
-v $(pwd):/var/www/html \
-e APP_ENV=production \
- -e APP_KEY=base64:your-app-key-here \
+ -e APP_KEY=$(php -r "echo 'base64:'.base64_encode(random_bytes(32));") \
-e DB_CONNECTION=mysql \
-e DB_HOST=db \
-e DB_DATABASE=laravel \
- -e DB_USERNAME=root \
+ -e DB_USERNAME=laravel \
-e DB_PASSWORD=secret \
kariricode/php-api-stack:latest
```
-#### Laravel Commands
+#### With Database and Redis
+
```bash
-# Optimize
-docker exec laravel-app php artisan optimize
+# Create network
+docker network create laravel-network
+
+# Run MySQL
+docker run -d \
+ --name laravel-db \
+ --network laravel-network \
+ -e MYSQL_ROOT_PASSWORD=root \
+ -e MYSQL_DATABASE=laravel \
+ -e MYSQL_USER=laravel \
+ -e MYSQL_PASSWORD=secret \
+ mysql:8.0
+
+# Run Redis (external)
+docker run -d \
+ --name laravel-redis \
+ --network laravel-network \
+ redis:7-alpine
+
+# Run Laravel app
+docker run -d \
+ --name laravel-app \
+ --network laravel-network \
+ -p 8080:80 \
+ -v $(pwd):/var/www/html \
+ -e APP_ENV=production \
+ -e APP_KEY=base64:your-key-here \
+ -e DB_CONNECTION=mysql \
+ -e DB_HOST=laravel-db \
+ -e DB_DATABASE=laravel \
+ -e DB_USERNAME=laravel \
+ -e DB_PASSWORD=secret \
+ -e CACHE_DRIVER=redis \
+ -e SESSION_DRIVER=redis \
+ -e QUEUE_CONNECTION=redis \
+ -e REDIS_HOST=laravel-redis \
+ kariricode/php-api-stack:latest
+```
+
+**Note**: The container has an internal Redis instance on `127.0.0.1:6379`. For external Redis (as shown above), use the container name as host.
-# Cache config
+#### Common Laravel Commands
+
+```bash
+# Optimization
+docker exec laravel-app php artisan optimize
docker exec laravel-app php artisan config:cache
+docker exec laravel-app php artisan route:cache
+docker exec laravel-app php artisan view:cache
-# Migrations
+# Database
docker exec laravel-app php artisan migrate --force
+docker exec laravel-app php artisan db:seed --force
-# Queue worker (if needed)
-docker exec laravel-app supervisorctl start symfony-messenger
+# Queue worker (run in background)
+docker exec -d laravel-app php artisan queue:work --tries=3
+
+# Cache
+docker exec laravel-app php artisan cache:clear
+docker exec laravel-app php artisan config:clear
```
### Custom PHP Application
@@ -319,10 +539,16 @@ docker exec laravel-app supervisorctl start symfony-messenger
```bash
# Simple PHP app structure
my-app/
-└── public/
- ├── index.php
- ├── api.php
- └── assets/
+├── public/
+│ ├── index.php
+│ ├── api.php
+│ └── assets/
+│ ├── css/
+│ └── js/
+├── src/
+│ └── App.php
+├── vendor/
+└── composer.json
# Run
docker run -d \
@@ -332,6 +558,8 @@ docker run -d \
kariricode/php-api-stack:latest
```
+---
+
## 🐳 Docker Compose
### Basic Setup
@@ -351,9 +579,10 @@ services:
- ./app:/var/www/html
- ./logs:/var/log
environment:
- - APP_ENV=production
- - PHP_MEMORY_LIMIT=512M
- - PHP_FPM_PM_MAX_CHILDREN=100
+ APP_ENV: production
+ PHP_MEMORY_LIMIT: 512M
+ PHP_FPM_PM_MAX_CHILDREN: 100
+ REDIS_HOST: 127.0.0.1 # Using internal Redis
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost/health"]
interval: 30s
@@ -362,14 +591,17 @@ services:
restart: unless-stopped
```
-Run:
+Commands:
+
```bash
-docker-compose up -d
-docker-compose logs -f
-docker-compose down
+docker-compose up -d # Start
+docker-compose logs -f # View logs
+docker-compose ps # Status
+docker-compose down # Stop
+docker-compose restart app # Restart service
```
-### With Database (Symfony)
+### With External Database
```yaml
version: '3.8'
@@ -384,8 +616,9 @@ services:
- ./:/var/www/html
- ./logs:/var/log
environment:
- - APP_ENV=prod
- - DATABASE_URL=mysql://symfony:secret@db:3306/symfony
+ APP_ENV: prod
+ DATABASE_URL: mysql://symfony:secret@db:3306/symfony
+ REDIS_HOST: 127.0.0.1 # Internal Redis
depends_on:
db:
condition: service_healthy
@@ -400,10 +633,10 @@ services:
image: mysql:8.0
container_name: symfony-db
environment:
- - MYSQL_DATABASE=symfony
- - MYSQL_USER=symfony
- - MYSQL_PASSWORD=secret
- - MYSQL_ROOT_PASSWORD=root
+ MYSQL_DATABASE: symfony
+ MYSQL_USER: symfony
+ MYSQL_PASSWORD: secret
+ MYSQL_ROOT_PASSWORD: root
volumes:
- db-data:/var/lib/mysql
healthcheck:
@@ -423,14 +656,14 @@ networks:
driver: bridge
```
-### Production Setup
+### Production Configuration
```yaml
version: '3.8'
services:
app:
- image: kariricode/php-api-stack:stable
+ image: kariricode/php-api-stack:1.5.0 # Pin version
container_name: prod-app
ports:
- "80:80"
@@ -438,12 +671,14 @@ services:
- ./app:/var/www/html:ro # Read-only
- ./logs:/var/log
environment:
- - APP_ENV=production
- - APP_DEBUG=false
- - PHP_MEMORY_LIMIT=512M
- - PHP_FPM_PM=static
- - PHP_FPM_PM_MAX_CHILDREN=100
- - PHP_OPCACHE_VALIDATE_TIMESTAMPS=0
+ APP_ENV: production
+ APP_DEBUG: "false"
+ PHP_MEMORY_LIMIT: 512M
+ PHP_FPM_PM: static
+ PHP_FPM_PM_MAX_CHILDREN: 100
+ PHP_OPCACHE_VALIDATE_TIMESTAMPS: "0"
+ PHP_OPCACHE_JIT: tracing
+ REDIS_HOST: 127.0.0.1
deploy:
resources:
limits:
@@ -464,11 +699,19 @@ services:
options:
max-size: "10m"
max-file: "3"
+ networks:
+ - prod-network
+
+networks:
+ prod-network:
+ driver: bridge
```
+---
+
## ☸️ Kubernetes Deployment
-### Deployment
+### Deployment Manifest
Create `k8s/deployment.yaml`:
@@ -479,6 +722,7 @@ metadata:
name: php-api-stack
labels:
app: php-api-stack
+ version: v1.5.0
spec:
replicas: 3
selector:
@@ -488,20 +732,29 @@ spec:
metadata:
labels:
app: php-api-stack
+ version: v1.5.0
spec:
containers:
- name: app
- image: kariricode/php-api-stack:stable
+ image: kariricode/php-api-stack:1.5.0
+ imagePullPolicy: IfNotPresent
ports:
- containerPort: 80
name: http
+ protocol: TCP
env:
- name: APP_ENV
value: "production"
- name: PHP_MEMORY_LIMIT
value: "512M"
+ - name: PHP_FPM_PM
+ value: "static"
- name: PHP_FPM_PM_MAX_CHILDREN
value: "100"
+ - name: PHP_OPCACHE_VALIDATE_TIMESTAMPS
+ value: "0"
+ - name: REDIS_HOST
+ value: "127.0.0.1" # Using internal Redis
resources:
requests:
memory: "512Mi"
@@ -513,29 +766,37 @@ spec:
httpGet:
path: /health
port: 80
+ scheme: HTTP
initialDelaySeconds: 30
periodSeconds: 10
timeoutSeconds: 5
+ successThreshold: 1
failureThreshold: 3
readinessProbe:
httpGet:
path: /health
port: 80
+ scheme: HTTP
initialDelaySeconds: 5
periodSeconds: 5
timeoutSeconds: 3
+ successThreshold: 1
failureThreshold: 3
volumeMounts:
- name: app-volume
mountPath: /var/www/html
readOnly: true
+ - name: logs-volume
+ mountPath: /var/log
volumes:
- name: app-volume
persistentVolumeClaim:
claimName: app-pvc
+ - name: logs-volume
+ emptyDir: {}
```
-### Service
+### Service Manifest
Create `k8s/service.yaml`:
@@ -544,17 +805,24 @@ apiVersion: v1
kind: Service
metadata:
name: php-api-stack
+ labels:
+ app: php-api-stack
spec:
- type: LoadBalancer
+ type: ClusterIP
selector:
app: php-api-stack
ports:
- - protocol: TCP
+ - name: http
+ protocol: TCP
port: 80
targetPort: 80
+ sessionAffinity: ClientIP
+ sessionAffinityConfig:
+ clientIP:
+ timeoutSeconds: 3600
```
-### Ingress
+### Ingress Manifest
Create `k8s/ingress.yaml`:
@@ -566,11 +834,13 @@ metadata:
annotations:
kubernetes.io/ingress.class: nginx
cert-manager.io/cluster-issuer: letsencrypt-prod
+ nginx.ingress.kubernetes.io/ssl-redirect: "true"
+ nginx.ingress.kubernetes.io/force-ssl-redirect: "true"
spec:
tls:
- hosts:
- api.example.com
- secretName: api-tls
+ secretName: api-tls-secret
rules:
- host: api.example.com
http:
@@ -584,24 +854,64 @@ spec:
number: 80
```
-### Deploy
+### HorizontalPodAutoscaler
+
+Create `k8s/hpa.yaml`:
+
+```yaml
+apiVersion: autoscaling/v2
+kind: HorizontalPodAutoscaler
+metadata:
+ name: php-api-stack-hpa
+spec:
+ scaleTargetRef:
+ apiVersion: apps/v1
+ kind: Deployment
+ name: php-api-stack
+ minReplicas: 3
+ maxReplicas: 10
+ metrics:
+ - type: Resource
+ resource:
+ name: cpu
+ target:
+ type: Utilization
+ averageUtilization: 70
+ - type: Resource
+ resource:
+ name: memory
+ target:
+ type: Utilization
+ averageUtilization: 80
+```
+
+### Deploy to Kubernetes
```bash
-# Apply configurations
+# Apply all configurations
kubectl apply -f k8s/
-# Check status
+# Check deployment status
+kubectl get deployments
kubectl get pods
kubectl get services
kubectl get ingress
-# Logs
+# View logs
kubectl logs -f deployment/php-api-stack
-# Scale
+# Scale manually
kubectl scale deployment php-api-stack --replicas=5
+
+# Check autoscaling
+kubectl get hpa
+
+# Port-forward for local testing
+kubectl port-forward deployment/php-api-stack 8080:80
```
+---
+
## 🏭 Production Setup
### Resource Limits
@@ -616,10 +926,12 @@ docker run -d \
--cpu-shares=1024 \
-p 80:80 \
-v $(pwd)/app:/var/www/html:ro \
- kariricode/php-api-stack:stable
+ -e APP_ENV=production \
+ -e PHP_OPCACHE_VALIDATE_TIMESTAMPS=0 \
+ kariricode/php-api-stack:1.5.0
```
-### Production Environment
+### Production Environment Variables
```bash
docker run -d \
@@ -632,22 +944,30 @@ docker run -d \
-e PHP_MEMORY_LIMIT=512M \
-e PHP_FPM_PM=static \
-e PHP_FPM_PM_MAX_CHILDREN=100 \
+ -e PHP_OPCACHE_ENABLE=1 \
-e PHP_OPCACHE_VALIDATE_TIMESTAMPS=0 \
-e PHP_OPCACHE_REVALIDATE_FREQ=0 \
- kariricode/php-api-stack:stable
+ -e PHP_OPCACHE_JIT=tracing \
+ -e PHP_OPCACHE_JIT_BUFFER_SIZE=256M \
+ --restart unless-stopped \
+ kariricode/php-api-stack:1.5.0
```
-### High Availability
+### High Availability with Docker Swarm
-```yaml
-# docker-compose.prod.yml
+```bash
+# Initialize swarm
+docker swarm init
+
+# Create stack file: docker-stack.yml
+cat > docker-stack.yml << 'EOF'
version: '3.8'
services:
app:
- image: kariricode/php-api-stack:stable
+ image: kariricode/php-api-stack:1.5.0
deploy:
- replicas: 3
+ replicas: 5
update_config:
parallelism: 1
delay: 10s
@@ -656,6 +976,7 @@ services:
condition: on-failure
delay: 5s
max_attempts: 3
+ window: 120s
resources:
limits:
cpus: '2'
@@ -666,97 +987,199 @@ services:
ports:
- "80:80"
volumes:
- - ./app:/var/www/html:ro
+ - app-data:/var/www/html:ro
environment:
- - APP_ENV=production
+ APP_ENV: production
+ PHP_FPM_PM: static
+ PHP_FPM_PM_MAX_CHILDREN: 100
+ PHP_OPCACHE_VALIDATE_TIMESTAMPS: 0
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost/health"]
interval: 30s
timeout: 3s
retries: 3
+
+volumes:
+ app-data:
+ driver: local
+EOF
+
+# Deploy stack
+docker stack deploy -c docker-stack.yml prod-stack
+
+# Check services
+docker service ls
+docker service ps prod-stack_app
+
+# Scale
+docker service scale prod-stack_app=10
+
+# Update image (zero-downtime)
+docker service update --image kariricode/php-api-stack:1.5.1 prod-stack_app
+
+# Remove stack
+docker stack rm prod-stack
```
+---
+
## 📊 Monitoring and Health
-### Health Checks
+### Health Check Endpoints
+
+#### Simple Health Check (Production)
-#### Simple Health Check
```bash
-# Basic health
+# Basic health status
curl http://localhost:8080/health
+
# Response: healthy
-# With details
+# With details (HTTP status)
curl -v http://localhost:8080/health
# HTTP/1.1 200 OK
# Content-Type: text/plain
+# Content-Length: 7
+#
# healthy
```
+**Use case**: Load balancers, Kubernetes liveness probes, Docker healthchecks
+
#### Comprehensive Health Check
+
```bash
-# Full health report (test builds only)
+# Full system diagnostics
curl http://localhost:8080/health.php | jq
-# Check specific component
-curl -s http://localhost:8080/health.php | jq '.checks.php'
-curl -s http://localhost:8080/health.php | jq '.checks.opcache'
-curl -s http://localhost:8080/health.php | jq '.checks.redis'
+# Response includes:
+# - Overall status (healthy/degraded/unhealthy)
+# - PHP runtime details
+# - OPcache statistics
+# - Redis connectivity
+# - System resources
+# - Application directories
```
-### PHP-FPM Status
+**Sample response**:
+
+```json
+{
+ "status": "healthy",
+ "timestamp": "2025-10-24T22:00:00+00:00",
+ "overall": "✓ All Systems Operational",
+ "components": {
+ "php": {
+ "status": "healthy",
+ "details": {
+ "version": "8.4.13",
+ "memory_limit": "512M",
+ "max_execution_time": "60"
+ }
+ },
+ "opcache": {
+ "status": "healthy",
+ "details": {
+ "enabled": true,
+ "hit_rate": 99.87,
+ "memory_used": "45.2 MB",
+ "jit_enabled": true
+ }
+ },
+ "redis": {
+ "status": "healthy",
+ "details": {
+ "connected": true,
+ "version": "7.2.11",
+ "latency_ms": 0.5
+ }
+ }
+ }
+}
+```
+
+### Checking Components
```bash
-# Status page (internal only)
-docker exec my-app curl http://localhost/fpm-status
+# PHP version and extensions
+docker exec my-app php -v
+docker exec my-app php -m
+
+# OPcache status
+docker exec my-app php -r "print_r(opcache_get_status());"
+
+# Redis connectivity
+docker exec my-app redis-cli -h 127.0.0.1 ping
+# PONG
-# Ping
-docker exec my-app curl http://localhost/fpm-ping
-# Response: pong
+# Redis info
+docker exec my-app redis-cli -h 127.0.0.1 info server
+
+# Nginx status
+docker exec my-app nginx -t
+docker exec my-app nginx -V
+
+# Process list
+docker exec my-app ps aux
```
-### Logs
+### Log Access
```bash
-# Application logs
+# Real-time logs
docker logs -f my-app
-# Nginx access logs
+# Nginx access log
docker exec my-app tail -f /var/log/nginx/access.log
-# Nginx error logs
+# Nginx error log
docker exec my-app tail -f /var/log/nginx/error.log
-# PHP error logs
+# PHP error log
docker exec my-app tail -f /var/log/php/error.log
-# PHP-FPM logs
+# PHP-FPM access log
docker exec my-app tail -f /var/log/php/fpm-access.log
+
+# PHP-FPM slow log
docker exec my-app tail -f /var/log/php/fpm-slow.log
-# All logs
+# Redis log
+docker exec my-app tail -f /var/log/redis/redis.log
+
+# All logs simultaneously
docker exec my-app tail -f /var/log/**/*.log
```
-### Metrics
+### Metrics and Statistics
```bash
-# Container stats
+# Container resource usage
docker stats my-app
-# OPcache stats
-docker exec my-app php -r "print_r(opcache_get_status());"
+# Container inspect (detailed info)
+docker inspect my-app
-# Process list
-docker exec my-app ps aux
+# OPcache statistics
+docker exec my-app php -r "
+\$status = opcache_get_status();
+echo 'Memory Used: ' . \$status['memory_usage']['used_memory'] / 1024 / 1024 . ' MB' . PHP_EOL;
+echo 'Hit Rate: ' . \$status['opcache_statistics']['opcache_hit_rate'] . '%' . PHP_EOL;
+echo 'Cached Scripts: ' . \$status['opcache_statistics']['num_cached_scripts'] . PHP_EOL;
+"
-# Service status
-docker exec my-app supervisorctl status
+# Redis statistics
+docker exec my-app redis-cli -h 127.0.0.1 info stats
+docker exec my-app redis-cli -h 127.0.0.1 info memory
```
+---
+
## ⚡ Performance Tuning
-### For High Traffic
+### High Traffic Configuration
+
+For applications with heavy load (100+ req/s):
```bash
docker run -d \
@@ -768,134 +1191,327 @@ docker run -d \
-e PHP_MEMORY_LIMIT=512M \
-e PHP_FPM_PM=static \
-e PHP_FPM_PM_MAX_CHILDREN=200 \
+ -e PHP_OPCACHE_ENABLE=1 \
-e PHP_OPCACHE_MEMORY=512 \
- -e PHP_OPCACHE_MAX_FILES=50000 \
+ -e PHP_OPCACHE_MAX_ACCELERATED_FILES=50000 \
+ -e PHP_OPCACHE_VALIDATE_TIMESTAMPS=0 \
+ -e PHP_OPCACHE_JIT=tracing \
+ -e PHP_OPCACHE_JIT_BUFFER_SIZE=256M \
+ -e NGINX_WORKER_PROCESSES=auto \
-e NGINX_WORKER_CONNECTIONS=4096 \
kariricode/php-api-stack:latest
```
-### For Low Latency
+### Low Latency Configuration
+
+For API with strict latency requirements (<50ms):
```bash
docker run -d \
--name low-latency-app \
+ --memory="1g" \
+ --cpus="2" \
-p 80:80 \
-v $(pwd)/app:/var/www/html:ro \
+ -e PHP_MEMORY_LIMIT=256M \
+ -e PHP_FPM_PM=ondemand \
+ -e PHP_FPM_PM_MAX_CHILDREN=50 \
+ -e PHP_FPM_PM_PROCESS_IDLE_TIMEOUT=10s \
-e PHP_OPCACHE_VALIDATE_TIMESTAMPS=0 \
-e PHP_OPCACHE_JIT=tracing \
-e PHP_OPCACHE_JIT_BUFFER_SIZE=256M \
- -e PHP_FPM_PM=ondemand \
- -e PHP_FPM_PM_MAX_CHILDREN=50 \
+ -e NGINX_KEEPALIVE_TIMEOUT=30 \
kariricode/php-api-stack:latest
```
-### For Memory Efficiency
+### Memory-Constrained Environment
+
+For environments with limited memory (<512MB):
```bash
docker run -d \
--name memory-efficient-app \
--memory="512m" \
+ --memory-swap="512m" \
-p 80:80 \
-v $(pwd)/app:/var/www/html:ro \
- -e PHP_MEMORY_LIMIT=256M \
+ -e PHP_MEMORY_LIMIT=128M \
-e PHP_FPM_PM=dynamic \
-e PHP_FPM_PM_MAX_CHILDREN=25 \
- -e PHP_OPCACHE_MEMORY=128 \
+ -e PHP_FPM_PM_START_SERVERS=5 \
+ -e PHP_FPM_PM_MIN_SPARE_SERVERS=2 \
+ -e PHP_FPM_PM_MAX_SPARE_SERVERS=10 \
+ -e PHP_OPCACHE_MEMORY=64 \
+ -e PHP_OPCACHE_JIT_BUFFER_SIZE=32M \
kariricode/php-api-stack:latest
```
+### Performance Monitoring
+
+```bash
+# Real-time resource monitoring
+docker stats my-app --no-stream
+
+# OPcache hit rate (should be >95%)
+docker exec my-app php -r "
+\$stats = opcache_get_status()['opcache_statistics'];
+echo 'Hit Rate: ' . round(\$stats['opcache_hit_rate'], 2) . '%' . PHP_EOL;
+echo 'Misses: ' . \$stats['misses'] . PHP_EOL;
+echo 'Hits: ' . \$stats['hits'] . PHP_EOL;
+"
+
+# PHP-FPM pool status (if status page is enabled)
+docker exec my-app curl -s http://localhost/fpm-status | jq
+
+# Request timing (add to Nginx config for detailed timing)
+docker exec my-app tail -f /var/log/nginx/access.log | \
+ awk '{print $NF}' | \
+ awk 'BEGIN{sum=0;count=0} {sum+=$1;count++} END{print "Avg:", sum/count, "Count:", count}'
+```
+
+---
+
## 🐛 Troubleshooting
### Container Won't Start
+**Symptoms**: Container exits immediately after starting
+
```bash
# Check logs
docker logs my-app
-# Common issues:
-# - Port already in use → Change port: -p 8081:80
-# - Volume permissions → Fix with: chmod -R 755 app/
-# - Memory limits → Increase: --memory="2g"
+# Common issues and solutions:
+
+# 1. Port already in use
+# Error: "bind: address already in use"
+# Solution: Use different port
+docker run -d -p 8081:80 ... kariricode/php-api-stack:latest
+
+# 2. Volume mount permission issues
+# Error: "Permission denied"
+# Solution: Fix permissions
+chmod -R 755 app/
+chown -R $(id -u):$(id -g) app/
+
+# 3. Invalid environment variables
+# Error: "Invalid PHP_MEMORY_LIMIT"
+# Solution: Check format (e.g., "512M" not "512")
+
+# 4. Memory limit too low
+# Solution: Increase Docker memory limit
+docker run -d --memory="1g" ... kariricode/php-api-stack:latest
```
### 502 Bad Gateway
+**Symptoms**: Nginx returns 502 error
+
```bash
-# Check PHP-FPM
-docker exec my-app supervisorctl status php-fpm
+# Check if PHP-FPM is running
+docker exec my-app ps aux | grep php-fpm
+
+# Check PHP-FPM socket
docker exec my-app ls -la /var/run/php/php-fpm.sock
+# Should show: srw-rw---- 1 www-data www-data
+
+# Test PHP-FPM configuration
+docker exec my-app php-fpm -t
+
+# Check PHP-FPM logs
+docker exec my-app tail -50 /var/log/php/fpm-error.log
+
+# Check Nginx error log
+docker exec my-app tail -50 /var/log/nginx/error.log
# Restart PHP-FPM
-docker exec my-app supervisorctl restart php-fpm
+docker exec my-app kill -USR2 $(cat /var/run/php/php-fpm.pid)
-# Check logs
-docker exec my-app tail -f /var/log/php/fpm-error.log
+# If problem persists, restart container
+docker restart my-app
+```
+
+### Redis Connection Issues
+
+**Symptoms**: Application can't connect to Redis
+
+#### Standalone Container Mode
+
+```bash
+# Check if Redis is running
+docker exec my-app ps aux | grep redis
+# Should show: redis-server 0.0.0.0:6379
+
+# Verify REDIS_HOST environment variable
+docker exec my-app env | grep REDIS_HOST
+# Should be: REDIS_HOST=127.0.0.1
+
+# Test Redis connection
+docker exec my-app redis-cli -h 127.0.0.1 ping
+# Should return: PONG
+
+# Test with password (if configured)
+docker exec my-app redis-cli -h 127.0.0.1 -a "your-password" ping
+
+# Check Redis logs
+docker exec my-app tail -f /var/log/redis/redis.log
+```
+
+#### Docker Compose Mode
+
+```bash
+# Check if redis service is running
+docker-compose ps redis
+
+# Verify REDIS_HOST points to service name
+docker-compose exec app env | grep REDIS_HOST
+# Should be: REDIS_HOST=redis (service name)
+
+# Test connection from app container
+docker-compose exec app redis-cli -h redis ping
+# Should return: PONG
+
+# Check network connectivity
+docker-compose exec app ping -c 3 redis
```
### Slow Performance
+**Symptoms**: Requests take longer than expected
+
```bash
-# Check OPcache
+# 1. Check OPcache hit rate
docker exec my-app php -r "
\$status = opcache_get_status();
-echo 'Hit Rate: ' . \$status['opcache_statistics']['opcache_hit_rate'] . '%' . PHP_EOL;
+\$hit_rate = \$status['opcache_statistics']['opcache_hit_rate'];
+echo 'OPcache Hit Rate: ' . round(\$hit_rate, 2) . '%' . PHP_EOL;
+if (\$hit_rate < 90) {
+ echo 'WARNING: Hit rate is low. Increase PHP_OPCACHE_MEMORY' . PHP_EOL;
+}
"
-# If hit rate < 90%, increase memory:
-# -e PHP_OPCACHE_MEMORY=512
+# 2. Check resource usage
+docker stats my-app --no-stream
-# Check resource usage
-docker stats my-app
+# 3. Check PHP-FPM pool
+docker exec my-app cat /var/run/php/php-fpm.pid
+docker exec my-app kill -USR1 $(cat /var/run/php/php-fpm.pid) # Get pool status
-# Adjust PHP-FPM pool
-# -e PHP_FPM_PM_MAX_CHILDREN=100
+# 4. Enable slow log analysis
+docker exec my-app tail -f /var/log/php/fpm-slow.log
+
+# Solutions:
+# - Increase OPcache memory: -e PHP_OPCACHE_MEMORY=512
+# - Increase FPM children: -e PHP_FPM_PM_MAX_CHILDREN=100
+# - Add more CPU/memory to container
+# - Enable JIT: -e PHP_OPCACHE_JIT=tracing
```
-### Memory Issues
+### High Memory Usage
+
+**Symptoms**: Container uses excessive memory
```bash
# Check memory usage
-docker exec my-app free -h
-docker exec my-app php -r "echo memory_get_usage(true);"
+docker stats my-app --no-stream
-# Increase PHP memory limit
-docker stop my-app
-docker rm my-app
-docker run -d \
- --name my-app \
- -e PHP_MEMORY_LIMIT=1G \
- ...
+# Check PHP memory limit
+docker exec my-app php -r "echo ini_get('memory_limit') . PHP_EOL;"
+
+# Check OPcache memory
+docker exec my-app php -r "
+\$status = opcache_get_status();
+\$memory = \$status['memory_usage'];
+echo 'Used: ' . round(\$memory['used_memory'] / 1024 / 1024, 2) . ' MB' . PHP_EOL;
+echo 'Free: ' . round(\$memory['free_memory'] / 1024 / 1024, 2) . ' MB' . PHP_EOL;
+echo 'Wasted: ' . round(\$memory['wasted_memory'] / 1024 / 1024, 2) . ' MB' . PHP_EOL;
+"
+
+# Solutions:
+# - Reduce PHP memory limit: -e PHP_MEMORY_LIMIT=256M
+# - Reduce FPM children: -e PHP_FPM_PM_MAX_CHILDREN=30
+# - Use dynamic PM mode: -e PHP_FPM_PM=dynamic
+# - Set container memory limit: --memory="512m"
```
-### Redis Connection Failed
+### Permission Issues
+
+**Symptoms**: Application can't write to directories
```bash
-# Check Redis
-docker exec my-app redis-cli ping
-# Should return: PONG
+# Check current ownership
+docker exec my-app ls -la /var/www/html
-# Restart Redis
-docker exec my-app supervisorctl restart redis
+# Fix ownership (application directories)
+docker exec my-app chown -R www-data:www-data /var/www/html/var
+docker exec my-app chown -R www-data:www-data /var/www/html/storage
-# Check logs
-docker exec my-app tail -f /var/log/redis/redis.log
+# Fix permissions
+docker exec my-app chmod -R 775 /var/www/html/var
+docker exec my-app chmod -R 775 /var/www/html/storage
+
+# From host (if volume mounted)
+sudo chown -R $(id -u):$(id -g) app/var
+sudo chmod -R 775 app/var
+```
+
+### Nginx Configuration Issues
+
+```bash
+# Test Nginx configuration
+docker exec my-app nginx -t
+
+# Reload Nginx (after config changes)
+docker exec my-app nginx -s reload
+
+# Check Nginx error log
+docker exec my-app tail -100 /var/log/nginx/error.log
+
+# View current configuration
+docker exec my-app cat /etc/nginx/nginx.conf
+docker exec my-app cat /etc/nginx/conf.d/default.conf
+```
+
+### Debug Mode
+
+Enable debug mode for troubleshooting:
+
+```bash
+docker run -d \
+ --name debug-app \
+ -p 8080:80 \
+ -v $(pwd)/app:/var/www/html \
+ -e APP_ENV=development \
+ -e APP_DEBUG=true \
+ -e PHP_DISPLAY_ERRORS=On \
+ -e PHP_ERROR_REPORTING=E_ALL \
+ -e PHP_OPCACHE_VALIDATE_TIMESTAMPS=1 \
+ kariricode/php-api-stack:dev # Use dev image for Xdebug
```
+---
+
## 📚 Examples
-### Example 1: Simple API
+### Example 1: Simple REST API
```bash
-# Create simple API
+# Create simple API structure
mkdir -p my-api/public
cat > my-api/public/index.php << 'EOF'
'ok',
+header('Access-Control-Allow-Origin: *');
+
+$response = [
+ 'status' => 'success',
'message' => 'API is running',
- 'php_version' => PHP_VERSION
-]);
+ 'timestamp' => time(),
+ 'php_version' => PHP_VERSION,
+ 'server' => $_SERVER['SERVER_SOFTWARE'] ?? 'Unknown'
+];
+
+echo json_encode($response, JSON_PRETTY_PRINT);
EOF
# Run
@@ -906,19 +1522,23 @@ docker run -d \
kariricode/php-api-stack:latest
# Test
-curl http://localhost:8080
+curl http://localhost:8080 | jq
```
### Example 2: With Environment File
```bash
# Create .env.production
-cat > .env.production << EOF
+cat > .env.production << 'EOF'
APP_ENV=production
APP_DEBUG=false
PHP_MEMORY_LIMIT=512M
+PHP_FPM_PM=static
PHP_FPM_PM_MAX_CHILDREN=100
+PHP_OPCACHE_VALIDATE_TIMESTAMPS=0
DATABASE_URL=mysql://user:pass@db:3306/mydb
+REDIS_HOST=127.0.0.1
+REDIS_PASSWORD=secure-password
EOF
# Run with env file
@@ -930,58 +1550,135 @@ docker run -d \
kariricode/php-api-stack:latest
```
-### Example 3: With Custom PHP Configuration
+### Example 3: Multi-Stage Development
```bash
-# Create custom php.ini
-cat > custom-php.ini << EOF
-memory_limit = 1G
-max_execution_time = 300
-upload_max_filesize = 200M
-post_max_size = 200M
-EOF
+# Development
+docker run -d \
+ --name dev-app \
+ -p 8001:80 \
+ -v $(pwd)/app:/var/www/html \
+ -e APP_ENV=development \
+ -e APP_DEBUG=true \
+ -e PHP_DISPLAY_ERRORS=On \
+ -e PHP_OPCACHE_VALIDATE_TIMESTAMPS=1 \
+ kariricode/php-api-stack:dev
-# Run with custom config
+# Staging
docker run -d \
- --name custom-app \
+ --name staging-app \
+ -p 8002:80 \
+ -v $(pwd)/app:/var/www/html:ro \
+ -e APP_ENV=staging \
+ -e APP_DEBUG=false \
+ -e PHP_OPCACHE_VALIDATE_TIMESTAMPS=1 \
+ kariricode/php-api-stack:latest
+
+# Production
+docker run -d \
+ --name prod-app \
+ -p 80:80 \
+ -v $(pwd)/app:/var/www/html:ro \
+ -e APP_ENV=production \
+ -e APP_DEBUG=false \
+ -e PHP_OPCACHE_VALIDATE_TIMESTAMPS=0 \
+ --restart unless-stopped \
+ kariricode/php-api-stack:1.5.0
+```
+
+### Example 4: Health Check Integration
+
+```bash
+# With Docker healthcheck
+docker run -d \
+ --name monitored-app \
-p 8080:80 \
- -v $(pwd)/custom-php.ini:/usr/local/etc/php/conf.d/99-custom.ini:ro \
-v $(pwd)/app:/var/www/html \
+ --health-cmd="curl -f http://localhost/health || exit 1" \
+ --health-interval=30s \
+ --health-timeout=3s \
+ --health-retries=3 \
+ --health-start-period=5s \
kariricode/php-api-stack:latest
+
+# Check health status
+docker inspect --format='{{.State.Health.Status}}' monitored-app
```
+---
+
## 🎓 Best Practices
### Development
-- ✅ Use `latest` tag for easier updates
-- ✅ Mount code as read-write (`-v $(pwd)/app:/var/www/html`)
-- ✅ Enable debug mode (`APP_DEBUG=true`)
-- ✅ Use hot reload tools
+
+- ✅ Use `dev` tag for full debugging capabilities
+- ✅ Mount code read-write: `-v $(pwd)/app:/var/www/html`
+- ✅ Enable debug mode: `-e APP_DEBUG=true`
+- ✅ Enable OPcache timestamp validation: `-e PHP_OPCACHE_VALIDATE_TIMESTAMPS=1`
+- ✅ Use local network for multi-container setup
+- ✅ Keep dependencies up to date with Composer
### Staging
-- ✅ Use specific version tags (e.g., `1.2`)
-- ✅ Mirror production config
-- ✅ Test with production data volume
-- ✅ Run load tests
+
+- ✅ Use specific version tags (e.g., `1.5`)
+- ✅ Mirror production configuration
+- ✅ Mount code read-only: `-v $(pwd)/app:/var/www/html:ro`
+- ✅ Test with production-like data volumes
+- ✅ Run load and performance tests
+- ✅ Validate health checks work correctly
### Production
-- ✅ Use `stable` or specific version tags
-- ✅ Mount code as read-only (`:ro`)
-- ✅ Set resource limits (`--memory`, `--cpus`)
-- ✅ Use health checks
-- ✅ Configure log rotation
-- ✅ Set up monitoring
-## 📞 Support
+- ✅ **Always** pin specific versions: `kariricode/php-api-stack:1.5.0`
+- ✅ **Always** mount code read-only: `:ro`
+- ✅ Set resource limits: `--memory`, `--cpus`
+- ✅ Disable OPcache validation: `-e PHP_OPCACHE_VALIDATE_TIMESTAMPS=0`
+- ✅ Use static PM mode for predictable load: `-e PHP_FPM_PM=static`
+- ✅ Enable health checks
+- ✅ Configure proper logging and monitoring
+- ✅ Use `--restart unless-stopped` or `always`
+- ✅ Store logs externally: `-v $(pwd)/logs:/var/log`
+- ✅ Never use `latest` tag in production
+- ✅ Plan for zero-downtime deployments
+
+### Security
+
+- ✅ Never run as root (container uses `www-data` by default)
+- ✅ Use read-only filesystem where possible
+- ✅ Keep secrets in environment variables, not in code
+- ✅ Regularly update to latest patch versions
+- ✅ Use HTTPS/TLS in production (via reverse proxy)
+- ✅ Implement rate limiting at the proxy level
+- ✅ Monitor container for vulnerabilities
+
+---
+
+## 📞 Support & Resources
+
+### Documentation
+
+- **Main README**: [GitHub README](https://github.com/kariricode/php-api-stack)
+- **Docker Compose Guide**: [DOCKER_COMPOSE_GUIDE.md](DOCKER_COMPOSE_GUIDE.md)
+- **Testing Guide**: [TESTING.md](TESTING.md) - For maintainers
+- **Docker Hub Guide**: [DOCKER_HUB.md](DOCKER_HUB.md) - For publishers
+
+### Community
-- **Documentation**: [GitHub README](https://github.com/kariricode/php-api-stack)
- **Issues**: [Report bugs](https://github.com/kariricode/php-api-stack/issues)
-- **Docker Hub**: [Image repository](https://hub.docker.com/r/kariricode/php-api-stack)
- **Discussions**: [Ask questions](https://github.com/kariricode/php-api-stack/discussions)
+- **Docker Hub**: [Image repository](https://hub.docker.com/r/kariricode/php-api-stack)
+
+### Quick Links
+
+- [PHP 8.4 Documentation](https://www.php.net/docs.php)
+- [Nginx Documentation](https://nginx.org/en/docs/)
+- [Redis Documentation](https://redis.io/documentation)
+- [Docker Best Practices](https://docs.docker.com/develop/dev-best-practices/)
+- [Symfony Deployment](https://symfony.com/doc/current/deployment.html)
+- [Laravel Deployment](https://laravel.com/docs/deployment)
---
-**More Info**:
-- [Testing Guide](TESTING.md) - For maintainers
-- [Docker Hub Guide](DOCKER_HUB.md) - For publishers
-- [README](README.md) - Project overview
\ No newline at end of file
+**Version**: 1.5.0
+**Last Updated**: 2025-10-24
+**Made with 💚 by [KaririCode](https://kariricode.org)**
\ No newline at end of file
diff --git a/Makefile b/Makefile
index 91aae03..f0df04e 100644
--- a/Makefile
+++ b/Makefile
@@ -54,277 +54,249 @@ ALPINE_VERSION?=3.21
COMPOSER_VERSION?=2.8.12
SYMFONY_CLI_VERSION?=5.15.1
+DEMO_MODE ?= false
+HEALTH_CHECK_INSTALL ?= false
+
# Common build args block
BUILD_ARGS := \
- --build-arg PHP_VERSION=$(PHP_VERSION) \
- --build-arg NGINX_VERSION=$(NGINX_VERSION) \
- --build-arg REDIS_VERSION=$(REDIS_VERSION) \
- --build-arg ALPINE_VERSION=$(ALPINE_VERSION) \
- --build-arg COMPOSER_VERSION=$(COMPOSER_VERSION) \
- --build-arg SYMFONY_CLI_VERSION=$(SYMFONY_CLI_VERSION) \
- --build-arg VERSION=$(VERSION) \
- --build-arg BUILD_DATE="$$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
- --build-arg VCS_REF=$$(git rev-parse --short=12 HEAD 2>/dev/null || echo unknown)
-
-# toggles development-specific build args
+ --build-arg DEMO_MODE=$(DEMO_MODE) \
+ --build-arg HEALTH_CHECK_INSTALL=$(HEALTH_CHECK_INSTALL) \
+ --build-arg VERSION=$(VERSION) \
+ --build-arg PHP_VERSION=$(PHP_VERSION) \
+ --build-arg PHP_CORE_EXTENSIONS=$(PHP_CORE_EXTENSIONS) \
+ --build-arg PHP_PECL_EXTENSIONS=$(PHP_PECL_EXTENSIONS) \
+ --build-arg NGINX_VERSION=$(NGINX_VERSION) \
+ --build-arg REDIS_VERSION=$(REDIS_VERSION) \
+ --build-arg ALPINE_VERSION=$(ALPINE_VERSION) \
+ --build-arg COMPOSER_VERSION=$(COMPOSER_VERSION) \
+ --build-arg PHP_REDIS_VERSION=$(PHP_REDIS_VERSION) \
+ --build-arg PHP_APCU_VERSION=$(PHP_APCU_VERSION) \
+ --build-arg PHP_UUID_VERSION=$(PHP_UUID_VERSION) \
+ --build-arg PHP_IMAGICK_VERSION=$(PHP_IMAGICK_VERSION) \
+ --build-arg PHP_AMQP_VERSION=$(PHP_AMQP_VERSION) \
+ --build-arg BUILD_DATE="$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
+ --build-arg VCS_REF=$(git rev-parse --short=12 HEAD 2>/dev/null || echo unknown)
+
+# Production build args
+PROD_BUILD_ARGS := \
+ --build-arg APP_ENV=production \
+ --build-arg PHP_OPCACHE_VALIDATE_TIMESTAMPS=0 \
+ --build-arg PHP_OPCACHE_MAX_ACCELERATED_FILES=20000 \
+ --build-arg PHP_OPCACHE_ENABLE=1 \
+ --build-arg PHP_OPCACHE_MEMORY_CONSUMPTION=256 \
+ --build-arg XDEBUG_ENABLE=0 \
+ --build-arg APP_DEBUG=false \
+ --build-arg DEMO_MODE=false \
+ --build-arg HEALTH_CHECK_INSTALL=false
+
+
+# Development build args
IMAGE_TAG ?= dev
-XDEBUG_ENABLE ?= 0
-APP_ENV ?= development
-APP_DEBUG ?= true
-DEMO_MODE ?= true
-HEALTH_CHECK_INSTALL ?= true
+XDEBUG_ENABLE ?= 1
+XDEBUG_VERSION ?= 3.4.6
DEV_BUILD_ARGS := \
---build-arg APP_ENV=$(APP_ENV) \
---build-arg APP_DEBUG=$(APP_DEBUG) \
---build-arg DEMO_MODE=$(DEMO_MODE) \
---build-arg HEALTH_CHECK_INSTALL=$(HEALTH_CHECK_INSTALL) \
---build-arg ENABLE_XDEBUG=$(XDEBUG_ENABLE)
+ --build-arg APP_ENV=development \
+ --build-arg APP_DEBUG=true \
+ --build-arg DEMO_MODE=true \
+ --build-arg SYMFONY_CLI_VERSION=$(SYMFONY_CLI_VERSION) \
+ --build-arg XDEBUG_VERSION=$(XDEBUG_VERSION) \
+ --build-arg HEALTH_CHECK_INSTALL=true \
+ --build-arg XDEBUG_ENABLE=$(XDEBUG_ENABLE)
-# =============================================================
-# HELP
-# =============================================================
.PHONY: help
help: ## Show this help message
- @echo "$(GREEN)╔══════════════════════════════════════════════════════╗$(NC)"
- @echo "$(GREEN)║ PHP API Stack - Docker Build System ║$(NC)"
- @echo "$(GREEN)╚══════════════════════════════════════════════════════╝$(NC)"
+ @echo "$(GREEN)╔═══════════════════════════════════════════════════╗$(NC)"
+ @echo "$(GREEN)║ PHP API Stack - Docker Build System ║$(NC)"
+ @echo "$(GREEN)╚═══════════════════════════════════════════════════╝$(NC)"
@echo "$(CYAN)Version: $(VERSION)$(NC)"
+ @echo "$(CYAN)Architecture: Base → Production | Dev$(NC)"
@echo ""
- @echo "$(MAGENTA)═══ BUILD TARGETS ═══$(NC)"
- @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | grep -v "^#" | awk 'BEGIN {FS = ":.*?## "}; /^build/ || /^scan/ || /^lint/ {printf " $(YELLOW)%-22s$(NC) %s\n", $$1, $$2}'
+ @echo "$(MAGENTA)━━━ BUILD TARGETS ━━━$(NC)"
+ @grep -hE '^build.*:.*##' $(MAKEFILE_LIST) | awk -F':.*## ' '{printf " $(YELLOW)%-22s$(NC) %s\n", $$1, $$2}'
@echo ""
- @echo "$(MAGENTA)═══ TEST TARGETS ═══$(NC)"
- @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | grep -v "^#" | awk 'BEGIN {FS = ":.*?## "}; /^test/ || /run-test/ || /stop-test/ || /logs-test/ || /shell-test/ {printf " $(YELLOW)%-22s$(NC) %s\n", $$1, $$2}'
+ @echo "$(MAGENTA)━━━ TEST TARGETS ━━━$(NC)"
+ @grep -hE '^test.*:.*##' $(MAKEFILE_LIST) | awk -F':.*## ' '{printf " $(YELLOW)%-22s$(NC) %s\n", $$1, $$2}'
@echo ""
- @echo "$(MAGENTA)═══ RUNTIME TARGETS ═══$(NC)"
- @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | grep -v "^#" | awk 'BEGIN {FS = ":.*?## "}; /^run/ || /^stop$$/ || /^restart/ || /^logs$$/ || /^exec/ || /^shell$$/ {printf " $(YELLOW)%-22s$(NC) %s\n", $$1, $$2}'
+ @echo "$(MAGENTA)━━━ RUNTIME TARGETS ━━━$(NC)"
+ @grep -hE '^(run|stop|restart|logs|shell).*:.*##' $(MAKEFILE_LIST) | awk -F':.*## ' '{printf " $(YELLOW)%-22s$(NC) %s\n", $$1, $$2}'
@echo ""
- @echo "$(MAGENTA)═══ DOCKER COMPOSE TARGETS (from Makefile.compose) ═══$(NC)"
- @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | grep -v "^#" | awk 'BEGIN {FS = ":.*?## "}; /^compose-/ {printf " $(YELLOW)%-22s$(NC) %s\n", $$1, $$2}'
+ @echo "$(MAGENTA)━━━ DOCKER HUB TARGETS ━━━$(NC)"
+ @grep -hE '^(push|release|publish|tag|hub|docker-).*:.*##' $(MAKEFILE_LIST) | awk -F':.*## ' '{printf " $(YELLOW)%-22s$(NC) %s\n", $$1, $$2}'
@echo ""
- @echo "$(MAGENTA)═══ UTILITY TARGETS ═══$(NC)"
- @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | grep -v "^#" | awk 'BEGIN {FS = ":.*?## "}; /version/ || /bump/ || /push/ || /release/ || /clean/ || /info/ || /stats/ {printf " $(YELLOW)%-22s$(NC) %s\n", $$1, $$2}'
+ @echo "$(MAGENTA)━━━ UTILITY TARGETS ━━━$(NC)"
+ @grep -hE '^(version|bump|clean|info|lint|scan).*:.*##' $(MAKEFILE_LIST) | awk -F':.*## ' '{printf " $(YELLOW)%-22s$(NC) %s\n", $$1, $$2}'
@echo ""
@echo "$(BLUE)Quick Start Examples:$(NC)"
- @echo " $(CYAN)make build$(NC) # Build production image"
- @echo " $(CYAN)make build-dev$(NC) # Build dev image (Symfony CLI/Xdebug opt.)"
- @echo " $(CYAN)make push$(NC) # Push production image to Docker Hub"
- @echo " $(CYAN)make push-dev$(NC) # Push dev image to Docker Hub"
- @echo " $(CYAN)make publish-dev$(NC) # Publish dev image to Docker Hub"
- @echo " $(CYAN)make run-dev$(NC) # Run dev container"
- @echo " $(CYAN)make build-test-image$(NC) # Build test image"
- @echo " $(CYAN)make test-quick$(NC) # Quick test of built image"
- @echo " $(CYAN)make test$(NC) # Run comprehensive tests"
- @echo " $(CYAN)make run$(NC) # Run local container on port $(LOCAL_PORT)"
- @echo " $(CYAN)make run-test$(NC) # Run test container on port $(TEST_PORT) with comprehensive health"
- @echo " $(CYAN)make release$(NC) # Full release pipeline"
- @echo " $(CYAN)make compose-help$(NC) # Show help for Docker Compose commands"
- @echo " $(CYAN)make compose-up-all$(NC) # Start all Docker Compose services"
- @echo " $(CYAN)make compose-down-v$(NC) # Stop all Docker Compose services"
+ @echo " $(CYAN)make build$(NC) # Build production image"
+ @echo " $(CYAN)make build-dev$(NC) # Build dev image with Xdebug"
+ @echo " $(CYAN)make test$(NC) # Run comprehensive tests"
+ @echo " $(CYAN)make release-production$(NC) # Full production release pipeline"
+ @echo " $(CYAN)make publish-dev$(NC) # Build + push dev image"
+ @echo " $(CYAN)make run$(NC) # Run production container"
+ @echo " $(CYAN)make run-dev$(NC) # Run dev container"
+ @echo ""
+ @echo "$(YELLOW)For Docker Hub commands:$(NC) $(CYAN)make hub-help$(NC)"
+ @echo "$(YELLOW)For Docker Hub commands:$(NC) $(CYAN)make compose-help$(NC)"
# =============================================================
# BUILD TARGETS
# =============================================================
.PHONY: build
-build: ## Build the Docker image locally (production)
+build: ## Build production image (target=production)
@echo "$(GREEN)Building production Docker image...$(NC)"
@if [ $(HAVE_BUILD_SCRIPT) -eq 1 ]; then \
$(BUILD_SCRIPT) --version=$(VERSION); \
else \
- docker build $(BUILD_ARGS) -t $(FULL_IMAGE):$(VERSION) . && \
- docker tag $(FULL_IMAGE):$(VERSION) $(FULL_IMAGE):latest && \
- echo "Tagged: $(FULL_IMAGE):latest"; \
+ docker build --no-cache \
+ $(BUILD_ARGS) \
+ $(PROD_BUILD_ARGS) \
+ --target production \
+ -t $(FULL_IMAGE):$(VERSION) \
+ -t $(FULL_IMAGE):latest \
+ -t $(FULL_IMAGE):$(MAJOR_VERSION) \
+ -t $(FULL_IMAGE):$(MINOR_VERSION) \
+ $(BUILD_CONTEXT); \
fi
- @echo "$(GREEN)✓ Production build complete!$(NC)"
-
-.PHONY: build-no-cache
-build-no-cache: ## Build without using cache (production)
- @echo "$(GREEN)Building Docker image (no cache)...$(NC)"
- @if [ $(HAVE_BUILD_SCRIPT) -eq 1 ]; then \
- $(BUILD_SCRIPT) --no-cache --version=$(VERSION); \
- else \
- docker build --no-cache $(BUILD_ARGS) -t $(FULL_IMAGE):$(VERSION) . && \
- docker tag $(FULL_IMAGE):$(VERSION) $(FULL_IMAGE):latest; \
- fi
- @echo "$(GREEN)✓ Build complete!$(NC)"
+ @echo "$(GREEN)OK: Production build complete!$(NC)"
.PHONY: build-dev
-build-dev: ## Build development image (target via BUILD_TARGET, tag via IMAGE_TAG)
+build-dev: ## Build development image (target=dev)
@echo "$(GREEN)Building development Docker image...$(NC)"
- @docker build --no-cache $(BUILD_ARGS) $(DEV_BUILD_ARGS) \
- -t $(FULL_IMAGE):$(IMAGE_TAG) .
- @echo "$(GREEN)✓ Dev image built as $(FULL_IMAGE):$(IMAGE_TAG)$(NC)"
-
-.PHONY: build-test-image
-build-test-image: ## Build test image (production base) with comprehensive health check
+ @docker build \
+ --no-cache \
+ $(BUILD_ARGS) \
+ $(DEV_BUILD_ARGS) \
+ --target dev \
+ --tag $(FULL_IMAGE):$(IMAGE_TAG) \
+ --tag $(FULL_IMAGE):dev \
+ .
+ @echo "$(GREEN)OK: Dev image built as $(FULL_IMAGE):$(IMAGE_TAG)$(NC)"
+
+.PHONY: build-base
+build-base: ## Build base image (target=base) - for debugging only
+ @echo "$(GREEN)Building base Docker image...$(NC)"
+ @docker build \
+ --no-cache \
+ $(BUILD_ARGS) \
+ --target base \
+ --tag $(FULL_IMAGE):base \
+ .
+ @echo "$(GREEN)OK: Base image built as $(FULL_IMAGE):base$(NC)"
+
+.PHONY: build-test
+build-test: ## Build test image (production with health check)
@echo "$(GREEN)Building test image with comprehensive health check...$(NC)"
- @docker build $(BUILD_ARGS) --build-arg HEALTH_CHECK_TYPE=comprehensive \
- -t $(FULL_IMAGE):test .
- @echo "$(GREEN)✓ Test image built: $(FULL_IMAGE):test$(NC)"
+ @docker build \
+ $(BUILD_ARGS) \
+ $(PROD_BUILD_ARGS) \
+ --build-arg HEALTH_CHECK_INSTALL=true \
+ --target production \
+ -t $(FULL_IMAGE):test \
+ .
+ @echo "$(GREEN)OK: Test image built: $(FULL_IMAGE):test$(NC)"
+
+.PHONY: build-all
+build-all: build build-dev ## Build both production and dev images
+ @echo "$(GREEN)OK: All images built successfully!$(NC)"
# =============================================================
-# PUSH & PUBLISH TARGETS
+# PUSH & PUBLISH TARGETS (moved to Makefile.dockerhub)
# =============================================================
-.PHONY: push
-push: ## Push the image to Docker Hub
- @echo "$(GREEN)Pushing to Docker Hub...$(NC)"
- @echo "Pushing $(FULL_IMAGE):$(VERSION)..."
- @docker push $(FULL_IMAGE):$(VERSION)
- @docker push $(FULL_IMAGE):latest || true
- @docker push $(FULL_IMAGE):$(MAJOR_VERSION) || true
- @docker push $(FULL_IMAGE):$(MINOR_VERSION) || true
- @echo "$(GREEN)✓ Push complete!$(NC)"
- @echo "Available at: https://hub.docker.com/r/$(FULL_IMAGE)"
-
-.PHONY: push-dev
-push-dev: ## Push dev image to Docker Hub (default IMAGE_TAG=dev)
- @echo "$(GREEN)Pushing dev image to Docker Hub...$(NC)"
- @docker push $(FULL_IMAGE):$(IMAGE_TAG)
- @echo "$(GREEN)✓ Pushed: $(FULL_IMAGE):$(IMAGE_TAG)$(NC)"
-
-.PHONY: publish-dev
-publish-dev: build-dev push-dev ## Build + Push dev image
- @echo "$(GREEN)✓ Dev image published: $(FULL_IMAGE):$(IMAGE_TAG)$(NC)"
+# Use: make push-production, make push-dev, make publish-all
+# See: make hub-help for all Docker Hub commands
# =============================================================
# RUNTIME TARGETS - RUN
# =============================================================
.PHONY: run
-run: ## Run local container for demo/testing
- @echo "$(GREEN)Starting local container...$(NC)"
+run: ## Run production container
+ @echo "$(GREEN)Starting production container...$(NC)"
@docker stop $(LOCAL_CONTAINER) >/dev/null 2>&1 || true
@docker rm $(LOCAL_CONTAINER) >/dev/null 2>&1 || true
@docker run -d \
--name $(LOCAL_CONTAINER) \
-p $(LOCAL_PORT):80 \
- -e DEMO_MODE=true \
- --env-file .env \
+ --env-file $(ENV_FILE) \
+ -e APP_ENV=production \
-v $(PWD)/logs:/var/log \
$(FULL_IMAGE):latest
- @echo "$(GREEN)✓ Container running at http://localhost:$(LOCAL_PORT)$(NC)"
+ @echo "$(GREEN)OK: Production container running at http://localhost:$(LOCAL_PORT)$(NC)"
@echo "$(CYAN)Container name:$(NC) $(LOCAL_CONTAINER)"
- @echo "\n$(YELLOW)⏳ Waiting for services to start...$(NC)"; sleep 5
- @echo "\n$(CYAN)Testing demo page...$(NC)"
- @if curl -s http://localhost:$(LOCAL_PORT) | grep -q "PHP API Stack"; then \
- echo "$(GREEN)✓ Demo page is live!$(NC)"; \
- echo "$(CYAN)Visit:$(NC) http://localhost:$(LOCAL_PORT)"; \
- else \
- echo "$(YELLOW)⚠ Page loaded but demo not detected$(NC)"; \
- fi
- @echo "\n$(BLUE)Useful commands:$(NC)"
- @echo " $(CYAN)make stop$(NC) # Stop container"
- @echo " $(CYAN)make logs$(NC) # View logs"
- @echo " $(CYAN)make shell$(NC) # Access shell"
- @echo " $(CYAN)make run-with-app$(NC) # Run with app mounted"
-
-.PHONY: run-with-app
-run-with-app: ## Run local container with mounted application
- @echo "$(GREEN)Starting local container with application mount...$(NC)"
- @docker stop $(LOCAL_CONTAINER) >/dev/null 2>&1 || true
- @docker rm $(LOCAL_CONTAINER) >/dev/null 2>&1 || true
- @if [ ! -d "$(PWD)/app" ]; then \
- echo "$(YELLOW)Creating app directory...$(NC)"; \
- mkdir -p $(PWD)/app/public; \
- fi
- @docker run -d \
- --name $(LOCAL_CONTAINER) \
- -p $(LOCAL_PORT):80 \
- -e DEMO_MODE=true \
- --env-file .env \
- -v $(PWD)/app:/var/www/html \
- -v $(PWD)/logs:/var/log \
- $(FULL_IMAGE):latest
- @echo "$(GREEN)✓ Container running at http://localhost:$(LOCAL_PORT)$(NC)"
- @echo "$(CYAN)Application mounted from:$(NC) $(PWD)/app"
- @echo "\n$(BLUE)Next steps:$(NC)"
- @echo " 1. Create your $(CYAN)app/public/index.php$(NC)"
- @echo " 2. Run $(CYAN)docker exec $(LOCAL_CONTAINER) nginx -s reload$(NC)"
- @echo " 3. Visit http://localhost:$(LOCAL_PORT)"
.PHONY: run-dev
-run-dev: ## Run dev container (port 8001, or override: make run-dev DEV_PORT=9000)
+run-dev: ## Run dev container with Xdebug
@echo "$(GREEN)Starting dev container...$(NC)"
@docker stop $(DEV_CONTAINER) >/dev/null 2>&1 || true
@docker rm $(DEV_CONTAINER) >/dev/null 2>&1 || true
@if docker ps --format '{{.Ports}}' | grep -q '$(DEV_PORT)->'; then \
- echo "$(RED)✗ Port $(DEV_PORT) is already in use!$(NC)"; \
+ echo "$(RED)X Port $(DEV_PORT) is already in use!$(NC)"; \
echo "$(YELLOW)Try another port:$(NC) make run-dev DEV_PORT=9000"; \
exit 1; \
fi
@docker run -d \
--name $(DEV_CONTAINER) \
-p $(DEV_PORT):80 \
+ -p 9003:9003 \
--env-file $(ENV_FILE) \
+ -e APP_ENV=development \
+ -e XDEBUG_ENABLE=1 \
-v $(PWD)/logs:/var/log \
$(FULL_IMAGE):$(IMAGE_TAG)
- @echo "$(GREEN)✓ Dev container running at http://localhost:$(DEV_PORT)$(NC)"
-
+ @echo "$(GREEN)OK: Dev container running at http://localhost:$(DEV_PORT)$(NC)"
+ @echo "$(CYAN)Xdebug enabled on port 9003$(NC)"
.PHONY: run-test
-run-test: build-test-image ## Run test container (comprehensive health)
+run-test: build-test ## Run test container
@echo "$(GREEN)Starting test container...$(NC)"
@docker stop $(TEST_CONTAINER) >/dev/null 2>&1 || true
@docker rm $(TEST_CONTAINER) >/dev/null 2>&1 || true
@docker run -d \
--name $(TEST_CONTAINER) \
-p $(TEST_PORT):80 \
- --env-file .env \
+ --env-file $(ENV_FILE) \
-v $(PWD)/logs:/var/log \
$(FULL_IMAGE):test
- @echo "\n$(GREEN)✓ Test container running!$(NC)"
- @echo "$(CYAN)════════════════════════════════════════$(NC)"
- @echo "$(CYAN)Container:$(NC) $(TEST_CONTAINER)"
- @echo "$(CYAN)URL:$(NC) http://localhost:$(TEST_PORT)"
- @echo "$(CYAN)Health Check:$(NC) http://localhost:$(TEST_PORT)/health"
- @echo "$(CYAN)════════════════════════════════════════$(NC)"
- @echo "\n$(YELLOW)⏳ Waiting for services to start...$(NC)"; sleep 5
- @echo "\n$(CYAN)Testing comprehensive health endpoint...$(NC)"
- @curl -s http://localhost:$(TEST_PORT)/health || true
+ @echo "$(GREEN)OK: Test container running at http://localhost:$(TEST_PORT)/health$(NC)"
# =============================================================
# RUNTIME TARGETS - STOP & RESTART
# =============================================================
.PHONY: stop
-stop: ## Stop and remove local container
- @echo "$(YELLOW)Stopping local container...$(NC)"
+stop: ## Stop production container
+ @echo "$(YELLOW)Stopping production container...$(NC)"
@docker stop $(LOCAL_CONTAINER) >/dev/null 2>&1 || true
@docker rm $(LOCAL_CONTAINER) >/dev/null 2>&1 || true
- @echo "$(GREEN)✓ Container stopped$(NC)"
+ @echo "$(GREEN)OK: Container stopped$(NC)"
.PHONY: stop-dev
-stop-dev: ## Stop and remove dev container
+stop-dev: ## Stop dev container
@echo "$(YELLOW)Stopping dev container...$(NC)"
@docker stop $(DEV_CONTAINER) >/dev/null 2>&1 || true
@docker rm $(DEV_CONTAINER) >/dev/null 2>&1 || true
- @echo "$(GREEN)✓ Dev container stopped$(NC)"
+ @echo "$(GREEN)OK: Dev container stopped$(NC)"
.PHONY: stop-test
-stop-test: ## Stop and remove test container
+stop-test: ## Stop test container
@echo "$(YELLOW)Stopping test container...$(NC)"
@docker stop $(TEST_CONTAINER) >/dev/null 2>&1 || true
@docker rm $(TEST_CONTAINER) >/dev/null 2>&1 || true
- @echo "$(GREEN)✓ Test container stopped$(NC)"
+ @echo "$(GREEN)OK: Test container stopped$(NC)"
.PHONY: restart
-restart: stop run ## Restart the local container
+restart: stop run ## Restart production container
.PHONY: restart-dev
-restart-dev: stop-dev run-dev ## Restart the dev container
-
-.PHONY: restart-test
-restart-test: stop-test run-test ## Restart the test container
+restart-dev: stop-dev run-dev ## Restart dev container
# =============================================================
# RUNTIME TARGETS - LOGS & SHELL
# =============================================================
.PHONY: logs
-logs: ## Show logs from local container
+logs: ## Show logs from production container
@if [ -z "$$(docker ps -a -q -f name=$(LOCAL_CONTAINER))" ]; then \
- echo "$(RED)No container named $(LOCAL_CONTAINER) found!$(NC)"; \
- echo "Run '$(CYAN)make run$(NC)' first."; \
+ echo "$(RED)Container $(LOCAL_CONTAINER) not found!$(NC)"; \
else \
- echo "$(GREEN)Showing logs for $(LOCAL_CONTAINER)...$(NC)"; \
docker logs -f $(LOCAL_CONTAINER); \
fi
@@ -332,57 +304,31 @@ logs: ## Show logs from local container
logs-dev: ## Show logs from dev container
@if [ -z "$$(docker ps -a -q -f name=$(DEV_CONTAINER))" ]; then \
echo "$(RED)Dev container not found!$(NC)"; \
- echo "Run '$(CYAN)make run-dev$(NC)' first."; \
else \
- echo "$(GREEN)Showing logs for $(DEV_CONTAINER)...$(NC)"; \
docker logs -f $(DEV_CONTAINER); \
fi
-.PHONY: logs-test
-logs-test: ## Show logs from test container
- @if [ -z "$$(docker ps -a -q -f name=$(TEST_CONTAINER))" ]; then \
- echo "$(RED)Test container not found!$(NC)"; \
- echo "Run '$(CYAN)make run-test$(NC)' first."; \
- else \
- echo "$(GREEN)Showing logs for $(TEST_CONTAINER)...$(NC)"; \
- docker logs -f $(TEST_CONTAINER); \
- fi
-
.PHONY: shell
-shell: exec ## Alias for exec
-
-.PHONY: exec
-exec: ## Execute bash in the running local container
+shell: ## Access shell in production container
@if [ -z "$$(docker ps -q -f name=$(LOCAL_CONTAINER))" ]; then \
- echo "$(RED)No running container found!$(NC)"; \
- echo "Run '$(CYAN)make run$(NC)' first."; \
+ echo "$(RED)Container not running!$(NC)"; \
else \
docker exec -it $(LOCAL_CONTAINER) bash; \
fi
.PHONY: shell-dev
-shell-dev: ## Execute bash in dev container
+shell-dev: ## Access shell in dev container
@if [ -z "$$(docker ps -q -f name=$(DEV_CONTAINER))" ]; then \
echo "$(RED)Dev container not running!$(NC)"; \
- echo "Run '$(CYAN)make run-dev$(NC)' first."; \
else \
docker exec -it $(DEV_CONTAINER) bash; \
fi
-.PHONY: shell-test
-shell-test: ## Execute bash in test container
- @if [ -z "$$(docker ps -q -f name=$(TEST_CONTAINER))" ]; then \
- echo "$(RED)Test container not running!$(NC)"; \
- echo "Run '$(CYAN)make run-test$(NC)' first."; \
- else \
- docker exec -it $(TEST_CONTAINER) bash; \
- fi
-
# =============================================================
# TEST TARGETS
# =============================================================
.PHONY: test-quick
-test-quick: ## Quick test of built image (versions only)
+test-quick: ## Quick version check
@echo "$(GREEN)Running quick version tests...$(NC)"
@echo "\n$(CYAN)Testing PHP:$(NC)"
@docker run --rm --entrypoint php $(FULL_IMAGE):latest -v | head -2
@@ -392,144 +338,16 @@ test-quick: ## Quick test of built image (versions only)
@docker run --rm --entrypoint redis-server $(FULL_IMAGE):latest --version
@echo "\n$(CYAN)Testing Composer:$(NC)"
@docker run --rm --entrypoint composer $(FULL_IMAGE):latest --version --no-ansi | head -1
- @echo "\n$(GREEN)✓ All components verified!$(NC)"
+ @echo "\n$(GREEN)OK: All components verified!$(NC)"
.PHONY: test
-test: ## Run comprehensive tests on the production image
+test: ## Run comprehensive tests
@echo "$(GREEN)Running comprehensive tests...$(NC)"
- @echo "\n$(YELLOW)[1/5] Testing component versions...$(NC)"
@$(MAKE) test-quick --no-print-directory
- @echo "\n$(YELLOW)[2/5] Testing configuration processing...$(NC)"
- @docker run --rm $(FULL_IMAGE):latest /usr/local/bin/process-configs
- @echo "\n$(YELLOW)[3/5] Testing Nginx configuration...$(NC)"
+ @echo "\n$(YELLOW)Testing production configuration...$(NC)"
@docker run --rm --entrypoint nginx $(FULL_IMAGE):latest -t
- @echo "\n$(YELLOW)[4/5] Testing PHP-FPM configuration...$(NC)"
@docker run --rm --entrypoint php-fpm $(FULL_IMAGE):latest -t
- @echo "\n$(YELLOW)[5/5] Testing health endpoint...$(NC)"
- @docker run -d --name $(CI_TEST_CONTAINER) -p $(TEST_PORT):80 $(FULL_IMAGE):latest >/dev/null
- @echo "Waiting for container to start on port $(TEST_PORT)..."
- @tries=0; while ! curl -s -f -o /dev/null http://localhost:$(TEST_PORT)/health; do \
- sleep 2; \
- tries=$$(($$tries + 1)); \
- if [ $$tries -ge 15 ]; then \
- echo "$(RED)✗ Health check failed: Container did not become healthy in time.$(NC)"; \
- docker logs $(CI_TEST_CONTAINER); \
- docker stop $(CI_TEST_CONTAINER) >/dev/null; \
- docker rm $(CI_TEST_CONTAINER) >/dev/null; \
- exit 1; \
- fi; \
- done
- @echo "$(GREEN)✓ Health check passed$(NC)"
- @docker stop $(CI_TEST_CONTAINER) >/dev/null
- @docker rm $(CI_TEST_CONTAINER) >/dev/null
- @echo "\n$(GREEN)✓ All tests complete!$(NC)"
-
-.PHONY: test-health
-test-health: ## Test comprehensive health check endpoint
- @if [ -z "$$(docker ps -q -f name=$(TEST_CONTAINER))" ]; then \
- echo "$(RED)Test container not running!$(NC)"; \
- echo "Run '$(CYAN)make run-test$(NC)' first."; \
- exit 1; \
- fi
- @curl -s http://localhost:$(TEST_PORT)/health.php | jq '.' || curl -s http://localhost:$(TEST_PORT)/health
-
-.PHONY: test-health-status
-test-health-status: ## Show health check status summary
- @if [ -z "$$(docker ps -q -f name=$(TEST_CONTAINER))" ]; then \
- echo "$(RED)Test container not running!$(NC)"; exit 1; \
- fi
- @echo "$(GREEN)Health Check Status Summary$(NC)"
- @echo "$(CYAN)════════════════════════════════════════$(NC)"
- @curl -s http://localhost:$(TEST_PORT)/health.php | jq -r '"Overall: \(.status | ascii_upcase)"' || true
- @curl -s http://localhost:$(TEST_PORT)/health.php | jq -r '"Duration: \(.duration_ms // "n/a")ms"' || true
- @echo ""; echo "$(CYAN)Component Status:$(NC)"; \
- curl -s http://localhost:$(TEST_PORT)/health.php | jq -r '.checks | to_entries[] | " \(.key | ascii_upcase): \(.value.status)"' || true
-
-.PHONY: test-health-watch
-test-health-watch: ## Watch health check status (updates every 5s)
- @if [ -z "$$(docker ps -q -f name=$(TEST_CONTAINER))" ]; then \
- echo "$(RED)Test container not running!$(NC)"; \
- echo "Run '$(CYAN)make run-test$(NC)' first."; \
- exit 1; \
- fi
- @echo "$(GREEN)Watching health status (Ctrl+C to stop)...$(NC)"; echo ""
- @watch -n 5 "curl -s http://localhost:$(TEST_PORT)/health.php | jq '{overall: .status, duration_ms: .duration_ms, checks: (.checks // {})}'"
-
-.PHONY: test-structure
-test-structure: ## Test container structure and files
- @echo "$(GREEN)Testing container structure...$(NC)"
- @docker run --rm $(FULL_IMAGE):latest ls -la /var/www/html/public/ || true
- @docker run --rm $(FULL_IMAGE):latest ls -la /etc/nginx/ || true
- @docker run --rm $(FULL_IMAGE):latest ls -la /usr/local/etc/php/ || true
-
-# =============================================================
-# VALIDATION TARGETS
-# =============================================================
-.PHONY: scan
-scan: ## Scan the image for vulnerabilities using Trivy
- @echo "$(GREEN)Scanning for vulnerabilities...$(NC)"
- @if command -v trivy >/dev/null 2>&1; then \
- trivy image --severity HIGH,CRITICAL $(FULL_IMAGE):latest; \
- else \
- docker run --rm -v /var/run/docker.sock:/var/run/docker.sock aquasec/trivy:latest image --severity HIGH,CRITICAL $(FULL_IMAGE):latest; \
- fi
-
-.PHONY: lint
-lint: ## Lint the Dockerfile with hadolint
- @echo "$(GREEN)Linting Dockerfile...$(NC)"
- @if command -v hadolint >/dev/null 2>&1; then \
- hadolint Dockerfile || true; \
- else \
- docker run --rm -i hadolint/hadolint < Dockerfile || true; \
- fi
-
-# =============================================================
-# RELEASE TARGETS
-# =============================================================
-.PHONY: tag-latest
-tag-latest: ## Tag current version as latest/major/minor
- @docker tag $(FULL_IMAGE):$(VERSION) $(FULL_IMAGE):latest || true
- @docker tag $(FULL_IMAGE):$(VERSION) $(FULL_IMAGE):$(MAJOR_VERSION) || true
- @docker tag $(FULL_IMAGE):$(VERSION) $(FULL_IMAGE):$(MINOR_VERSION) || true
- @echo "$(GREEN)✓ Tags created: latest, $(MAJOR_VERSION), $(MINOR_VERSION)$(NC)"
-
-.PHONY: release
-release: lint build test scan tag-latest push ## Full release pipeline
- @echo "$(GREEN)════════════════════════════════════════$(NC)"
- @echo "$(GREEN)✅ Release $(VERSION) complete!$(NC)"
- @echo "$(GREEN)════════════════════════════════════════$(NC)"
-
-# =============================================================
-# VERSION MANAGEMENT
-# =============================================================
-.PHONY: version
-version: ## Display current version
- @echo "$(CYAN)Current version:$(NC) $(VERSION)"
- @echo "$(CYAN)Docker image:$(NC) $(FULL_IMAGE):$(VERSION)"
-
-.PHONY: bump-patch
-bump-patch: ## Bump patch version (x.x.X)
- @VERSION=$$(echo $(VERSION) | awk -F. '{print $$1"."$$2"."$$3+1}'); \
- echo $$VERSION > VERSION; \
- echo "$(GREEN)✓ Version bumped to $$VERSION$(NC)"; \
- git add VERSION 2>/dev/null || true; \
- git commit -m "chore: bump version to $$VERSION" 2>/dev/null || true
-
-.PHONY: bump-minor
-bump-minor: ## Bump minor version (x.X.x)
- @VERSION=$$(echo $(VERSION) | awk -F. '{print $$1"."$$2+1".0"}'); \
- echo $$VERSION > VERSION; \
- echo "$(GREEN)✓ Version bumped to $$VERSION$(NC)"; \
- git add VERSION 2>/dev/null || true; \
- git commit -m "chore: bump version to $$VERSION" 2>/dev/null || true
-
-.PHONY: bump-major
-bump-major: ## Bump major version (X.x.x)
- @VERSION=$$(echo $(VERSION) | awk -F. '{print $$1+1".0.0"}'); \
- echo $$VERSION > VERSION; \
- echo "$(GREEN)✓ Version bumped to $$VERSION$(NC)"; \
- git add VERSION 2>/dev/null || true; \
- git commit -m "chore: bump version to $$VERSION" 2>/dev/null || true
+ @echo "$(GREEN)OK: All tests passed!$(NC)"
# =============================================================
# CLEANUP TARGETS
@@ -537,65 +355,64 @@ bump-major: ## Bump major version (X.x.x)
.PHONY: clean
clean: ## Remove local images and containers
@echo "$(YELLOW)Cleaning up...$(NC)"
- @docker stop $(LOCAL_CONTAINER) $(TEST_CONTAINER) >/dev/null 2>&1 || true
- @docker rm $(LOCAL_CONTAINER) $(TEST_CONTAINER) >/dev/null 2>&1 || true
+ @docker stop $(LOCAL_CONTAINER) $(DEV_CONTAINER) $(TEST_CONTAINER) >/dev/null 2>&1 || true
+ @docker rm $(LOCAL_CONTAINER) $(DEV_CONTAINER) $(TEST_CONTAINER) >/dev/null 2>&1 || true
@docker rmi $(FULL_IMAGE):$(VERSION) >/dev/null 2>&1 || true
@docker rmi $(FULL_IMAGE):latest >/dev/null 2>&1 || true
+ @docker rmi $(FULL_IMAGE):dev >/dev/null 2>&1 || true
@docker rmi $(FULL_IMAGE):test >/dev/null 2>&1 || true
- @echo "$(GREEN)✓ Cleanup complete$(NC)"
-
-.PHONY: clean-all
-clean-all: clean ## Deep clean including volumes and build cache
- @echo "$(YELLOW)Deep cleaning...$(NC)"
- @docker volume prune -f 2>/dev/null || true
- @docker builder prune -f 2>/dev/null || true
- @echo "$(GREEN)✓ Deep cleanup complete$(NC)"
+ @echo "$(GREEN)OK: Cleanup complete$(NC)"
# =============================================================
# INFORMATION TARGETS
# =============================================================
.PHONY: info
-info: ## Show image information
- @echo "$(CYAN)════════════════════════════════════════$(NC)"
- @echo "$(GREEN)PHP API Stack - Image Information$(NC)"
- @echo "$(CYAN)════════════════════════════════════════$(NC)"
- @echo " $(CYAN)Hub User:$(NC) $(DOCKER_HUB_USER)"
- @echo " $(CYAN)Image:$(NC) $(IMAGE_NAME)"
- @echo " $(CYAN)Version:$(NC) $(VERSION)"
- @echo " $(CYAN)Full path:$(NC) $(FULL_IMAGE):$(VERSION)"
- @echo "\n$(GREEN)Container names:$(NC)"
- @echo " $(CYAN)Local:$(NC) $(LOCAL_CONTAINER) (Port: $(LOCAL_PORT))"
- @echo " $(CYAN)Test:$(NC) $(TEST_CONTAINER) (Port: $(TEST_PORT))"
- @echo "\n$(GREEN)Available tags:$(NC)"
- @echo " - $(FULL_IMAGE):$(VERSION)"
- @echo " - $(FULL_IMAGE):$(MAJOR_VERSION)"
- @echo " - $(FULL_IMAGE):$(MINOR_VERSION)"
- @echo " - $(FULL_IMAGE):latest"
- @echo " - $(FULL_IMAGE):test"
+info: ## Show build information
+ @echo "$(CYAN)╔══════════════════════════════════════════╗$(NC)"
+ @echo "$(GREEN)║ PHP API Stack - Build Information ║$(NC)"
+ @echo "$(CYAN)╚══════════════════════════════════════════╝$(NC)"
@echo ""
- @if docker images $(FULL_IMAGE) --format "table {{.Repository}}:{{.Tag}}\t{{.Size}}\t{{.CreatedAt}}" | grep -q $(IMAGE_NAME); then \
- echo "$(GREEN)Local images:$(NC)"; \
- docker images $(FULL_IMAGE) --format "table {{.Repository}}:{{.Tag}}\t{{.Size}}\t{{.CreatedAt}}"; \
- else \
- echo "$(YELLOW)No local images found. Run 'make build' to create one.$(NC)"; \
- fi
-
-.PHONY: stats
-stats: ## Show container resource usage
- @if [ -z "$$(docker ps -q -f name=$(LOCAL_CONTAINER))" ]; then \
- echo "$(YELLOW)Local container not running. Checking test container...$(NC)"; \
- if [ -z "$$(docker ps -q -f name=$(TEST_CONTAINER))" ]; then \
- echo "$(RED)No running containers found!$(NC)"; \
- else \
- echo "$(GREEN)Test container resource usage:$(NC)"; \
- docker stats $(TEST_CONTAINER) --no-stream; \
- fi; \
- else \
- echo "$(GREEN)Local container resource usage:$(NC)"; \
- docker stats $(LOCAL_CONTAINER) --no-stream; \
- fi
-
+ @echo "$(CYAN)Architecture:$(NC)"
+ @echo " Base -> Production | Dev"
+ @echo ""
+ @echo "$(CYAN)Available Stages:$(NC)"
+ @echo " • base (foundation layer)"
+ @echo " • production (optimized for production)"
+ @echo " • dev (with Xdebug + Symfony CLI)"
+ @echo ""
+ @echo "$(CYAN)Version:$(NC) $(VERSION)"
+ @echo "$(CYAN)Full Image:$(NC) $(FULL_IMAGE)"
+ @echo "$(CYAN)PHP Version:$(NC) $(PHP_VERSION)"
+ @echo "$(CYAN)Nginx:$(NC) $(NGINX_VERSION)"
+ @echo "$(CYAN)Redis:$(NC) $(REDIS_VERSION)"
+
+
+.PHONY: info-versions
+info-versions: ## Show detailed version information
+ @echo "$(CYAN)╔═══════════════════════════════════════════════╗$(NC)"
+ @echo "$(GREEN)║ Component Versions - PHP API Stack ║$(NC)"
+ @echo "$(CYAN)╚═══════════════════════════════════════════════╝$(NC)"
+ @echo ""
+ @echo "$(CYAN)Base Components:$(NC)"
+ @echo " PHP: $(PHP_VERSION)"
+ @echo " Nginx: $(NGINX_VERSION)"
+ @echo " Redis Server: $(REDIS_VERSION)"
+ @echo " Alpine: $(ALPINE_VERSION)"
+ @echo " Composer: $(COMPOSER_VERSION)"
+ @echo ""
+ @echo "$(CYAN)PECL Extensions:$(NC)"
+ @echo " redis: $(PHP_REDIS_VERSION)"
+ @echo " apcu: $(PHP_APCU_VERSION)"
+ @echo " uuid: $(PHP_UUID_VERSION)"
+ @echo " imagick: $(PHP_IMAGICK_VERSION)"
+ @echo " amqp: $(PHP_AMQP_VERSION)"
+ @echo ""
+ @echo "$(CYAN)Dev Tools:$(NC)"
+ @echo " Symfony CLI: $(SYMFONY_CLI_VERSION)"
+ @echo " Xdebug: $(XDEBUG_VERSION)"
+
# =============================================================
-# DOCKER-COMPOSE TARGETS (optional include)
+# INCLUDE ADDITIONAL MAKEFILES
# =============================================================
+-include Makefile.dockerhub
-include Makefile.compose
\ No newline at end of file
diff --git a/Makefile.dockerhub b/Makefile.dockerhub
new file mode 100644
index 0000000..1ec3840
--- /dev/null
+++ b/Makefile.dockerhub
@@ -0,0 +1,397 @@
+# Makefile.dockerhub - Docker Hub Publication Commands
+# Include in main Makefile with: -include Makefile.dockerhub
+
+# =============================================================
+# DOCKER HUB AUTHENTICATION
+# =============================================================
+.PHONY: docker-login
+docker-login: ## Login to Docker Hub
+ @echo "$(GREEN)Logging in to Docker Hub...$(NC)"
+ @if docker info 2>/dev/null | grep -q "Username"; then \
+ echo "$(CYAN)Already logged in$(NC)"; \
+ else \
+ docker login -u $(DOCKER_HUB_USER); \
+ fi
+
+.PHONY: docker-logout
+docker-logout: ## Logout from Docker Hub
+ @echo "$(YELLOW)Logging out from Docker Hub...$(NC)"
+ @docker logout
+ @echo "$(GREEN)OK: Logged out$(NC)"
+
+# =============================================================
+# VERSION MANAGEMENT
+# =============================================================
+.PHONY: version
+version: ## Display current version
+ @echo "$(CYAN)Current version:$(NC) $(VERSION)"
+ @echo "$(CYAN)Docker image:$(NC) $(FULL_IMAGE):$(VERSION)"
+ @echo "$(CYAN)Major:$(NC) $(MAJOR_VERSION)"
+ @echo "$(CYAN)Minor:$(NC) $(MINOR_VERSION)"
+
+.PHONY: bump-patch
+bump-patch: ## Bump patch version (x.x.X)
+ @NEW_VERSION=$$(echo $(VERSION) | awk -F. '{print $$1"."$$2"."$$3+1}'); \
+ echo $$NEW_VERSION > VERSION; \
+ echo "$(GREEN)OK: Version bumped to $$NEW_VERSION$(NC)"; \
+ git add VERSION 2>/dev/null || true; \
+ git commit -m "chore: bump version to $$NEW_VERSION" 2>/dev/null || true
+
+.PHONY: bump-minor
+bump-minor: ## Bump minor version (x.X.0)
+ @NEW_VERSION=$$(echo $(VERSION) | awk -F. '{print $$1"."$$2+1".0"}'); \
+ echo $$NEW_VERSION > VERSION; \
+ echo "$(GREEN)OK: Version bumped to $$NEW_VERSION$(NC)"; \
+ git add VERSION 2>/dev/null || true; \
+ git commit -m "chore: bump version to $$NEW_VERSION" 2>/dev/null || true
+
+.PHONY: bump-major
+bump-major: ## Bump major version (X.0.0)
+ @NEW_VERSION=$$(echo $(VERSION) | awk -F. '{print $$1+1".0.0"}'); \
+ echo $$NEW_VERSION > VERSION; \
+ echo "$(GREEN)OK: Version bumped to $$NEW_VERSION$(NC)"; \
+ git add VERSION 2>/dev/null || true; \
+ git commit -m "chore: bump version to $$NEW_VERSION" 2>/dev/null || true
+
+# =============================================================
+# TAGGING
+# =============================================================
+.PHONY: tag-production
+tag-production: ## Tag production image with version tags
+ @echo "$(GREEN)Tagging production image...$(NC)"
+ @docker tag $(FULL_IMAGE):latest $(FULL_IMAGE):$(VERSION)
+ @docker tag $(FULL_IMAGE):latest $(FULL_IMAGE):$(MAJOR_VERSION)
+ @docker tag $(FULL_IMAGE):latest $(FULL_IMAGE):$(MINOR_VERSION)
+ @echo "$(GREEN)OK: Tags created:$(NC)"
+ @echo " • $(VERSION)"
+ @echo " • $(MAJOR_VERSION)"
+ @echo " • $(MINOR_VERSION)"
+ @echo " • latest"
+
+.PHONY: tag-dev
+tag-dev: ## Tag dev image
+ @echo "$(GREEN)Tagging dev image...$(NC)"
+ @echo "$(GREEN)OK: Tag created:$(NC)"
+ @echo " • dev"
+
+.PHONY: tag-all
+tag-all: tag-production tag-dev ## Tag all images
+
+# =============================================================
+# PUSH TO DOCKER HUB
+# =============================================================
+.PHONY: push-production
+push-production: docker-login ## Push production image to Docker Hub
+ @echo "$(GREEN)Pushing production images to Docker Hub...$(NC)"
+ @echo ""
+ @echo "$(CYAN)Pushing $(FULL_IMAGE):$(VERSION)...$(NC)"
+ @docker push $(FULL_IMAGE):$(VERSION)
+ @echo "$(CYAN)Pushing $(FULL_IMAGE):latest...$(NC)"
+ @docker push $(FULL_IMAGE):latest
+ @echo "$(CYAN)Pushing $(FULL_IMAGE):$(MAJOR_VERSION)...$(NC)"
+ @docker push $(FULL_IMAGE):$(MAJOR_VERSION)
+ @echo "$(CYAN)Pushing $(FULL_IMAGE):$(MINOR_VERSION)...$(NC)"
+ @docker push $(FULL_IMAGE):$(MINOR_VERSION)
+ @echo ""
+ @echo "$(GREEN)OK: Production images pushed successfully!$(NC)"
+ @echo "$(CYAN)Available at: https://hub.docker.com/r/$(FULL_IMAGE)$(NC)"
+
+.PHONY: push-dev
+push-dev: docker-login ## Push dev image to Docker Hub
+ @echo "$(GREEN)Pushing dev image to Docker Hub...$(NC)"
+ @echo ""
+ @echo "$(CYAN)Pushing $(FULL_IMAGE):dev...$(NC)"
+ @docker push $(FULL_IMAGE):dev
+ @echo ""
+ @echo "$(GREEN)OK: Dev image pushed successfully!$(NC)"
+
+.PHONY: push-base
+push-base: docker-login ## Push base image to Docker Hub
+ @echo "$(GREEN)Pushing base image to Docker Hub...$(NC)"
+ @docker push $(FULL_IMAGE):base
+ @echo "$(GREEN)OK: Base image pushed!$(NC)"
+
+.PHONY: push
+push: push-production ## Alias for push-production
+
+.PHONY: push-all
+push-all: push-production push-dev ## Push all images (production + dev)
+ @echo ""
+ @echo "$(GREEN)==========================================$(NC)"
+ @echo "$(GREEN)OK: All images pushed successfully!$(NC)"
+ @echo "$(GREEN)==========================================$(NC)"
+
+# =============================================================
+# VALIDATION & SECURITY
+# =============================================================
+.PHONY: lint
+lint: ## Lint Dockerfile with hadolint
+ @echo "$(GREEN)Linting Dockerfile...$(NC)"
+ @if command -v hadolint >/dev/null 2>&1; then \
+ hadolint Dockerfile || true; \
+ else \
+ docker run --rm -i hadolint/hadolint < Dockerfile || true; \
+ fi
+ @echo "$(GREEN)OK: Lint complete$(NC)"
+
+.PHONY: scan
+scan: ## Scan image for vulnerabilities (Trivy)
+ @echo "$(GREEN)Scanning for vulnerabilities...$(NC)"
+ @if command -v trivy >/dev/null 2>&1; then \
+ trivy image --severity HIGH,CRITICAL $(FULL_IMAGE):latest; \
+ else \
+ docker run --rm -v /var/run/docker.sock:/var/run/docker.sock \
+ aquasec/trivy:latest image --severity HIGH,CRITICAL $(FULL_IMAGE):latest; \
+ fi
+
+.PHONY: scan-dev
+scan-dev: ## Scan dev image for vulnerabilities
+ @echo "$(GREEN)Scanning dev image...$(NC)"
+ @if command -v trivy >/dev/null 2>&1; then \
+ trivy image --severity HIGH,CRITICAL $(FULL_IMAGE):dev; \
+ else \
+ docker run --rm -v /var/run/docker.sock:/var/run/docker.sock \
+ aquasec/trivy:latest image --severity HIGH,CRITICAL $(FULL_IMAGE):dev; \
+ fi
+
+.PHONY: validate-images
+validate-images: ## Validate all images exist locally
+ @echo "$(GREEN)Validating images...$(NC)"
+ @MISSING=0; \
+ for tag in latest $(VERSION) $(MAJOR_VERSION) $(MINOR_VERSION) dev; do \
+ if docker image inspect $(FULL_IMAGE):$$tag >/dev/null 2>&1; then \
+ echo "$(GREEN)✓ $(FULL_IMAGE):$$tag$(NC)"; \
+ else \
+ echo "$(RED)✗ $(FULL_IMAGE):$$tag (missing)$(NC)"; \
+ MISSING=$$((MISSING + 1)); \
+ fi; \
+ done; \
+ if [ $$MISSING -gt 0 ]; then \
+ echo ""; \
+ echo "$(YELLOW)WARNING: $$MISSING image(s) missing. Run 'make build-all' first.$(NC)"; \
+ exit 1; \
+ fi; \
+ echo "$(GREEN)OK: All images validated$(NC)"
+
+# =============================================================
+# PUBLISH (BUILD + PUSH)
+# =============================================================
+.PHONY: publish-production
+publish-production: build tag-production push-production ## Build, tag and push production
+ @echo "$(GREEN)OK: Production published: $(VERSION)$(NC)"
+
+.PHONY: publish-dev
+publish-dev: build-dev tag-dev push-dev ## Build, tag and push dev
+ @echo "$(GREEN)OK: Dev published: dev$(NC)"
+
+.PHONY: publish-all
+publish-all: build-all tag-all push-all ## Build, tag and push all images
+ @echo "$(GREEN)OK: All images published$(NC)"
+
+# =============================================================
+# RELEASE PIPELINE
+# =============================================================
+.PHONY: pre-release-check
+pre-release-check: ## Pre-release validation checks
+ @echo "$(GREEN)Running pre-release checks...$(NC)"
+ @echo ""
+ @echo "$(CYAN)[1/5] Checking Git status...$(NC)"
+ @if [ -n "$$(git status --porcelain 2>/dev/null)" ]; then \
+ echo "$(RED)✗ Uncommitted changes detected$(NC)"; \
+ git status --short; \
+ exit 1; \
+ fi
+ @echo "$(GREEN)✓ Git working directory clean$(NC)"
+ @echo ""
+ @echo "$(CYAN)[2/5] Checking Docker daemon...$(NC)"
+ @docker info >/dev/null 2>&1 || (echo "$(RED)✗ Docker daemon not running$(NC)" && exit 1)
+ @echo "$(GREEN)✓ Docker daemon running$(NC)"
+ @echo ""
+ @echo "$(CYAN)[3/5] Checking Docker Hub authentication...$(NC)"
+ @if ! docker info 2>/dev/null | grep -q "Username"; then \
+ echo "$(YELLOW)⚠ Not logged in to Docker Hub$(NC)"; \
+ $(MAKE) docker-login; \
+ else \
+ echo "$(GREEN)✓ Logged in to Docker Hub$(NC)"; \
+ fi
+ @echo ""
+ @echo "$(CYAN)[4/5] Checking VERSION file...$(NC)"
+ @if [ ! -f VERSION ]; then \
+ echo "$(RED)✗ VERSION file not found$(NC)"; \
+ exit 1; \
+ fi
+ @echo "$(GREEN)✓ VERSION file exists: $(VERSION)$(NC)"
+ @echo ""
+ @echo "$(CYAN)[5/5] Linting Dockerfile...$(NC)"
+ @$(MAKE) lint --no-print-directory
+ @echo ""
+ @echo "$(GREEN)✓ All pre-release checks passed!$(NC)"
+
+.PHONY: release-production
+release-production: pre-release-check build test scan tag-production push-production ## Full production release pipeline
+ @echo ""
+ @echo "$(GREEN)╔═══════════════════════════════════════════════════╗$(NC)"
+ @echo "$(GREEN)║ OK: Production Release Complete! ║$(NC)"
+ @echo "$(GREEN)╚═══════════════════════════════════════════════════╝$(NC)"
+ @echo ""
+ @echo "$(CYAN)Version:$(NC) $(VERSION)"
+ @echo "$(CYAN)Image:$(NC) $(FULL_IMAGE):$(VERSION)"
+ @echo "$(CYAN)Docker Hub:$(NC) https://hub.docker.com/r/$(FULL_IMAGE)"
+ @echo ""
+ @echo "$(YELLOW)Available tags:$(NC)"
+ @echo " • $(FULL_IMAGE):$(VERSION)"
+ @echo " • $(FULL_IMAGE):$(MAJOR_VERSION)"
+ @echo " • $(FULL_IMAGE):$(MINOR_VERSION)"
+ @echo " • $(FULL_IMAGE):latest"
+ @echo ""
+ @echo "$(CYAN)Pull command:$(NC)"
+ @echo " docker pull $(FULL_IMAGE):latest"
+ @echo ""
+
+.PHONY: release-dev
+release-dev: build-dev tag-dev push-dev ## Release dev image
+ @echo ""
+ @echo "$(GREEN)OK: Dev Release Complete: dev$(NC)"
+
+.PHONY: release-all
+release-all: pre-release-check build-all test scan tag-all push-all ## Full release (production + dev)
+ @echo ""
+ @echo "$(GREEN)╔═══════════════════════════════════════════════════╗$(NC)"
+ @echo "$(GREEN)║ OK: Full Release Complete! ║$(NC)"
+ @echo "$(GREEN)╚═══════════════════════════════════════════════════╝$(NC)"
+ @echo ""
+ @echo "$(CYAN)Version:$(NC) $(VERSION)"
+ @echo ""
+ @echo "$(YELLOW)Production tags:$(NC)"
+ @echo " • $(FULL_IMAGE):$(VERSION)"
+ @echo " • $(FULL_IMAGE):latest"
+ @echo ""
+ @echo "$(YELLOW)Development tag:$(NC)"
+ @echo " • $(FULL_IMAGE):dev"
+
+.PHONY: release
+release: release-production ## Alias for release-production
+
+# =============================================================
+# DOCKER HUB UTILITIES
+# =============================================================
+.PHONY: hub-check
+hub-check: ## Check if image exists on Docker Hub
+ @echo "$(GREEN)Checking Docker Hub...$(NC)"
+ @printf "$(CYAN)Checking $(FULL_IMAGE):latest...$(NC) "; \
+ if docker manifest inspect $(FULL_IMAGE):latest >/dev/null 2>&1; then \
+ echo "$(GREEN)✓ exists$(NC)"; \
+ else \
+ echo "$(YELLOW)✗ not found$(NC)"; \
+ fi
+ @printf "$(CYAN)Checking $(FULL_IMAGE):$(VERSION)...$(NC) "; \
+ if docker manifest inspect $(FULL_IMAGE):$(VERSION) >/dev/null 2>&1; then \
+ echo "$(GREEN)✓ exists$(NC)"; \
+ else \
+ echo "$(YELLOW)✗ not found$(NC)"; \
+ fi
+ @printf "$(CYAN)Checking $(FULL_IMAGE):dev...$(NC) "; \
+ if docker manifest inspect $(FULL_IMAGE):dev >/dev/null 2>&1; then \
+ echo "$(GREEN)✓ exists$(NC)"; \
+ else \
+ echo "$(YELLOW)✗ not found$(NC)"; \
+ fi
+
+.PHONY: hub-tags
+hub-tags: ## List all tags on Docker Hub
+ @echo "$(GREEN)Fetching tags from Docker Hub...$(NC)"
+ @curl -s https://registry.hub.docker.com/v2/repositories/$(FULL_IMAGE)/tags/ | \
+ jq -r '.results[].name' | sort -V || \
+ echo "$(YELLOW)Install jq for better output: apt install jq$(NC)"
+
+.PHONY: hub-info
+hub-info: ## Show Docker Hub repository info
+ @echo "$(CYAN)╔═══════════════════════════════════════════════════╗$(NC)"
+ @echo "$(GREEN)║ Docker Hub Repository Information ║$(NC)"
+ @echo "$(CYAN)╚═══════════════════════════════════════════════════╝$(NC)"
+ @echo ""
+ @echo "$(CYAN)Repository:$(NC) $(FULL_IMAGE)"
+ @echo "$(CYAN)URL:$(NC) https://hub.docker.com/r/$(FULL_IMAGE)"
+ @echo "$(CYAN)Current Ver:$(NC) $(VERSION)"
+ @echo ""
+ @echo "$(YELLOW)Expected Production Tags:$(NC)"
+ @echo " • latest"
+ @echo " • $(VERSION)"
+ @echo " • $(MAJOR_VERSION)"
+ @echo " • $(MINOR_VERSION)"
+ @echo ""
+ @echo "$(YELLOW)Expected Dev Tag:$(NC)"
+ @echo " • dev"
+ @echo ""
+ @$(MAKE) hub-check --no-print-directory
+
+.PHONY: hub-clean
+hub-clean: ## Remove old local tags (keeps latest, current version, dev)
+ @echo "$(YELLOW)Cleaning old local tags...$(NC)"
+ @echo "$(CYAN)Keeping: latest, $(VERSION), dev$(NC)"
+ @docker images $(FULL_IMAGE) --format "{{.Tag}}" | \
+ grep -v -E "^(latest|$(VERSION)|dev|$(MAJOR_VERSION)|$(MINOR_VERSION))$$" | \
+ xargs -I {} sh -c 'echo "Removing $(FULL_IMAGE):{}" && docker rmi $(FULL_IMAGE):{} 2>/dev/null' || true
+ @echo "$(GREEN)OK: Cleanup complete$(NC)"
+
+# =============================================================
+# HELP FOR DOCKER HUB COMMANDS
+# =============================================================
+.PHONY: hub-help
+hub-help: ## Show Docker Hub specific help
+ @echo "$(GREEN)╔═══════════════════════════════════════════════════╗$(NC)"
+ @echo "$(GREEN)║ Docker Hub Commands - Quick Reference ║$(NC)"
+ @echo "$(GREEN)╚═══════════════════════════════════════════════════╝$(NC)"
+ @echo ""
+ @echo "$(MAGENTA)=== AUTHENTICATION ===$(NC)"
+ @echo " $(YELLOW)make docker-login$(NC) Login to Docker Hub"
+ @echo " $(YELLOW)make docker-logout$(NC) Logout from Docker Hub"
+ @echo ""
+ @echo "$(MAGENTA)=== VERSION MANAGEMENT ===$(NC)"
+ @echo " $(YELLOW)make version$(NC) Show current version"
+ @echo " $(YELLOW)make bump-patch$(NC) Bump patch version (x.x.X)"
+ @echo " $(YELLOW)make bump-minor$(NC) Bump minor version (x.X.0)"
+ @echo " $(YELLOW)make bump-major$(NC) Bump major version (X.0.0)"
+ @echo ""
+ @echo "$(MAGENTA)=== TAGGING ===$(NC)"
+ @echo " $(YELLOW)make tag-production$(NC) Tag production image"
+ @echo " $(YELLOW)make tag-dev$(NC) Tag dev image"
+ @echo " $(YELLOW)make tag-all$(NC) Tag all images"
+ @echo ""
+ @echo "$(MAGENTA)=== PUSH TO HUB ===$(NC)"
+ @echo " $(YELLOW)make push-production$(NC) Push production images"
+ @echo " $(YELLOW)make push-dev$(NC) Push dev image"
+ @echo " $(YELLOW)make push-all$(NC) Push all images"
+ @echo ""
+ @echo "$(MAGENTA)=== COMPLETE WORKFLOWS ===$(NC)"
+ @echo " $(YELLOW)make publish-production$(NC) Build + Tag + Push production"
+ @echo " $(YELLOW)make publish-dev$(NC) Build + Tag + Push dev"
+ @echo " $(YELLOW)make publish-all$(NC) Build + Tag + Push all"
+ @echo ""
+ @echo "$(MAGENTA)=== RELEASE PIPELINE ===$(NC)"
+ @echo " $(YELLOW)make release-production$(NC) Full production release"
+ @echo " $(YELLOW)make release-dev$(NC) Release dev image"
+ @echo " $(YELLOW)make release-all$(NC) Full release (prod + dev)"
+ @echo " $(YELLOW)make release$(NC) Alias for release-production"
+ @echo ""
+ @echo "$(MAGENTA)=== VALIDATION ===$(NC)"
+ @echo " $(YELLOW)make lint$(NC) Lint Dockerfile"
+ @echo " $(YELLOW)make scan$(NC) Scan for vulnerabilities"
+ @echo " $(YELLOW)make validate-images$(NC) Check local images exist"
+ @echo " $(YELLOW)make pre-release-check$(NC) Pre-release validation"
+ @echo ""
+ @echo "$(MAGENTA)=== HUB UTILITIES ===$(NC)"
+ @echo " $(YELLOW)make hub-check$(NC) Check if images exist on Hub"
+ @echo " $(YELLOW)make hub-tags$(NC) List all Hub tags"
+ @echo " $(YELLOW)make hub-info$(NC) Show repository info"
+ @echo " $(YELLOW)make hub-clean$(NC) Remove old local tags"
+ @echo ""
+ @echo "$(BLUE)Examples:$(NC)"
+ @echo " $(CYAN)# Quick release$(NC)"
+ @echo " make release-production"
+ @echo ""
+ @echo " $(CYAN)# Version bump + release$(NC)"
+ @echo " make bump-patch && make release-production"
+ @echo ""
+ @echo " $(CYAN)# Build and publish dev$(NC)"
+ @echo " make publish-dev"
\ No newline at end of file
diff --git a/README.md b/README.md
index 1995665..5cc69fc 100644
--- a/README.md
+++ b/README.md
@@ -4,6 +4,7 @@
[](https://hub.docker.com/r/kariricode/php-api-stack)
[](https://hub.docker.com/r/kariricode/php-api-stack)
+[](https://hub.docker.com/r/kariricode/php-api-stack)
[](LICENSE)
[](https://github.com/kariricode/php-api-stack/actions)
@@ -22,26 +23,26 @@
- 🔴 **Redis 7.2** for caching and session management
- 🎯 **Production-ready** with security hardening and performance tuning
- 📊 **Comprehensive health checks** for monitoring and orchestration
-- 🛠️ **Developer-friendly** with extensive Make targets and examples
+- 🛠️ **Developer-friendly** with extensive Make targets (50+) and examples
- 🔒 **Security-first** with rate limiting, headers, and vulnerability scanning
- 📦 **Multi-platform** support (amd64, arm64)
- 🎭 **Flexible deployment** via Docker or Docker Compose with profiles
+- 🧰 **Three specialized Makefiles** for building, Docker Hub, and Compose operations
---
## 🚀 Quick Start
-### Option 1: Docker Run (Simple)
+### Option 1: Docker Run (Simplest)
```bash
-# Pull the image
+# Pull and run with demo page
docker pull kariricode/php-api-stack:latest
-
-# Run with demo page
docker run -d -p 8080:80 --name my-api kariricode/php-api-stack:latest
# Access the demo
-open http://localhost:8080
+curl http://localhost:8080
+# or open http://localhost:8080 in browser
```
### Option 2: With Your Application
@@ -50,223 +51,338 @@ open http://localhost:8080
docker run -d \
-p 8080:80 \
-v $(pwd)/app:/var/www/html \
- --env-file .env \
+ -v $(pwd)/.env:/var/www/html/.env:ro \
+ -e APP_ENV=production \
--name my-api \
kariricode/php-api-stack:latest
```
-### Option 3: Docker Compose (Recommended for Development)
+### Option 3: Docker Compose (Recommended)
```bash
-# Copy example files
+# Setup
cp .env.example .env
cp docker-compose.example.yml docker-compose.yml
-# Start with profiles
+# Start base services
+make compose-up
+
+# Start with database + monitoring
make compose-up PROFILES="db,monitoring"
# Or start everything
make compose-up-all
+
+# Access
+open http://localhost:8089 # Application
+open http://localhost:8089/health # Health check
+open http://localhost:3000 # Grafana (admin/password)
```
**📖 See [DOCKER_COMPOSE_GUIDE.md](DOCKER_COMPOSE_GUIDE.md) for complete Docker Compose documentation**
-✅ **The stack is ready with demo page, health checks, and all services running.**
-
---
## 📚 Documentation
| Document | Audience | Description |
|----------|----------|-------------|
-| **[IMAGE_USAGE_GUIDE.md](IMAGE_USAGE_GUIDE.md)** | End Users | How to use the published image |
-| **[DOCKER_COMPOSE_GUIDE.md](DOCKER_COMPOSE_GUIDE.md)** | Developers | Complete Docker Compose setup guide |
-| **[TESTING.md](TESTING.md)** | Maintainers | Complete testing guide |
-| **[DOCKER_HUB.md](DOCKER_HUB.md)** | Publishers | How to publish to Docker Hub |
+| **[IMAGE_USAGE_GUIDE.md](IMAGE_USAGE_GUIDE.md)** | End Users | How to use the published Docker image |
+| **[DOCKER_COMPOSE_GUIDE.md](DOCKER_COMPOSE_GUIDE.md)** | Developers | Complete Docker Compose orchestration guide |
+| **[TESTING.md](TESTING.md)** | Maintainers | Comprehensive testing procedures |
+| **[DOCKER_HUB.md](DOCKER_HUB.md)** | Publishers | Docker Hub publication workflow |
---
## 🗃️ Architecture
```
-Client → Nginx (port 80) → PHP-FPM (Unix socket) → PHP Application
- ↓ ↓
- FastCGI Cache Redis (sessions/cache)
-```
-
-**Container Management**: Services managed by custom entrypoint with health monitoring
+ ┌─────────────────────────────────┐
+ │ Client Request │
+ └─────────────┬───────────────────┘
+ │
+ ┌─────────────▼───────────────────┐
+ │ Nginx (port 80) │
+ │ • FastCGI Cache │
+ │ • Rate Limiting │
+ │ • Security Headers │
+ └─────────────┬───────────────────┘
+ │ Unix Socket
+ ┌─────────────▼───────────────────┐
+ │ PHP-FPM 8.4 │
+ │ • OPcache + JIT │
+ │ • Pool Manager (60 children) │
+ └─────────────┬───────────────────┘
+ │
+ ┌─────────────▼───────────────────┐
+ │ PHP Application │
+ │ • Framework (Symfony/Laravel) │
+ │ • Business Logic │
+ └─────────────┬───────────────────┘
+ │
+ ┌─────────────▼───────────────────┐
+ │ Redis (sessions/cache) │
+ │ • AOF Persistence │
+ │ • LRU Eviction │
+ └─────────────────────────────────┘
+```
+
+**Container Management**: All services orchestrated by custom entrypoint with health monitoring and graceful shutdown
---
## 📦 Stack Components
-| Component | Version | Purpose |
-|-----------|---------|---------|
-| **PHP-FPM** | 8.4 | PHP processing with optimized pool |
-| **Nginx** | 1.27.3 | High-performance web server |
-| **Redis** | 7.2 | Cache and session management |
-| **Composer** | 2.8.12 | PHP dependency manager |
-| **Symfony CLI** | 5.15.1 | Symfony tools (dev build only) |
+| Component | Version | Purpose | Configuration |
+|-----------|---------|---------|--------------|
+| **PHP-FPM** | 8.4.13 | PHP processing | Optimized pool, OPcache JIT |
+| **Nginx** | 1.27.3 | Web server | FastCGI cache, rate limiting |
+| **Redis** | 7.2.11 | Cache & sessions | AOF persistence, LRU eviction |
+| **Composer** | 2.8.12 | Dependency manager | Included in production |
+| **Symfony CLI** | 5.15.1 | Symfony tools | Dev image only |
+| **Xdebug** | 3.4.6 | PHP debugger | Dev image only (optional) |
---
## 📌 PHP Extensions
-### Core Extensions (Installed)
+### Core Extensions (Pre-installed)
```
pdo, pdo_mysql, opcache, intl, zip, bcmath, gd, mbstring, xml, sockets
```
-### PECL Extensions (Installed)
+### PECL Extensions (Pre-installed)
```
-redis, apcu, uuid
+redis (6.1.0), apcu (5.1.24), uuid (1.2.1), imagick (3.7.0), amqp (2.1.2)
```
-### Built-in Extensions (Auto-available)
+### Built-in Extensions (Always Available)
```
-json, curl, fileinfo, ctype, iconv, session, tokenizer, filter
+json, curl, fileinfo, ctype, iconv, session, tokenizer, filter, hash, openssl
```
-**Add more?** Edit `.env` and rebuild:
+### Adding Custom Extensions
+
+Edit `.env` before building:
```bash
-PHP_CORE_EXTENSIONS="... newext"
+# Add your extension to the list
+PHP_CORE_EXTENSIONS="pdo pdo_mysql opcache intl zip bcmath gd mbstring xml sockets mysqli"
+PHP_PECL_EXTENSIONS="redis apcu uuid xdebug"
+
+# Rebuild
make build
```
+**Note**: For production images, extensions are optimized and loaded automatically.
+
---
## ⚙️ Configuration
-All configurations via `.env` file:
+All configuration is managed via `.env` file with extensive customization options:
-```bash
-# PHP Performance
-PHP_MEMORY_LIMIT=256M
-PHP_OPCACHE_MEMORY=256
-PHP_OPCACHE_JIT=tracing
-PHP_FPM_PM_MAX_CHILDREN=60
+### Essential Variables
+```bash
# Environment
-APP_ENV=production
-APP_DEBUG=false
-PHP_DISPLAY_ERRORS=Off
+APP_ENV=production # production|development|test
+APP_DEBUG=false # Enable debug mode (dev only)
+
+# PHP Performance
+PHP_MEMORY_LIMIT=256M # Memory limit per request
+PHP_MAX_EXECUTION_TIME=60 # Script timeout
+PHP_OPCACHE_MEMORY=256 # OPcache memory (MB)
+PHP_OPCACHE_JIT=tracing # JIT mode: off|tracing|function
+PHP_OPCACHE_ENABLE=1 # Enable OPcache (always 1 in prod)
+
+# PHP-FPM Pool
+PHP_FPM_PM=dynamic # Process manager mode
+PHP_FPM_PM_MAX_CHILDREN=60 # Max child processes
+PHP_FPM_PM_START_SERVERS=10 # Initial servers
+PHP_FPM_PM_MIN_SPARE_SERVERS=5 # Min idle servers
+PHP_FPM_PM_MAX_SPARE_SERVERS=20 # Max idle servers
+
+# Redis
+REDIS_HOST=127.0.0.1 # Redis host (standalone mode)
+REDIS_PASSWORD=your-secure-password # Redis authentication
+REDIS_DATABASES=16 # Number of databases
+REDIS_MAXMEMORY=256mb # Max memory usage
+REDIS_MAXMEMORY_POLICY=allkeys-lru # Eviction policy
+
+# Nginx
+NGINX_WORKER_PROCESSES=auto # Worker processes (auto = CPU cores)
+NGINX_WORKER_CONNECTIONS=2048 # Connections per worker
+NGINX_KEEPALIVE_TIMEOUT=65 # Keep-alive timeout
+NGINX_CLIENT_MAX_BODY_SIZE=20M # Max upload size
+```
+
+### Development Mode
-# Extensions
-PHP_CORE_EXTENSIONS="pdo pdo_mysql opcache..."
-PHP_PECL_EXTENSIONS="redis apcu uuid"
+```bash
+APP_ENV=development
+APP_DEBUG=true
+PHP_DISPLAY_ERRORS=On
+PHP_OPCACHE_VALIDATE_TIMESTAMPS=1 # Reload PHP files without restart
+XDEBUG_ENABLE=1 # Enable Xdebug
```
-**Full reference**: See `.env.example`
+**Full reference**: Copy `.env.example` and customize for your needs.
---
## 🧰 Makefile Commands
-The project includes a comprehensive Makefile with **logically organized targets by similarity**. Run `make help` for the complete list.
+The project includes **three specialized Makefiles** for different purposes:
+
+### 📁 Makefile Organization
+
+| Makefile | Purpose | Commands |
+|----------|---------|----------|
+| **Makefile** | Build, test, run containers | 40+ commands |
+| **Makefile.dockerhub** | Docker Hub operations | Version management, tagging, push, release |
+| **Makefile.compose** | Docker Compose orchestration | Multi-service management |
+
+Run `make help` for the main menu, or specialized help:
+- `make hub-help` - Docker Hub commands
+- `make compose-help` - Docker Compose commands
+
+---
### 🏗️ Build Targets
```bash
-make build # Build production image
-make build-dev # Build dev image (with Symfony CLI, optional Xdebug)
-make build-no-cache # Build without cache
-make build-test-image # Build test image with comprehensive health check
+make build # Build production image (optimized)
+make build-dev # Build dev image (Symfony CLI + optional Xdebug)
+make build-base # Build base layer only (for debugging)
+make build-test # Build test image with comprehensive health check
+make build-all # Build both production and dev images
make lint # Lint Dockerfile with hadolint
-make scan # Scan for vulnerabilities with Trivy
+make scan # Security scan with Trivy
+make scan-dev # Scan dev image for vulnerabilities
```
### 🚀 Runtime - Run Containers
```bash
-make run # Run local container (port 8080)
-make run-with-app # Run with mounted application
-make run-dev # Run development container (port 8001, Xdebug ready)
-make run-test # Run test container with comprehensive health checks
+make run # Run production container (port 8080)
+make run-dev # Run dev container (port 8001, Xdebug on 9003)
+make run-test # Run test container with health monitoring
```
### ⏹️ Runtime - Stop & Restart
```bash
-make stop # Stop and remove local container
-make stop-dev # Stop and remove dev container
-make stop-test # Stop and remove test container
-make restart # Restart local container
-make restart-dev # Restart development container
-make restart-test # Restart test container
+make stop # Stop production container
+make stop-dev # Stop dev container
+make stop-test # Stop test container
+make restart # Restart production container
+make restart-dev # Restart dev container
```
### 📋 Runtime - Logs & Shell
```bash
-make logs # View local container logs (follow)
-make logs-dev # View development container logs
-make logs-test # View test container logs
-make shell # Access local container shell (alias: exec)
-make shell-dev # Access development container shell
-make shell-test # Access test container shell
-make stats # Show container resource usage
+make logs # Follow production logs
+make logs-dev # Follow dev logs
+make shell # Open shell in production container
+make shell-dev # Open shell in dev container
```
### 🧪 Test Targets
```bash
-make test # Run full test suite (versions, config, health)
+make test # Run comprehensive test suite
make test-quick # Quick component version checks
-make test-structure # Test container structure and directories
-make test-health # Test comprehensive health check endpoint
-make test-health-status # Show health check status summary
-make test-health-watch # Live health monitoring (updates every 5s)
+make test-structure # Validate container structure
+make test-health # Test health check endpoint
```
-### 📤 Push & Publish Targets
+---
+
+### 🐳 Docker Hub Commands
+
+These commands are in **Makefile.dockerhub**. Run `make hub-help` for complete list.
+
+#### Version Management
```bash
-make push # Push all tags to Docker Hub
-make push-dev # Push dev image with custom tag (IMAGE_TAG=xxx)
-make publish-dev # Build and push dev image (build-dev + push-dev)
+make version # Show current version
+make bump-patch # Bump patch (1.5.0 → 1.5.1)
+make bump-minor # Bump minor (1.5.0 → 1.6.0)
+make bump-major # Bump major (1.5.0 → 2.0.0)
```
-### 🏷️ Release & Versioning
+#### Tagging & Push
```bash
-make tag-latest # Tag current version as latest/major/minor
-make release # Full release pipeline (lint+build+test+scan+push)
-make version # Display current version
-make bump-patch # Bump patch version (x.x.X)
-make bump-minor # Bump minor version (x.X.x)
-make bump-major # Bump major version (X.x.x)
+make tag-production # Tag production image (latest, 1, 1.5, 1.5.0)
+make tag-dev # Tag dev image (dev only)
+make push-production # Push all production tags
+make push-dev # Push dev tag
+make push-all # Push all images
```
-### 🧹 Cleanup Targets
+#### Complete Workflows
```bash
-make clean # Remove local images and containers
-make clean-all # Deep clean (volumes + build cache)
+make publish-production # Build + Tag + Push production
+make publish-dev # Build + Tag + Push dev
+make release-production # Full release: pre-check + build + test + scan + push
+make release-all # Release both production and dev
```
-### ℹ️ Information Targets
+#### Hub Utilities
+
+```bash
+make hub-check # Check if images exist on Docker Hub
+make hub-tags # List all published tags
+make hub-info # Show repository information
+make hub-clean # Remove old local tags
+make pre-release-check # Validate before release (git, docker, auth)
+```
+**Example Release Workflow:**
```bash
-make info # Show image information (tags, sizes)
+make bump-minor # 1.5.0 → 1.6.0
+make release-production # Full automated release
```
-### 🐳 Docker Compose Targets
+---
+
+### 🎭 Docker Compose Commands
+
+These commands are in **Makefile.compose**. Run `make compose-help` for complete list.
+
+#### Lifecycle
-For complete infrastructure setup with databases, load balancers, and monitoring:
+```bash
+make compose-up # Start services (respects PROFILES)
+make compose-up-all # Start with all profiles
+make compose-down-v # Stop and remove volumes
+make compose-down-all # Stop everything + all profiles
+make compose-restart # Restart services
+make compose-ps # Show service status
+```
+
+#### Logs & Shell
```bash
-make compose-help # Show Docker Compose help
-make compose-up # Start services (respects PROFILES)
-make compose-up-all # Start with all profiles (loadbalancer,monitoring)
-make compose-down-v # Stop and remove volumes
-make compose-down-all # Stop everything including all profiles
-make compose-logs # Tail logs for active services
-make compose-logs-all # Tail logs for all services
-make compose-ps # Show container status
-make compose-shell # Access service shell (default: php-api-stack)
+make compose-logs # Tail logs (all active services)
+make compose-logs-all # Tail logs (base + all profiles)
+make compose-logs-svc # Tail logs for specific services
+make compose-shell # Open shell in service
+make compose-exec # Execute command in service
```
-**Profile Examples:**
+#### Utilities
+
+```bash
+make compose-config # Show resolved config
+make compose-health # Test health endpoint
+make compose-open # Open app/Prometheus/Grafana in browser
+```
+
+**Example with Profiles:**
```bash
# Start with database
make compose-up PROFILES="db"
@@ -274,142 +390,163 @@ make compose-up PROFILES="db"
# Start with monitoring
make compose-up PROFILES="monitoring"
-# Start with multiple profiles
-make compose-up PROFILES="db,monitoring"
-
# Start specific services
make compose-up-svc SERVICES="php-api-stack mysql"
-# View logs for specific service
+# View specific logs
make compose-logs-svc SERVICES="php-api-stack"
```
+---
+
### Quick Workflow Examples
**Development Workflow:**
```bash
make build-dev # Build dev image
-make run-dev # Start dev container (with Xdebug)
-make logs-dev # View dev logs
-make shell-dev # Access dev container
-make restart-dev # Restart dev container
-make stop-dev # Stop dev container
+make run-dev # Start dev (port 8001, Xdebug ready)
+make logs-dev # View logs
+make shell-dev # Access container
+curl http://localhost:8001/health
```
**Testing Workflow:**
```bash
-make build-test-image # Build test image
+make build-test # Build with comprehensive health check
make run-test # Start test container
-make test-health-watch # Monitor health in real-time
-make logs-test # View test logs
-make stop-test # Stop test container
+make test-health # Test health endpoint
+make logs-test # View logs
```
**Release Workflow:**
```bash
make lint # Lint Dockerfile
-make build # Build production image
+make build-all # Build production + dev
make test # Run tests
make scan # Security scan
make bump-patch # Bump version
-make release # Full release pipeline
+make release-all # Full release pipeline
+make hub-check # Verify on Docker Hub
+```
+
+**Docker Compose Workflow:**
+```bash
+make compose-up-all # Start all services
+make compose-ps # Check status
+make compose-logs # View logs
+make compose-open # Open in browser
+make compose-down-all # Stop everything
```
---
## 🐳 Docker Compose
-The project includes a complete `docker-compose.example.yml` with multiple service profiles:
+Complete orchestration with databases, load balancing, and monitoring.
### Available Profiles
-- **Base** (always active): `php-api-stack` - The main application container
-- **db**: MySQL database with optimized configuration
-- **loadbalancer**: Nginx load balancer for scaling
-- **monitoring**: Prometheus + Grafana + cAdvisor stack
+| Profile | Services | Purpose |
+|---------|----------|---------|
+| **Base** | php-api-stack | Main application (always active) |
+| **db** | mysql, redis-external | Databases with persistence |
+| **loadbalancer** | nginx-lb | Nginx load balancer for scaling |
+| **monitoring** | prometheus, grafana, cadvisor | Full monitoring stack |
### Quick Start
```bash
-# 1. Setup environment
+# 1. Setup
cp .env.example .env
cp docker-compose.example.yml docker-compose.yml
-# 2. Start base services
+# 2. Start base
make compose-up
-# 3. Start with database
-make compose-up PROFILES="db"
+# 3. Start with profiles
+make compose-up PROFILES="db,monitoring"
-# 4. Start with monitoring
-make compose-up PROFILES="monitoring"
+# 4. Or start everything
+make compose-up-all
-# 5. Start everything
-make compose-up-all # Equivalent to PROFILES="loadbalancer,monitoring"
+# 5. Access services
+make compose-open # Opens app, Prometheus, Grafana
+```
-# 6. View services
-make compose-ps
+### Service URLs
-# 7. View logs
-make compose-logs
+| Service | URL | Credentials |
+|---------|-----|-------------|
+| **Application** | http://localhost:8089 | - |
+| **Health Check** | http://localhost:8089/health | - |
+| **Prometheus** | http://localhost:9091 | - |
+| **Grafana** | http://localhost:3000 | admin / HmlGrafana_7uV4mRp |
+| **MySQL** | localhost:3307 | root / HmlMysql_9tQ2wRx |
-# 8. Access application
-open http://localhost:8089
+### Management
-# 9. Access monitoring (if enabled)
-open http://localhost:3000 # Grafana (admin/HmlGrafana_7uV4mRp)
-open http://localhost:9091 # Prometheus
-```
+```bash
+# View status
+make compose-ps
-### Service URLs
+# View logs
+make compose-logs
-When all profiles are active:
+# Restart services
+make compose-restart
-| Service | URL | Description |
-|---------|-----|-------------|
-| Application | http://localhost:8089 | Main PHP application |
-| Health Check | http://localhost:8089/health.php | Comprehensive health endpoint |
-| Prometheus | http://localhost:9091 | Metrics collection |
-| Grafana | http://localhost:3000 | Monitoring dashboards |
-| MySQL | localhost:3307 | Database (if db profile enabled) |
+# Scale application
+docker compose up -d --scale php-api-stack=3
-**📖 Complete guide with examples**: [DOCKER_COMPOSE_GUIDE.md](DOCKER_COMPOSE_GUIDE.md)
+# Stop everything
+make compose-down-all
+```
+
+**📖 Complete guide**: [DOCKER_COMPOSE_GUIDE.md](DOCKER_COMPOSE_GUIDE.md)
---
## 🛠️ Development Workflow
-### For Maintainers (GitHub)
+### For Maintainers
```bash
-# Clone and build
+# 1. Clone and setup
git clone https://github.com/kariricode/php-api-stack.git
cd php-api-stack
-make build
+cp .env.example .env
+
+# 2. Build
+make build-all
-# Run tests
+# 3. Test
make test
+make scan
-# Run with comprehensive health check
-make run-test
-make test-health
+# 4. Run dev
+make run-dev
+curl http://localhost:8001/health
-# Release
+# 5. Release
make bump-patch
-make release
+make release-production
```
**Complete guide**: [TESTING.md](TESTING.md)
-### For Publishers (Docker Hub)
+### For Publishers
```bash
-# Build and push
-make build
-make push
+# Quick release
+make release-production
-# Or full release
-make release # lint + build + test + scan + push
+# Or step by step
+make lint
+make build
+make test
+make scan
+make bump-patch
+make push-production
```
**Complete guide**: [DOCKER_HUB.md](DOCKER_HUB.md)
@@ -417,12 +554,9 @@ make release # lint + build + test + scan + push
### For End Users
```bash
-# Pull image
+# Pull and run
docker pull kariricode/php-api-stack:latest
-
-# Run with your app
-docker run -d \
- -p 8080:80 \
+docker run -d -p 8080:80 \
-v $(pwd)/app:/var/www/html \
kariricode/php-api-stack:latest
```
@@ -433,157 +567,438 @@ docker run -d \
## 🏥 Health Check System
-The stack includes **two health check implementations**:
+Two health check implementations for different needs:
+
+### 1. Simple Health Check (Production)
+
+Lightweight endpoint for load balancers and orchestrators:
-### Simple Health Check (Production)
```bash
curl http://localhost:8080/health
-# {"status":"healthy","timestamp":"2025-10-17T14:30:00+00:00"}
```
-### Comprehensive Health Check (Testing/Monitoring)
+**Response:**
+```json
+{
+ "status": "healthy",
+ "timestamp": "2025-10-24T22:00:00+00:00"
+}
+```
+
+### 2. Comprehensive Health Check (Monitoring)
+
+Detailed system diagnostics with component-level checks:
+
```bash
curl http://localhost:8080/health.php | jq
```
**Features:**
-- ✅ PHP Runtime validation (version, memory, SAPI)
-- ✅ PHP Extensions check (required + optional)
-- ✅ OPcache performance (memory, hit rate, JIT status)
-- ✅ Redis connectivity (latency, stats, memory)
-- ✅ System resources (disk, CPU load, memory)
-- ✅ Application directories (permissions, accessibility)
-
-**Architecture**: SOLID principles, Design Patterns (Strategy, Template Method, Facade)
-
-**Build with comprehensive health**:
+- ✅ **PHP Runtime**: Version, memory, SAPI, configuration
+- ✅ **PHP Extensions**: Required and optional extensions validation
+- ✅ **OPcache**: Hit rate, memory usage, JIT status, cached scripts
+- ✅ **Redis**: Connectivity, latency, stats, memory usage, persistence
+- ✅ **System Resources**: Disk space, CPU load, memory usage
+- ✅ **Application**: Directory permissions, accessibility checks
+
+**Response Structure:**
+```json
+{
+ "status": "healthy|degraded|unhealthy",
+ "timestamp": "2025-10-24T22:00:00+00:00",
+ "overall": "✓ All Systems Operational",
+ "components": {
+ "php": { "status": "healthy", "details": {...} },
+ "opcache": { "status": "healthy", "details": {...} },
+ "redis": { "status": "healthy", "details": {...} },
+ "system": { "status": "healthy", "details": {...} },
+ "application": { "status": "healthy", "details": {...} }
+ },
+ "stack_info": {
+ "docker_image": "kariricode/php-api-stack",
+ "version": "1.5.0",
+ "php_version": "8.4.13"
+ }
+}
+```
+
+### Using Health Checks
+
+**With Docker:**
+```dockerfile
+HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
+ CMD curl -f http://localhost/health || exit 1
+```
+
+**With Docker Compose:**
+```yaml
+services:
+ app:
+ image: kariricode/php-api-stack:latest
+ healthcheck:
+ test: ["CMD", "curl", "-f", "http://localhost/health"]
+ interval: 30s
+ timeout: 3s
+ retries: 3
+```
+
+**With Kubernetes:**
+```yaml
+livenessProbe:
+ httpGet:
+ path: /health
+ port: 80
+ initialDelaySeconds: 5
+ periodSeconds: 30
+
+readinessProbe:
+ httpGet:
+ path: /health.php
+ port: 80
+ initialDelaySeconds: 10
+ periodSeconds: 10
+```
+
+**Makefile Commands:**
```bash
-make build-test-image
-make run-test
-make test-health
+make test-health # Test comprehensive health check
+make test-health-status # Show health summary
+make test-health-watch # Live monitoring (updates every 5s)
```
+**Architecture**: Built with SOLID principles using Strategy, Template Method, and Facade patterns.
+
---
## 🔐 Security Features
-- ✅ Security headers (X-Frame-Options, CSP, HSTS)
-- ✅ Rate limiting (general: 10r/s, API: 100r/s)
-- ✅ Disabled dangerous PHP functions
-- ✅ Open basedir restrictions
-- ✅ Hidden server tokens
+Production-hardened security configuration:
+
+### Nginx Security
+
+- ✅ **Security Headers**: X-Frame-Options, X-Content-Type-Options, CSP, HSTS
+- ✅ **Rate Limiting**:
+ - General: 10 req/s per IP
+ - API endpoints: 100 req/s per IP
+- ✅ **Hidden Tokens**: Server version and tokens hidden
+- ✅ **Request Filtering**: Protection against common attacks
+
+### PHP Security
+
+- ✅ **Disabled Functions**: `exec`, `shell_exec`, `system`, `passthru`, etc.
+- ✅ **Open Basedir**: Restricted to `/var/www/html` and `/tmp`
+- ✅ **Expose PHP**: Off (version hidden)
+- ✅ **File Uploads**: Configurable with size limits
+- ✅ **Session Security**: Secure cookies, HTTP-only
+
+### Redis Security
+
+- ✅ **Authentication**: Password-protected (configurable)
+- ✅ **Bind Address**: Internal only (127.0.0.1 or network)
+- ✅ **Command Renaming**: Dangerous commands can be disabled
+- ✅ **Persistence**: AOF with fsync control
+
+### Container Security
+
+- ✅ **Non-root User**: Application runs as `www-data`
+- ✅ **Read-only Filesystem**: Where possible
+- ✅ **Resource Limits**: CPU and memory constraints
+- ✅ **Security Scanning**: Automated Trivy scans
+
+**Scan for vulnerabilities:**
+```bash
+make scan # Scan production image
+make scan-dev # Scan dev image
+```
---
## 🛠 Troubleshooting
-### Container won't start
+### Container Won't Start
+
```bash
+# Check logs
docker logs
+
+# Test structure
make test-structure
+
+# Check entrypoint
+docker run --rm kariricode/php-api-stack:latest cat /entrypoint.sh
```
### 502 Bad Gateway
+
```bash
# Check PHP-FPM socket
docker exec ls -la /var/run/php/php-fpm.sock
+# Check PHP-FPM status
+docker exec php-fpm -t
+
# Check logs
make logs
```
+### Redis Connection Issues
+
+**Standalone Mode:**
+```bash
+# Should use 127.0.0.1
+docker exec env | grep REDIS_HOST
+# REDIS_HOST=127.0.0.1
+
+# Test connection
+docker exec redis-cli -h 127.0.0.1 -a "$REDIS_PASSWORD" ping
+```
+
+**Docker Compose Mode:**
+```bash
+# Should use 'redis' (service name)
+docker compose exec php-api-stack env | grep REDIS_HOST
+# REDIS_HOST=redis
+
+# Test connection
+docker compose exec php-api-stack redis-cli -h redis -a "$REDIS_PASSWORD" ping
+```
+
### Poor Performance
+
```bash
+# Check container resources
docker stats
+
+# Check OPcache status
docker exec php -r "print_r(opcache_get_status());"
+
+# Check PHP-FPM pool
+docker exec cat /var/run/php/php-fpm.pid
+docker exec kill -USR2 $(cat /var/run/php/php-fpm.pid)
```
-**Full troubleshooting**: [IMAGE_USAGE_GUIDE.md](IMAGE_USAGE_GUIDE.md)
+### Permission Issues
+
+```bash
+# Check ownership
+docker exec ls -la /var/www/html
+
+# Fix permissions
+docker exec chown -R www-data:www-data /var/www/html
+```
+
+**Full troubleshooting guide**: [IMAGE_USAGE_GUIDE.md](IMAGE_USAGE_GUIDE.md#troubleshooting)
---
-## 📖 Documentation Reference
+## 📖 External References
- [PHP 8.4 Documentation](https://www.php.net/docs.php)
- [Nginx Documentation](https://nginx.org/en/docs/)
- [Redis Documentation](https://redis.io/documentation)
+- [Docker Best Practices](https://docs.docker.com/develop/dev-best-practices/)
- [Symfony Best Practices](https://symfony.com/doc/current/best_practices.html)
-- [Docker Compose](https://docs.docker.com/compose/)
+- [Laravel Deployment](https://laravel.com/docs/deployment)
- [Twelve-Factor App](https://12factor.net/)
---
## 🤝 Contributing
-Contributions are welcome! Please:
+Contributions are welcome! Please follow these guidelines:
+
+### Getting Started
1. Fork the repository
-2. Create feature branch (`git checkout -b feature/amazing`)
-3. Commit changes (`git commit -m 'Add amazing feature'`)
-4. Push to branch (`git push origin feature/amazing`)
-5. Open Pull Request
+2. Create a feature branch: `git checkout -b feature/amazing-feature`
+3. Make your changes
+4. Run tests: `make test`
+5. Lint: `make lint`
+6. Commit: `git commit -m 'feat: add amazing feature'`
+7. Push: `git push origin feature/amazing-feature`
+8. Open a Pull Request
+
+### Standards
+
+- **Code Style**: Follow [PSR-12](https://www.php-fig.org/psr/psr-12/) for PHP code
+- **Commit Messages**: Use [Conventional Commits](https://www.conventionalcommits.org/)
+ - `feat:` New features
+ - `fix:` Bug fixes
+ - `docs:` Documentation changes
+ - `refactor:` Code refactoring
+ - `test:` Test additions/changes
+ - `chore:` Build/tooling changes
+- **Testing**: Add tests for new features
+- **Documentation**: Update relevant documentation
+
+### Before Submitting
-**Standards:**
-- Follow [PSR-12](https://www.php-fig.org/psr/psr-12/) for PHP
-- Use [Conventional Commits](https://www.conventionalcommits.org/)
-- Add tests for new features
-- Update documentation
+```bash
+make lint # Lint Dockerfile
+make build-all # Build all images
+make test # Run tests
+make scan # Security scan
+```
---
## 📄 License
-This project is licensed under the MIT License. See [LICENSE](LICENSE) file.
+This project is licensed under the MIT License. See the [LICENSE](LICENSE) file for details.
+
+**TL;DR**: You can use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the software.
---
-## 🌟 Support
+## 🌟 Support & Community
+
+### Get Help
- **Issues**: [GitHub Issues](https://github.com/kariricode/php-api-stack/issues)
- **Discussions**: [GitHub Discussions](https://github.com/kariricode/php-api-stack/discussions)
- **Docker Hub**: [kariricode/php-api-stack](https://hub.docker.com/r/kariricode/php-api-stack)
+### Report Bugs
+
+Found a bug? Please open an issue with:
+- Docker image version
+- Steps to reproduce
+- Expected vs actual behavior
+- Relevant logs
+
+### Request Features
+
+Have an idea? Open a discussion or issue with:
+- Use case description
+- Proposed solution
+- Any alternative solutions considered
+
---
-## 🧭 Roadmap & Contributing
+## 🧭 Roadmap
-Feature requests and PRs are welcome in the source repository:
+### Current Focus (v1.5.x)
-* GitHub: [https://github.com/kariricode/php-api-stack](https://github.com/kariricode/php-api-stack)
+- ✅ Comprehensive Makefile system with 50+ commands
+- ✅ Docker Hub automation and versioning
+- ✅ Docker Compose orchestration with profiles
+- ✅ Advanced health check system
+- 🔄 Performance benchmarking suite
+- 🔄 Automated CI/CD workflows
-For broader ecosystem projects, visit:
+### Future Plans (v1.6+)
-* KaririCode Framework: [https://github.com/KaririCode-Framework](https://github.com/KaririCode-Framework)
+- 📋 Multi-stage build optimization
+- 📋 Additional PECL extensions (gRPC, protobuf)
+- 📋 ARM64 native builds
+- 📋 Kubernetes manifests and Helm charts
+- 📋 Additional monitoring integrations (Datadog, New Relic)
+- 📋 Development containers (devcontainer.json)
+
+### Long-term Vision (v2.0+)
+
+- 📋 PHP 8.5 support
+- 📋 Alternative web servers (Caddy, FrankenPHP)
+- 📋 WebAssembly integration
+- 📋 Enhanced security profiles
+
+---
+
+## 📚 Related Projects
+
+This image is part of the **KaririCode** ecosystem:
+
+### KaririCode Framework
+
+Modern PHP framework with advanced features:
+
+- **Repository**: [KaririCode-Framework](https://github.com/KaririCode-Framework)
+- **Features**: ARFA architecture, DI container, Router, Auth, etc.
+- **Components**: 30+ independent packages
+- **Documentation**: Comprehensive guides and examples
+
+### KaririCode DevKit
+
+Development environment automation:
+
+- **Repository**: [kariricode/devkit](https://github.com/kariricode/devkit)
+- **Features**: Docker + Compose, quality tools, CI/CD
+- **Setup Time**: 2-3 minutes
+- **Integration**: Uses this Docker image
---
## 📝 Changelog
-**1.4.3** (Latest)
+### **v1.5.0** (2025-10-24)
+
+**Docker Hub Integration:**
+- ✨ Fixed `hub-check` command display bug (showing 'ag' instead of tag names)
+- ✨ Simplified dev tagging strategy: only `dev` tag (removed `dev-X.Y.Z`)
+- ✨ Fixed `bump-patch/bump-minor/bump-major` dollar sign escaping
+- ✨ Improved `hub-check` output with checkmark indicators (✓/✗)
+- ✨ Added comprehensive Docker Hub utilities (`hub-tags`, `hub-info`, `hub-clean`)
+
+**Breaking Changes:**
+- ⚠️ Dev versioned tags (`dev-X.Y.Z`) are no longer created or pushed to Docker Hub
+
+### **v1.4.5** (2025-10-24)
-* ✨ **Makefile refactored** with semantic grouping by similarity (Build, Push, Runtime, Test, Validation, Release, etc.)
-* 🎯 **Enhanced development workflow** with dedicated dev container targets (`run-dev`, `stop-dev`, `restart-dev`, `logs-dev`, `shell-dev`)
-* 🧪 **Improved test targets** with health monitoring (`test-health-status`, `test-health-watch`)
-* 📊 **Better organization** of 50+ Make targets into logical categories
-* 📚 **Updated documentation** reflecting new command structure
-* 🔧 **Zero breaking changes** - all existing targets work identically
+**Build System:**
+- ✨ Fixed PHP extension quoting in Makefile (`PHP_CORE_EXTENSIONS`, `PHP_PECL_EXTENSIONS`)
+- ✨ Secure `.env` parsing in `build-from-env.sh` to prevent command execution
+- ✨ Proper escaping for build args with spaces
-**1.2.1**
+**Redis Integration:**
+- ✨ Automatic `REDIS_HOST` override to `127.0.0.1` for standalone containers
+- ✨ Smart DNS fallback in health.php for docker-compose vs standalone modes
+- ✨ Documentation improvements for `REDIS_HOST` behavior
-* Added comprehensive Makefile with Docker Compose integration
-* Added Docker Compose example with multiple profiles (db, loadbalancer, monitoring)
-* Improved documentation structure with dedicated guides
-* Enhanced health check system with monitoring capabilities
+**Dockerfile Fixes:**
+- 🐛 Fixed OPcache validation check (Zend extension vs regular extension)
+- 🐛 Added `util-linux` runtime dependency for UUID extension
+- 🐛 Fixed SC1075 shellcheck error (`else if` → `elif`)
+- ✨ Improved extension loading verification
-**1.2.0**
+### **v1.4.3**
-* PHP 8.4, Nginx 1.27.3, Redis 7.2
-* Socket-based PHP-FPM; OPcache + JIT optimized
-* `/health.php` endpoint; improved entrypoint & config processor
-* Extensive env-var configuration for Nginx/PHP/Redis
+**Makefile Refactoring:**
+- ✨ Semantic grouping by similarity (Build, Push, Runtime, Test, Validation, Release)
+- ✨ Enhanced development workflow with dedicated dev targets
+- ✨ Improved test targets with health monitoring
+- ✨ Better organization of 50+ Make targets
+- 📚 Updated documentation
-> Full release notes are available in the GitHub repository.
+### **v1.2.1**
+
+- ✨ Added comprehensive Makefile with Docker Compose integration
+- ✨ Added Docker Compose example with multiple profiles
+- ✨ Improved documentation structure
+- ✨ Enhanced health check system
+
+### **v1.2.0**
+
+- ✨ PHP 8.4, Nginx 1.27.3, Redis 7.2
+- ✨ Socket-based PHP-FPM communication
+- ✨ OPcache + JIT optimization
+- ✨ `/health.php` comprehensive endpoint
+- ✨ Improved entrypoint and config processor
+- ✨ Extensive environment variable configuration
+
+### **v1.0.0**
+
+- 🎉 Initial release
+- ✨ PHP 8.3, Nginx 1.25, Redis 7.0
+- ✨ Basic production configuration
+- ✨ Docker and Docker Compose support
---
-**Made with 💚 by KaririCode** – [https://kariricode.org/](https://kariricode.org/)
\ No newline at end of file
+
+
+**Made with 💚 by [KaririCode](https://kariricode.org)**
+
+[](https://kariricode.org)
+[](https://github.com/KaririCode-Framework)
+
+
\ No newline at end of file
diff --git a/VERSION b/VERSION
index e516bb9..bc80560 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-1.4.5
+1.5.0
diff --git a/app/public/health.php b/app/public/health.php
deleted file mode 100644
index 627a138..0000000
--- a/app/public/health.php
+++ /dev/null
@@ -1,3 +0,0 @@
-"healthy","timestamp"=>date("c")]);
diff --git a/app/public/index.php b/app/public/index.php
deleted file mode 100644
index 3a499df..0000000
--- a/app/public/index.php
+++ /dev/null
@@ -1,602 +0,0 @@
-performCheck();
- } catch (\Throwable $e) {
- return new StatusResult(
- healthy: false,
- message: 'Error: ' . $e->getMessage(),
- details: []
- );
- }
- }
-}
-
-/**
- * PHP Runtime Check
- */
-final class PhpCheck extends AbstractComponentCheck
-{
- public function getName(): string
- {
- return 'PHP';
- }
-
- protected function performCheck(): StatusResult
- {
- $version = PHP_VERSION;
- $extensions = get_loaded_extensions();
-
- return new StatusResult(
- healthy: true,
- message: "PHP {$version} with " . count($extensions) . " extensions",
- details: [
- 'version' => $version,
- 'sapi' => PHP_SAPI,
- 'extensions_count' => count($extensions),
- 'memory_limit' => ini_get('memory_limit'),
- 'max_execution_time' => ini_get('max_execution_time') . 's',
- ]
- );
- }
-}
-
-/**
- * OPcache Check
- */
-final class OpcacheCheck extends AbstractComponentCheck
-{
- public function getName(): string
- {
- return 'OPcache';
- }
-
- protected function performCheck(): StatusResult
- {
- if (!function_exists('opcache_get_status')) {
- return new StatusResult(
- healthy: false,
- message: 'OPcache extension not available',
- details: []
- );
- }
-
- $status = @opcache_get_status(false);
-
- if ($status === false) {
- return new StatusResult(
- healthy: false,
- message: 'OPcache is disabled',
- details: []
- );
- }
-
- $memoryUsed = $status['memory_usage']['used_memory'] ?? 0;
- $memoryFree = $status['memory_usage']['free_memory'] ?? 0;
- $memoryTotal = $memoryUsed + $memoryFree;
- $usagePercent = $memoryTotal > 0 ? round(($memoryUsed / $memoryTotal) * 100, 1) : 0;
-
- return new StatusResult(
- healthy: true,
- message: "OPcache enabled ({$usagePercent}% memory used)",
- details: [
- 'memory_used' => $this->formatBytes($memoryUsed),
- 'cached_scripts' => $status['opcache_statistics']['num_cached_scripts'] ?? 0,
- 'jit_enabled' => ($status['jit']['enabled'] ?? false) ? 'Yes' : 'No',
- ]
- );
- }
-
- private function formatBytes(int $bytes): string
- {
- $units = ['B', 'KB', 'MB', 'GB'];
- $bytes = max($bytes, 0);
- $pow = floor(($bytes ? log($bytes) : 0) / log(1024));
- $pow = min($pow, count($units) - 1);
- $bytes /= (1 << (10 * $pow));
- return round($bytes, 2) . ' ' . $units[$pow];
- }
-}
-
-/**
- * Redis Connectivity Check
- */
-final class RedisCheck extends AbstractComponentCheck
-{
- public function getName(): string
- {
- return 'Redis';
- }
-
- protected function performCheck(): StatusResult
- {
- if (!extension_loaded('redis')) {
- return new StatusResult(
- healthy: false,
- message: 'Redis extension not installed',
- details: []
- );
- }
-
- $redis = new \Redis();
-
- try {
- $connected = @$redis->connect('127.0.0.1', 6379, 1.0);
-
- if (!$connected) {
- return new StatusResult(
- healthy: false,
- message: 'Cannot connect to Redis server',
- details: []
- );
- }
-
- $pong = $redis->ping();
-
- if ($pong !== true && $pong !== '+PONG') {
- return new StatusResult(
- healthy: false,
- message: 'Redis ping failed',
- details: []
- );
- }
-
- $info = $redis->info();
-
- return new StatusResult(
- healthy: true,
- message: 'Redis ' . ($info['redis_version'] ?? 'unknown') . ' connected',
- details: [
- 'version' => $info['redis_version'] ?? 'unknown',
- 'uptime' => $this->formatUptime((int)($info['uptime_in_seconds'] ?? 0)),
- 'memory' => $info['used_memory_human'] ?? 'unknown',
- ]
- );
- } catch (\Throwable $e) {
- return new StatusResult(
- healthy: false,
- message: 'Redis error: ' . $e->getMessage(),
- details: []
- );
- } finally {
- @$redis->close();
- }
- }
-
- private function formatUptime(int $seconds): string
- {
- if ($seconds < 60) return "{$seconds}s";
- if ($seconds < 3600) return floor($seconds / 60) . "m";
- if ($seconds < 86400) return floor($seconds / 3600) . "h";
- return floor($seconds / 86400) . "d";
- }
-}
-
-/**
- * Status Dashboard (Facade Pattern)
- */
-final class StatusDashboard
-{
- /** @var ComponentCheckInterface[] */
- private array $checks = [];
-
- public function addCheck(ComponentCheckInterface $check): self
- {
- $this->checks[] = $check;
- return $this;
- }
-
- public function runAll(): array
- {
- $results = [];
- $allHealthy = true;
-
- foreach ($this->checks as $check) {
- $result = $check->check();
- $results[$check->getName()] = $result;
-
- if (!$result->healthy) {
- $allHealthy = false;
- }
- }
-
- return [
- 'overall_healthy' => $allHealthy,
- 'checks' => $results,
- ];
- }
-}
-
-// Run status checks
-$dashboard = new StatusDashboard();
-$dashboard
- ->addCheck(new PhpCheck())
- ->addCheck(new OpcacheCheck())
- ->addCheck(new RedisCheck());
-
-$status = $dashboard->runAll();
-
-// Collect system information
-$stackInfo = [
- 'image' => 'kariricode/php-api-stack',
- 'version' => getenv('STACK_VERSION') ?: '1.2.1',
- 'php_version' => PHP_VERSION,
- 'server_software' => $_SERVER['SERVER_SOFTWARE'] ?? 'nginx',
- 'document_root' => $_SERVER['DOCUMENT_ROOT'] ?? '/var/www/html/public',
-];
-
-?>
-
-
-
-
-
-
- PHP API Stack - Ready for Development
-
-
-
-
-
-
-
-
-
-
⚠️ Demo Mode Active
-
- This is a placeholder page shown when no application is mounted.
- Mount your Symfony/Laravel project to = htmlspecialchars($stackInfo['document_root']) ?>
- to replace this demo page with your application.
-
-
-
-
Component Status
-
-
- $result): ?>
-
-
-
- = htmlspecialchars($name) ?>
-
-
- = htmlspecialchars($result->message) ?>
-
- details)): ?>
-
- details as $key => $value): ?>
-
= htmlspecialchars(ucfirst(str_replace('_', ' ', $key))) ?>: = htmlspecialchars((string)$value) ?>
-
-
-
-
-
-
-
-
-
Stack Information
-
-
- Docker Image:
- = htmlspecialchars($stackInfo['image']) ?>
-
-
- Stack Version:
- = htmlspecialchars($stackInfo['version']) ?>
-
-
- PHP Version:
- = htmlspecialchars($stackInfo['php_version']) ?>
-
-
- Web Server:
- = htmlspecialchars($stackInfo['server_software']) ?>
-
-
- Document Root:
- = htmlspecialchars($stackInfo['document_root']) ?>
-
-
- Overall Status:
-
- = $status['overall_healthy'] ? '✓ Healthy' : '✗ Issues Detected' ?>
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/build-from-env.sh b/build-from-env.sh
index cd9414f..16658b7 100755
--- a/build-from-env.sh
+++ b/build-from-env.sh
@@ -1,7 +1,7 @@
#!/bin/bash
# Build script for kariricode/php-api-stack
-# This script reads all configurations from .env file and builds the Docker image
-# Usage: ./build-from-env.sh [--push] [--no-cache]
+# Architecture: Base → Production | Dev
+# Usage: ./build-from-env.sh [OPTIONS]
set -euo pipefail
@@ -19,12 +19,14 @@ CYAN='\033[0;36m'
WHITE='\033[1;37m'
NC='\033[0m' # No Color
-# Parse command line arguments
+# Default values
PUSH_TO_HUB=false
NO_CACHE=false
MULTI_PLATFORM=false
-TEST_BUILD=false
+BUILD_TARGET="production" # default: production
+VERSION_ARG=""
+# Parse command line arguments
for arg in "$@"; do
case $arg in
--push)
@@ -39,8 +41,8 @@ for arg in "$@"; do
MULTI_PLATFORM=true
shift
;;
- --test)
- TEST_BUILD=true
+ --target=*)
+ BUILD_TARGET="${arg#*=}"
shift
;;
--version=*)
@@ -50,13 +52,21 @@ for arg in "$@"; do
--help)
echo "Usage: $0 [OPTIONS]"
echo ""
+ echo "Build Architecture: Base → Production | Dev"
+ echo ""
echo "Options:"
- echo " --push Push image to Docker Hub after build"
- echo " --no-cache Build without using cache"
- echo " --multi-platform Build for multiple platforms (amd64, arm64)"
- echo " --test Build with comprehensive health check" # <-- ADICIONAR
- echo " --version=X.Y.Z Override version instead of using VERSION file"
- echo " --help Show this help message"
+ echo " --target=TARGET Build target stage: base, production, dev (default: production)"
+ echo " --push Push image to Docker Hub after build"
+ echo " --no-cache Build without using cache"
+ echo " --multi-platform Build for multiple platforms (amd64, arm64)"
+ echo " --version=X.Y.Z Override version instead of using VERSION file"
+ echo " --help Show this help message"
+ echo ""
+ echo "Examples:"
+ echo " $0 # Build production image"
+ echo " $0 --target=dev # Build dev image with Xdebug"
+ echo " $0 --target=base # Build base layer only"
+ echo " $0 --target=production --push # Build and push production"
exit 0
;;
*)
@@ -67,7 +77,16 @@ for arg in "$@"; do
esac
done
-
+# Validate build target
+case $BUILD_TARGET in
+ base|production|dev)
+ ;;
+ *)
+ echo -e "${RED}Invalid build target: $BUILD_TARGET${NC}"
+ echo "Valid targets: base, production, dev"
+ exit 1
+ ;;
+esac
# Functions
log_info() {
@@ -108,7 +127,14 @@ fi
# Load .env file
log_step "Loading configuration from .env..."
set -a
-source .env
+while IFS='=' read -r key value; do
+ # Skip comments and empty lines
+ [[ $key =~ ^#.*$ ]] || [[ -z $key ]] && continue
+ # Remove leading/trailing whitespace from value
+ value=$(echo "$value" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')
+ # Export the variable
+ export "$key=$value"
+done < .env
set +a
# Set default values if not defined in .env
@@ -130,12 +156,13 @@ VCS_REF=$(git rev-parse --short HEAD 2>/dev/null || echo "no-git")
# Display build configuration
echo ""
-echo -e "${MAGENTA}╔══════════════════════════════════════════════════════╗${NC}"
-echo -e "${MAGENTA}║ PHP API Stack - Docker Build Configuration ║${NC}"
-echo -e "${MAGENTA}╚══════════════════════════════════════════════════════╝${NC}"
+echo -e "${MAGENTA}╔═══════════════════════════════════════════════════╗${NC}"
+echo -e "${MAGENTA}║ PHP API Stack - Docker Build Configuration ║${NC}"
+echo -e "${MAGENTA}╚═══════════════════════════════════════════════════╝${NC}"
echo ""
+echo -e "${WHITE}Architecture:${NC} Base → Production | Dev"
+echo -e "${WHITE}Build Target:${NC} ${CYAN}${BUILD_TARGET}${NC}"
echo -e "${WHITE}Image:${NC} ${FULL_IMAGE}:${VERSION}"
-echo -e "${WHITE}Environment:${NC} ${APP_ENV}"
echo ""
echo -e "${YELLOW}Stack Versions:${NC}"
echo " • PHP: ${PHP_VERSION}"
@@ -143,17 +170,24 @@ echo " • Nginx: ${NGINX_VERSION}"
echo " • Redis: ${REDIS_VERSION}"
echo " • Alpine: ${ALPINE_VERSION}"
echo " • Composer: ${COMPOSER_VERSION}"
-echo " • Symfony CLI: ${SYMFONY_CLI_VERSION}"
+
+if [ "$BUILD_TARGET" = "dev" ]; then
+ echo " • Symfony CLI: ${SYMFONY_CLI_VERSION}"
+ echo " • Xdebug: ${XDEBUG_VERSION}"
+fi
+
echo ""
echo -e "${YELLOW}PHP Extensions:${NC}"
echo " • Core: ${PHP_CORE_EXTENSIONS}"
echo " • PECL: ${PHP_PECL_EXTENSIONS}"
echo ""
-echo -e "${YELLOW}Build Options:${NC}"
-echo " • No Cache: ${NO_CACHE}"
-echo " • Push to Hub: ${PUSH_TO_HUB}"
-echo " • Multi-Platform: ${MULTI_PLATFORM}"
-echo ""
+echo -e "${YELLOW}PECL Versions:${NC}"
+echo " • Redis: ${PHP_REDIS_VERSION}"
+echo " • APCu: ${PHP_APCU_VERSION}"
+echo " • UUID: ${PHP_UUID_VERSION}"
+echo " • ImageMagick: ${PHP_IMAGICK_VERSION}"
+echo " • AMQP: ${PHP_AMQP_VERSION}"
+echo ""
# Confirm build
read -p "$(echo -e ${GREEN}Proceed with build? [Y/n]: ${NC})" -n 1 -r
@@ -164,7 +198,7 @@ if [[ $REPLY =~ ^[Nn]$ ]]; then
fi
# Prepare build command
-log_step "Preparing build command..."
+log_step "Preparing build command for target: ${BUILD_TARGET}..."
# Base build command
BUILD_CMD="docker"
@@ -173,7 +207,6 @@ BUILD_CMD="docker"
if [ "$MULTI_PLATFORM" = true ]; then
log_info "Setting up Docker buildx for multi-platform build..."
- # Create builder if it doesn't exist
if ! docker buildx ls | grep -q "php-api-stack-builder"; then
docker buildx create --name php-api-stack-builder --use
docker buildx inspect --bootstrap
@@ -195,8 +228,7 @@ if [ "$MULTI_PLATFORM" = true ]; then
BUILD_CMD="$BUILD_CMD $PLATFORM_ARG"
fi
-# Add no-cache if requested
-# Add no-cache if requested or guard cache-from
+# Add no-cache or cache-from
if [ "$NO_CACHE" = true ]; then
BUILD_CMD="$BUILD_CMD --no-cache"
else
@@ -207,72 +239,61 @@ else
fi
fi
-
-# Add build arguments
+# Common build arguments
BUILD_CMD="$BUILD_CMD \
--build-arg PHP_VERSION=${PHP_VERSION} \
--build-arg NGINX_VERSION=${NGINX_VERSION} \
--build-arg REDIS_VERSION=${REDIS_VERSION} \
--build-arg ALPINE_VERSION=${ALPINE_VERSION} \
--build-arg COMPOSER_VERSION=${COMPOSER_VERSION} \
- --build-arg SYMFONY_CLI_VERSION=${SYMFONY_CLI_VERSION} \
- --build-arg PHP_CORE_EXTENSIONS=\"${PHP_CORE_EXTENSIONS}\" \
- --build-arg PHP_PECL_EXTENSIONS=\"${PHP_PECL_EXTENSIONS}\" \
- --build-arg APP_NAME=\"${APP_NAME}\" \
- --build-arg APP_ENV=${APP_ENV} \
- --build-arg APP_PORT=${APP_PORT} \
+ --build-arg PHP_CORE_EXTENSIONS=${PHP_CORE_EXTENSIONS} \
+ --build-arg PHP_PECL_EXTENSIONS=${PHP_PECL_EXTENSIONS} \
+ --build-arg VERSION=${VERSION} \
--build-arg BUILD_DATE=\"${BUILD_DATE}\" \
--build-arg VCS_REF=\"${VCS_REF}\""
-# Add install PHP tools flag based on environment
-if [ "${APP_ENV}" = "development" ]; then
- BUILD_CMD="$BUILD_CMD --build-arg INSTALL_PHP_TOOLS=true"
-else
- BUILD_CMD="$BUILD_CMD --build-arg INSTALL_PHP_TOOLS=false"
-fi
-
-# Add health check type for test builds
-if [ "$TEST_BUILD" = true ]; then
- BUILD_CMD="$BUILD_CMD --build-arg HEALTH_CHECK_TYPE=comprehensive"
- log_info "Building with comprehensive health check"
-fi
-
-# Add tags
-BUILD_CMD="$BUILD_CMD \
- --tag ${FULL_IMAGE}:${VERSION} \
- --tag ${FULL_IMAGE}:latest"
-
-# Add minor and major version tags
-MAJOR_VERSION=$(echo $VERSION | cut -d. -f1)
-MINOR_VERSION=$(echo $VERSION | cut -d. -f1-2)
-
-# Add tags
-if [ "$TEST_BUILD" = true ]; then
- BUILD_CMD="$BUILD_CMD \
- --tag ${FULL_IMAGE}:test \
- --tag ${FULL_IMAGE}:test-${VERSION}"
- log_info "Using test tags: test, test-${VERSION}"
-else
- BUILD_CMD="$BUILD_CMD \
- --tag ${FULL_IMAGE}:${VERSION} \
- --tag ${FULL_IMAGE}:latest"
-
- # Add minor and major version tags
- MAJOR_VERSION=$(echo $VERSION | cut -d. -f1)
- MINOR_VERSION=$(echo $VERSION | cut -d. -f1-2)
- BUILD_CMD="$BUILD_CMD \
- --tag ${FULL_IMAGE}:${MAJOR_VERSION} \
- --tag ${FULL_IMAGE}:${MINOR_VERSION}"
-fi
-
-# Add environment-specific tag
-if [ "${APP_ENV}" = "development" ]; then
- BUILD_CMD="$BUILD_CMD --tag ${FULL_IMAGE}:dev"
-elif [ "${APP_ENV}" = "staging" ]; then
- BUILD_CMD="$BUILD_CMD --tag ${FULL_IMAGE}:staging"
-elif [ "${APP_ENV}" = "production" ]; then
- BUILD_CMD="$BUILD_CMD --tag ${FULL_IMAGE}:stable"
-fi
+# Target-specific build arguments and tags
+case $BUILD_TARGET in
+ base)
+ BUILD_CMD="$BUILD_CMD --target base"
+ BUILD_CMD="$BUILD_CMD --tag ${FULL_IMAGE}:base"
+ ;;
+
+ production)
+ BUILD_CMD="$BUILD_CMD --target production"
+ BUILD_CMD="$BUILD_CMD \
+ --build-arg APP_ENV=production \
+ --build-arg PHP_OPCACHE_VALIDATE_TIMESTAMPS=0 \
+ --build-arg PHP_OPCACHE_MAX_ACCELERATED_FILES=20000 \
+ --build-arg PHP_OPCACHE_ENABLE=1 \
+ --build-arg PHP_OPCACHE_MEMORY_CONSUMPTION=256"
+
+ # Production tags
+ BUILD_CMD="$BUILD_CMD \
+ --tag ${FULL_IMAGE}:${VERSION} \
+ --tag ${FULL_IMAGE}:latest"
+
+ MAJOR_VERSION=$(echo $VERSION | cut -d. -f1)
+ MINOR_VERSION=$(echo $VERSION | cut -d. -f1-2)
+ BUILD_CMD="$BUILD_CMD \
+ --tag ${FULL_IMAGE}:${MAJOR_VERSION} \
+ --tag ${FULL_IMAGE}:${MINOR_VERSION}"
+ ;;
+
+ dev)
+ BUILD_CMD="$BUILD_CMD --target dev"
+ BUILD_CMD="$BUILD_CMD \
+ --build-arg APP_ENV=development \
+ --build-arg SYMFONY_CLI_VERSION=${SYMFONY_CLI_VERSION} \
+ --build-arg XDEBUG_VERSION=${XDEBUG_VERSION} \
+ --build-arg XDEBUG_ENABLE=1"
+
+ # Dev tags
+ BUILD_CMD="$BUILD_CMD \
+ --tag ${FULL_IMAGE}:dev \
+ --tag ${FULL_IMAGE}:dev-${VERSION}"
+ ;;
+esac
# Add push flag if multi-platform and push requested
if [ "$MULTI_PLATFORM" = true ] && [ "$PUSH_TO_HUB" = true ]; then
@@ -283,7 +304,7 @@ fi
BUILD_CMD="$BUILD_CMD --file Dockerfile ."
# Execute build
-log_step "Building Docker image..."
+log_step "Building Docker image (target: ${BUILD_TARGET})..."
echo -e "${BLUE}Command:${NC} $BUILD_CMD"
echo ""
@@ -307,103 +328,95 @@ if [ $? -eq 0 ]; then
log_step "Image information:"
docker images ${FULL_IMAGE} --format "table {{.Repository}}\t{{.Tag}}\t{{.Size}}\t{{.CreatedAt}}"
- # Get image size
- IMAGE_SIZE=$(docker images ${FULL_IMAGE}:latest --format "{{.Size}}")
+ IMAGE_SIZE=$(docker images ${FULL_IMAGE}:${BUILD_TARGET} --format "{{.Size}}" 2>/dev/null || echo "N/A")
echo ""
echo -e "${GREEN}Image size:${NC} ${IMAGE_SIZE}"
fi
- # Test the image if not multi-platform
- if [ "$MULTI_PLATFORM" = false ]; then
+ # Test the image if not multi-platform and not base
+ if [ "$MULTI_PLATFORM" = false ] && [ "$BUILD_TARGET" != "base" ]; then
echo ""
log_step "Testing image..."
- # Quick test
- docker run --rm ${FULL_IMAGE}:${VERSION} php -v > /dev/null 2>&1
- if [ $? -eq 0 ]; then
- log_info "✓ PHP test passed"
- else
- log_warning "PHP test failed"
- fi
+ TEST_TAG="${BUILD_TARGET}"
+ [ "$BUILD_TARGET" = "production" ] && TEST_TAG="latest"
- docker run --rm ${FULL_IMAGE}:${VERSION} nginx -v > /dev/null 2>&1
- if [ $? -eq 0 ]; then
- log_info "✓ Nginx test passed"
- else
- log_warning "Nginx test failed"
- fi
+ # Quick tests
+ docker run --rm ${FULL_IMAGE}:${TEST_TAG} php -v > /dev/null 2>&1 && log_info "✓ PHP test passed"
+ docker run --rm ${FULL_IMAGE}:${TEST_TAG} nginx -v > /dev/null 2>&1 && log_info "✓ Nginx test passed"
+ docker run --rm ${FULL_IMAGE}:${TEST_TAG} redis-server --version > /dev/null 2>&1 && log_info "✓ Redis test passed"
- docker run --rm ${FULL_IMAGE}:${VERSION} redis-server --version > /dev/null 2>&1
- if [ $? -eq 0 ]; then
- log_info "✓ Redis test passed"
- else
- log_warning "Redis test failed"
+ if [ "$BUILD_TARGET" = "dev" ]; then
+ docker run --rm ${FULL_IMAGE}:dev php -m | grep -q xdebug && log_info "✓ Xdebug installed"
+ docker run --rm ${FULL_IMAGE}:dev symfony version > /dev/null 2>&1 && log_info "✓ Symfony CLI installed"
fi
fi
- # Push to Docker Hub if requested and not already pushed (multi-platform)
+ # Push to Docker Hub if requested
if [ "$PUSH_TO_HUB" = true ] && [ "$MULTI_PLATFORM" = false ]; then
echo ""
log_step "Pushing to Docker Hub..."
- # Check if logged in
if ! docker info 2>/dev/null | grep -q "Username"; then
log_warning "Not logged in to Docker Hub. Logging in..."
docker login -u ${DOCKER_HUB_USER}
-
- if [ $? -ne 0 ]; then
- log_error "Failed to login to Docker Hub"
- fi
+ [ $? -ne 0 ] && log_error "Failed to login to Docker Hub"
fi
- # Push all tags
- for tag in ${VERSION} latest ${MAJOR_VERSION} ${MINOR_VERSION}; do
- log_info "Pushing ${FULL_IMAGE}:${tag}..."
- docker push ${FULL_IMAGE}:${tag}
- done
-
- # Push environment-specific tag
- if [ "${APP_ENV}" = "development" ]; then
- docker push ${FULL_IMAGE}:dev
- elif [ "${APP_ENV}" = "staging" ]; then
- docker push ${FULL_IMAGE}:staging
- elif [ "${APP_ENV}" = "production" ]; then
- docker push ${FULL_IMAGE}:stable
- fi
+ case $BUILD_TARGET in
+ production)
+ for tag in ${VERSION} latest ${MAJOR_VERSION} ${MINOR_VERSION}; do
+ log_info "Pushing ${FULL_IMAGE}:${tag}..."
+ docker push ${FULL_IMAGE}:${tag}
+ done
+ ;;
+ dev)
+ log_info "Pushing ${FULL_IMAGE}:dev..."
+ docker push ${FULL_IMAGE}:dev
+ docker push ${FULL_IMAGE}:dev-${VERSION}
+ ;;
+ base)
+ log_info "Pushing ${FULL_IMAGE}:base..."
+ docker push ${FULL_IMAGE}:base
+ ;;
+ esac
echo ""
log_info "✅ Push completed!"
- echo ""
- echo -e "${GREEN}Image available at:${NC}"
- echo " https://hub.docker.com/r/${FULL_IMAGE}"
+ echo -e "${GREEN}Image available at:${NC} https://hub.docker.com/r/${FULL_IMAGE}"
fi
# Show usage instructions
echo ""
- echo -e "${CYAN}═══ Usage Instructions ═══${NC}"
+ echo -e "${CYAN}━━━ Usage Instructions ━━━${NC}"
echo ""
- echo "To run the container:"
- echo -e " ${YELLOW}docker run -d -p ${APP_PORT}:80 ${FULL_IMAGE}:${VERSION}${NC}"
+
+ case $BUILD_TARGET in
+ production)
+ echo "To run the production container:"
+ echo -e " ${YELLOW}docker run -d -p 8080:80 ${FULL_IMAGE}:latest${NC}"
+ ;;
+ dev)
+ echo "To run the dev container with Xdebug:"
+ echo -e " ${YELLOW}docker run -d -p 8080:80 -p 9003:9003 -e XDEBUG_ENABLE=1 ${FULL_IMAGE}:dev${NC}"
+ ;;
+ base)
+ echo "Base image built successfully (foundation layer)"
+ echo "Use as base for production or dev stages"
+ ;;
+ esac
+
echo ""
echo "To pull from Docker Hub:"
- echo -e " ${YELLOW}docker pull ${FULL_IMAGE}:latest${NC}"
+ echo -e " ${YELLOW}docker pull ${FULL_IMAGE}:${BUILD_TARGET}${NC}"
echo ""
- if [ "$PUSH_TO_HUB" = false ]; then
- echo "To push to Docker Hub:"
- echo -e " ${YELLOW}docker push ${FULL_IMAGE}:${VERSION}${NC}"
- echo "Or run this script with --push flag:"
- echo -e " ${YELLOW}./build-from-env.sh --push${NC}"
- echo ""
- fi
-
else
log_error "❌ Build failed! Check the error messages above."
fi
# Cleanup buildx if it was created
if [ "$MULTI_PLATFORM" = true ]; then
- # Don't remove the builder, keep it for future use
log_info "Builder 'php-api-stack-builder' kept for future use"
log_info "To remove: docker buildx rm php-api-stack-builder"
fi
diff --git a/docker-compose.example.yml b/docker-compose.example.yml
index 7faf4c6..4791a35 100644
--- a/docker-compose.example.yml
+++ b/docker-compose.example.yml
@@ -5,6 +5,7 @@ services:
restart: unless-stopped
environment:
- DEMO_MODE=true
+ - HEALTH_CHECK_INSTALL=true
env_file:
- .env
ports:
diff --git a/docker-entrypoint.sh b/docker-entrypoint.sh
index e87747d..d37cd95 100755
--- a/docker-entrypoint.sh
+++ b/docker-entrypoint.sh
@@ -1,7 +1,9 @@
#!/bin/bash
set -e
+# ============================================================================
# Colors for output
+# ============================================================================
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
@@ -20,10 +22,10 @@ log_error() {
exit 1
}
-# ==============================================================================
-# BYPASS RÁPIDO PARA COMANDOS SIMPLES
-# ==============================================================================
-# Se for apenas verificação de versão ou comando direto, executar sem processamento
+# ============================================================================
+# QUICK BYPASS FOR SIMPLE COMMANDS
+# ============================================================================
+# If it's only a version check or a direct shell, execute without any processing
if [[ "$1" == "php" && "$2" == "-v" ]] || \
[[ "$1" == "php" && "$2" == "--version" ]] || \
[[ "$1" == "nginx" && "$2" == "-v" ]] || \
@@ -35,23 +37,23 @@ if [[ "$1" == "php" && "$2" == "-v" ]] || \
exec "$@"
fi
-# Se for comando básico sem argumentos complexos
+# If it's a basic shell command
if [[ "$1" == "bash" ]] || [[ "$1" == "sh" ]] || [[ "$1" == "/bin/bash" ]] || [[ "$1" == "/bin/sh" ]]; then
exec "$@"
fi
-# ==============================================================================
-# PROCESSAMENTO COMPLETO PARA EXECUÇÃO NORMAL
-# ==============================================================================
+# ============================================================================
+# FULL INITIALIZATION FOR NORMAL EXECUTION
+# ============================================================================
-# Function to wait for a service to be ready
+# Wait until a service is ready (basic checks)
wait_for_service() {
local service=$1
local max_attempts=${2:-30}
local attempt=0
-
+
log_info "Waiting for $service to be ready..."
-
+
case $service in
"php-fpm")
while [ $attempt -lt $max_attempts ]; do
@@ -84,15 +86,16 @@ wait_for_service() {
done
;;
esac
-
+
log_warning "$service failed to start after $max_attempts attempts"
return 1
}
-# Check if configurations need to be processed
+# ----------------------------------------------------------------------------
+# Process configuration templates (once per container lifetime)
+# ----------------------------------------------------------------------------
CONFIG_PROCESSED_FLAG="/tmp/.config_processed"
if [ ! -f "$CONFIG_PROCESSED_FLAG" ]; then
- # Process configuration templates
log_info "Processing configuration templates..."
/usr/local/bin/process-configs
touch "$CONFIG_PROCESSED_FLAG"
@@ -100,36 +103,40 @@ else
log_info "Configurations already processed, skipping..."
fi
-
-# optimization for production environment to force static PHP-FPM mode
+# ----------------------------------------------------------------------------
+# Production optimization: enforce static PHP-FPM in prod
+# ----------------------------------------------------------------------------
if [ "${APP_ENV}" = "production" ] || [ "${APP_ENV}" = "prod" ]; then
if [ "${PHP_FPM_PM}" != "static" ]; then
log_info "Environment is production. Overriding PHP_FPM_PM to 'static' for peak performance."
export PHP_FPM_PM="static"
fi
+ # Guard low pm.max_children (only warns)
if [ -z "${PHP_FPM_PM_MAX_CHILDREN}" ] || [ "${PHP_FPM_PM_MAX_CHILDREN}" -lt 50 ]; then
- log_warning "PHP_FPM_PM_MAX_CHILDREN is low for production static mode. Recommended >= 50."
+ log_warning "PHP_FPM_PM_MAX_CHILDREN may be low for production static mode. Recommended >= 50."
fi
else
log_info "Environment is non-production. Using dynamic PHP-FPM settings."
fi
-
-# Create required directories
+# ----------------------------------------------------------------------------
+# Create required directories and fix permissions
+# ----------------------------------------------------------------------------
log_info "Creating required directories..."
chown -R nginx:nginx /var/log/php /var/log/nginx /var/run/php
chown -R redis:redis /var/log/redis || true
-# chown -R root:root /var/log/supervisor
-chown -R nginx:nginx /var/run/nginx # Nginx run dir
+chown -R nginx:nginx /run/nginx
-# Fix permissions for session directory if using files
+# Session directory (file handler)
if [ "${PHP_SESSION_SAVE_HANDLER}" = "files" ]; then
mkdir -p /var/lib/php/sessions
chown -R nginx:nginx /var/lib/php/sessions
chmod 700 /var/lib/php/sessions
fi
-# Validate configurations apenas se não foram validadas ainda
+# ----------------------------------------------------------------------------
+# Validate configurations (only once)
+# ----------------------------------------------------------------------------
VALIDATION_FLAG="/tmp/.config_validated"
if [ ! -f "$VALIDATION_FLAG" ]; then
log_info "Validating configurations..."
@@ -138,11 +145,13 @@ if [ ! -f "$VALIDATION_FLAG" ]; then
touch "$VALIDATION_FLAG"
fi
-# Handle Symfony-specific initialization
+# ----------------------------------------------------------------------------
+# Symfony bootstrap (if app detected)
+# ----------------------------------------------------------------------------
if [ -f "/var/www/html/bin/console" ]; then
log_info "Symfony application detected"
-
- # Clear and warm up cache
+
+ # Cache warmup (prod only)
if [ "${APP_ENV}" = "production" ] || [ "${APP_ENV}" = "prod" ]; then
CACHE_FLAG="/tmp/.symfony_cache_warmed"
if [ ! -f "$CACHE_FLAG" ]; then
@@ -152,15 +161,15 @@ if [ -f "/var/www/html/bin/console" ]; then
touch "$CACHE_FLAG"
fi
fi
-
- # Run migrations if configured
+
+ # Database migrations (optional)
if [ "${RUN_MIGRATIONS}" = "true" ]; then
log_info "Running database migrations..."
su -s /bin/bash -c "php bin/console doctrine:migrations:migrate --no-interaction --allow-no-migration" nginx || \
log_warning "Migration failed or no migrations to run"
fi
-
- # Install assets
+
+ # Assets (optional)
if [ -d "/var/www/html/public/bundles" ]; then
ASSETS_FLAG="/tmp/.symfony_assets_installed"
if [ ! -f "$ASSETS_FLAG" ]; then
@@ -171,69 +180,64 @@ if [ -f "/var/www/html/bin/console" ]; then
fi
fi
-# Install demo index.php if no application is mounted AND DEMO_MODE is enabled
-if [ ! -f "/var/www/html/public/index.php" ]; then
- if [ "${DEMO_MODE}" = "true" ]; then
- log_info "DEMO_MODE enabled. Installing demo landing page..."
-
- # Create directory if it doesn't exist
- mkdir -p /var/www/html/public
-
- if [ -f "/usr/local/share/php-api-stack/index.php" ]; then
- cp /usr/local/share/php-api-stack/index.php /var/www/html/public/index.php
- log_info "Demo landing page installed"
- else
- log_warning "Demo template not found, creating basic fallback"
- cat > /var/www/html/public/index.php << 'EOF'
-/dev/null || true
fi
+# HEALTH_CHECK_INSTALL
+if [ "${HEALTH_CHECK_INSTALL}" = "true" ] && [ -f "/opt/php-api-stack-templates/health.php" ]; then
+ log_info "Publishing health.php to /var/www/html/public"
+ mkdir -p /var/www/html/public
+ cp /opt/php-api-stack-templates/health.php /var/www/html/public/health.php
+ chown nginx:nginx /var/www/html/public/health.php
+ chmod 644 /var/www/html/public/health.php
+else
+ rm -f /var/www/html/public/health.php 2>/dev/null || true
+fi
-# Create health check endpoint if doesn't exist
-if [ ! -f "/var/www/html/public/health.php" ]; then
- # Apenas instala health check se habilitado ou se DEMO_MODE estiver ativo
- if [ "${HEALTH_CHECK_INSTALL}" = "true" ] || [ "${DEMO_MODE}" = "true" ]; then
- log_info "Installing health check endpoint..."
-
- # Cria diretório se não existir
- mkdir -p /var/www/html/public
-
- # Copy from template if exists, otherwise create basic fallback
- if [ -f "/usr/local/share/php-api-stack/health.php" ]; then
- cp /usr/local/share/php-api-stack/health.php /var/www/html/public/health.php
- log_info "Health check installed from template"
+# -----------------------------------------------------------------------------
+# XDEBUG
+# -----------------------------------------------------------------------------
+XDEBUG_IS_ACTIVE=false
+# Toggle Xdebug at runtime
+if [ "${XDEBUG_ENABLE:-0}" = "1" ]; then
+ # First, check if the .so module is actually installed
+ if php -m | grep -q xdebug; then
+ # Module is installed. Now, apply the configuration.
+ if [ -f /usr/local/etc/php/conf.d/xdebug.ini.template ]; then
+ envsubst < /usr/local/etc/php/conf.d/xdebug.ini.template \
+ > /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini
+ log_info "Xdebug enabled (module loaded and config applied)"
+ XDEBUG_IS_ACTIVE=true
else
- log_warning "Health check template not found, creating basic fallback"
- cat > /var/www/html/public/health.php << 'EOF'
- 'healthy', 'timestamp' => date('c')], JSON_PRETTY_PRINT);
-EOF
+ # Module is installed, but the .ini is missing (bad state)
+ log_warning "Xdebug module is installed, but xdebug.ini.template is missing!"
fi
-
- chown nginx:nginx /var/www/html/public/health.php
- chmod 644 /var/www/html/public/health.php
else
- log_info "HEALTH_CHECK_INSTALL not enabled - skipping health check installation"
+ # XDEBUG_ENABLE=1, but the module was not found in the image.
+ log_warning "XDEBUG_ENABLE=1, but Xdebug module is not installed. Skipping."
fi
else
- log_info "Health check already exists at /var/www/html/public/health.php"
+ # XDEBUG_ENABLE is not 1, so ensure it's disabled.
+ rm -f /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini 2>/dev/null || true
+ log_info "Xdebug disabled"
fi
-# Set up log rotation apenas uma vez
+
+# ----------------------------------------------------------------------------
+# Log rotate setup (only useful if logs are bind-mounted)
+# ----------------------------------------------------------------------------
if [ ! -f "/etc/logrotate.d/php-api-stack" ]; then
- cat > /etc/logrotate.d/php-api-stack << EOF
+ cat > /etc/logrotate.d/php-api-stack << 'EOF'
/var/log/nginx/*.log {
daily
missingok
@@ -244,7 +248,7 @@ if [ ! -f "/etc/logrotate.d/php-api-stack" ]; then
create 640 nginx nginx
sharedscripts
postrotate
- [ -f /var/run/nginx.pid ] && kill -USR1 \$(cat /var/run/nginx.pid)
+ [ -f /var/run/nginx.pid ] && kill -USR1 $(cat /var/run/nginx.pid)
endscript
}
@@ -270,61 +274,52 @@ if [ ! -f "/etc/logrotate.d/php-api-stack" ]; then
EOF
fi
-# Performance tuning for production
+# ----------------------------------------------------------------------------
+# Production tuning (best-effort inside containers)
+# ----------------------------------------------------------------------------
if [ "${APP_ENV}" = "production" ] || [ "${APP_ENV}" = "prod" ]; then
log_info "Applying production performance optimizations..."
-
- # Increase system limits
ulimit -n 65536 2>/dev/null || log_warning "Could not set ulimit -n (normal in containers)"
ulimit -c unlimited 2>/dev/null || true
-
- # TCP optimizations (geralmente não funcionam em containers, mas tentamos)
- if [ -w /proc/sys/net/core/somaxconn ]; then
- echo 1024 > /proc/sys/net/core/somaxconn
- fi
-
- if [ -w /proc/sys/net/ipv4/tcp_max_syn_backlog ]; then
- echo 2048 > /proc/sys/net/ipv4/tcp_max_syn_backlog
- fi
+ if [ -w /proc/sys/net/core/somaxconn ]; then echo 1024 > /proc/sys/net/core/somaxconn; fi
+ if [ -w /proc/sys/net/ipv4/tcp_max_syn_backlog ]; then echo 2048 > /proc/sys/net/ipv4/tcp_max_syn_backlog; fi
fi
-# # Git safe directory fix for mounted volumes
+# ----------------------------------------------------------------------------
+# Git safe directory fix for mounted volumes
+# ----------------------------------------------------------------------------
if [ "$1" != "bash" ] && [ "$1" != "sh" ]; then
- log_info() { echo -e "${GREEN}[INFO]${NC} $1"; }
- log_info "Fixing git safe directory for mounted volumes..."
+ log_info "Marking /var/www/html as a safe Git directory"
git config --global --add safe.directory /var/www/html || true
fi
-# ==============================================================================
+# ============================================================================
# START SERVICES BASED ON COMMAND
-# ==============================================================================
-# removed:
-# supervisord|supervisor)
-# log_info "Starting all services with Supervisor..."
-# exec /usr/bin/supervisord -c /etc/supervisor/supervisord.conf
-# ;;
-# Especifics commands
+# ============================================================================
case "$1" in
start)
log_info "Starting all services..."
-
- # Start Redis in background (daemonize)
+
+ # Redis in background (daemonized)
log_info " -> Starting Redis..."
redis-server /etc/redis/redis.conf --daemonize yes
-
- # Start PHP-FPM in background (daemonize)
+
+ # PHP-FPM in background (daemonized)
log_info " -> Starting PHP-FPM..."
+ if [ "${XDEBUG_IS_ACTIVE}" = "true" ]; then
+ log_info " -> Xdebug is active"
+ fi
php-fpm -D
-
- # Start Nginx in foreground. The `exec` command is crucial here.
- # It replaces the script process with the Nginx process, making Nginx
- # the main container process (PID 1). This ensures it receives
- # Docker signals correctly (e.g., docker stop).
+
+ # Nginx in foreground (PID 1) so it receives Docker signals (e.g., stop)
log_info " -> Starting Nginx (foreground)..."
exec nginx -g 'daemon off;'
;;
php-fpm)
log_info "Starting PHP-FPM only..."
+ if [ "${XDEBUG_IS_ACTIVE}" = "true" ]; then
+ log_info " -> Xdebug is active"
+ fi
exec php-fpm -F
;;
nginx)
@@ -341,13 +336,8 @@ case "$1" in
exec su -s /bin/bash -c "php bin/console $*" nginx
;;
*)
- # Default: start supervisord if no command given
- # if [ "$#" -eq 0 ]; then
- # log_info "Starting all services with Supervisor (default)..."
- # exec /usr/bin/supervisord -c /etc/supervisor/supervisord.conf
- # else
- # Pass through any command
- exec "$@"
- # fi
+ # Pass through any command
+ exec "$@"
;;
-esac
\ No newline at end of file
+esac
+
diff --git a/health.php b/php/health.php
similarity index 96%
rename from health.php
rename to php/health.php
index 52f6271..41ce9ae 100644
--- a/health.php
+++ b/php/health.php
@@ -333,6 +333,29 @@ public function getName(): string
return 'redis';
}
+ /**
+ * Smart Redis Host Resolution
+ *
+ * Tenta resolver o hostname. Se falhar, usa 127.0.0.1 (standalone mode).
+ */
+ protected function getRedisHost(): string
+ {
+ $host = getenv('REDIS_HOST') ?: '127.0.0.1';
+
+ // Se for "redis" (docker-compose), verifica se resolve
+ if ($host === 'redis') {
+ // Suprime warning de DNS
+ $resolved = @gethostbyname($host);
+
+ // Se não resolveu (retorna o próprio hostname), usa localhost
+ if ($resolved === $host) {
+ return '127.0.0.1';
+ }
+ }
+
+ return $host;
+ }
+
protected function performCheck(): array
{
if (!extension_loaded('redis')) {
@@ -346,12 +369,13 @@ protected function performCheck(): array
$redis = new \Redis();
- $host = getenv('REDIS_HOST') ?: '127.0.0.1';
+ $host = $this->getRedisHost();
$password = getenv('REDIS_PASSWORD') ?: null;
+ $port = 6379;
try {
$connectStart = microtime(true);
- $connected = @$redis->connect('127.0.0.1', 6379, self::TIMEOUT);
+ $connected = @$redis->connect($host, $port, self::TIMEOUT);
$connectDuration = (microtime(true) - $connectStart) * 1000;
if (!$connected) {
diff --git a/index.php b/php/index.php
similarity index 96%
rename from index.php
rename to php/index.php
index 3fb168b..4764b8d 100644
--- a/index.php
+++ b/php/index.php
@@ -159,6 +159,31 @@ public function getName(): string
return 'Redis';
}
+
+ /**
+ * Smart Redis Host Resolution
+ *
+ * Tenta resolver o hostname. Se falhar, usa 127.0.0.1 (standalone mode).
+ */
+ protected function getRedisHost(): string
+ {
+ $host = getenv('REDIS_HOST') ?: '127.0.0.1';
+
+ // Se for "redis" (docker-compose), verifica se resolve
+ if ($host === 'redis') {
+ // Suprime warning de DNS
+ $resolved = @gethostbyname($host);
+
+ // Se não resolveu (retorna o próprio hostname), usa localhost
+ if ($resolved === $host) {
+ return '127.0.0.1';
+ }
+ }
+
+ return $host;
+ }
+
+
protected function performCheck(): StatusResult
{
if (!extension_loaded('redis')) {
@@ -171,11 +196,12 @@ protected function performCheck(): StatusResult
$redis = new \Redis();
- $host = getenv('REDIS_HOST') ?: '127.0.0.1';
+ $host = $this->getRedisHost();
$password = getenv('REDIS_PASSWORD') ?: null;
+ $port = 6379;
try {
- $connected = @$redis->connect('127.0.0.1', 6379, 1.0);
+ $connected = @$redis->connect($host, $port, 1.0);
if (!$connected) {
return new StatusResult(
diff --git a/php/xdebug.ini b/php/xdebug.ini
new file mode 100644
index 0000000..aa10b84
--- /dev/null
+++ b/php/xdebug.ini
@@ -0,0 +1,12 @@
+; Xdebug (dev only)
+zend_extension=xdebug
+
+xdebug.mode = ${XDEBUG_MODE}
+xdebug.start_with_request = trigger
+xdebug.client_host = ${XDEBUG_HOST}
+xdebug.client_port = ${XDEBUG_PORT}
+xdebug.idekey = ${XDEBUG_IDE_KEY}
+xdebug.max_nesting_level = 256
+xdebug.discover_client_host = 0
+xdebug.output_dir = /tmp
+xdebug.log = /var/log/php/xdebug.log
diff --git a/redis/redis.conf b/redis/redis.conf
index a9a1a12..02f9132 100644
--- a/redis/redis.conf
+++ b/redis/redis.conf
@@ -16,16 +16,14 @@ daemonize no
pidfile /var/run/redis.pid
loglevel ${REDIS_LOG_LEVEL}
# In containers, send logs to STDOUT (empty string)
-logfile /var/log/redis/redis.log
+logfile ${REDIS_LOG_FILE}
# Number of databases. Default: 16
databases ${REDIS_DATABASES}
always-show-logo no
# --- SNAPSHOTTING (RDB) ---
-# Mantidas fixas; se quiser controlar via REDIS_SAVE, trate no entrypoint
-save 900 1
-save 300 10
-save 60 10000
+# Controlled by REDIS_SAVE environment variable
+save ${REDIS_SAVE}
stop-writes-on-bgsave-error yes
rdbcompression yes
rdbchecksum yes
diff --git a/scripts/process-configs.sh b/scripts/process-configs.sh
index 6d9efc5..3730aba 100755
--- a/scripts/process-configs.sh
+++ b/scripts/process-configs.sh
@@ -2,7 +2,30 @@
# Process configuration templates with default values
set -e
+# ============================================================================
+# Colors for output
+# ============================================================================
+RED='\033[0;31m'
+GREEN='\033[0;32m'
+YELLOW='\033[1;33m'
+NC='\033[0m' # No Color
+
+log_info() {
+ echo -e "${GREEN}[INFO]${NC} $1"
+}
+
+log_warning() {
+ echo -e "${YELLOW}[WARNING]${NC} $1"
+}
+
+log_error() {
+ echo -e "${RED}[ERROR]${NC} $1"
+ exit 1
+}
+
+# ============================================================================
# Set default environment variables
+# ============================================================================
export PHP_FPM_PM=${PHP_FPM_PM:-dynamic}
export PHP_FPM_PM_MAX_CHILDREN=${PHP_FPM_PM_MAX_CHILDREN:-60}
export PHP_FPM_PM_START_SERVERS=${PHP_FPM_PM_START_SERVERS:-5}
@@ -32,13 +55,24 @@ export PHP_OPCACHE_REVALIDATE_FREQ=${PHP_OPCACHE_REVALIDATE_FREQ:-0}
export PHP_OPCACHE_JIT=${PHP_OPCACHE_JIT:-tracing}
export PHP_OPCACHE_JIT_BUFFER_SIZE=${PHP_OPCACHE_JIT_BUFFER_SIZE:-128M}
export PHP_FPM_REQUEST_SLOWLOG_TIMEOUT=${PHP_FPM_REQUEST_SLOWLOG_TIMEOUT:-30s}
+export PHP_PECL_EXTENSIONS="${PHP_PECL_EXTENSIONS:-redis apcu uuid}"
+export PHP_CORE_EXTENSIONS="${PHP_CORE_EXTENSIONS:-pdo pdo_mysql opcache intl zip bcmath gd mbstring xml sockets}"
+export ENABLE_CACHE=${ENABLE_CACHE:-true}
+export ENABLE_COMPRESSION=${ENABLE_COMPRESSION:-true}
+export ENABLE_HTTP2=${ENABLE_HTTP2:-true}
+export ENABLE_METRICS=${ENABLE_METRICS:-true}
+export ENABLE_HEALTH_CHECK=${ENABLE_HEALTH_CHECK:-true}
+export SECURITY_HEADERS=${SECURITY_HEADERS:-true}
+export HEALTH_CHECK_PATH=${HEALTH_CHECK_PATH:-/health}
export METRICS_PHP_FPM_PORT=${METRICS_PHP_FPM_PORT:-9000}
+export CACHE_TTL=${CACHE_TTL:-3600}
export XDEBUG_MODE=${XDEBUG_MODE:-off}
export XDEBUG_HOST=${XDEBUG_HOST:-host.docker.internal}
export XDEBUG_PORT=${XDEBUG_PORT:-9003}
-export XDEBUG_IDE_KEY=${XDEBUG_IDE_KEY:-PHPSTORM}
+export XDEBUG_IDE_KEY=${XDEBUG_IDE_KEY:-VSCODE}
+export XDEBUG_VERSION=${XDEBUG_VERSION:-3.4.6}
export NGINX_WORKER_PROCESSES=${NGINX_WORKER_PROCESSES:-auto}
export NGINX_WORKER_CONNECTIONS=${NGINX_WORKER_CONNECTIONS:-2048}
@@ -57,7 +91,7 @@ export REDIS_LOG_LEVEL=${REDIS_LOG_LEVEL:-notice}
export REDIS_LOG_FILE=${REDIS_LOG_FILE:-""}
export REDIS_MAXMEMORY=${REDIS_MAXMEMORY:-256M}
-export REDIS_MAXMEMORY_SAMPLES=${REDIS_MAXMEMORY_SAMPLES:-5}
+export REDIS_MAXMEMORY_SAMPLES=${REDIS_MAXMEMORY_SAMPLES:-5}
export REDIS_PASSWORD=${REDIS_PASSWORD:-}
export REDIS_DATABASES=${REDIS_DATABASES:-16}
export REDIS_MAXMEMORY_POLICY=${REDIS_MAXMEMORY_POLICY:-volatile-lru}
@@ -88,6 +122,8 @@ $PHP_DISPLAY_ERRORS
$PHP_ERROR_LOG
$PHP_SESSION_SAVE_HANDLER
$PHP_SESSION_SAVE_PATH
+$PHP_PECL_EXTENSIONS
+$PHP_CORE_EXTENSIONS
$PHP_OPCACHE_ENABLE
$PHP_OPCACHE_MEMORY
$PHP_OPCACHE_MAX_FILES
@@ -95,11 +131,20 @@ $PHP_OPCACHE_VALIDATE_TIMESTAMPS
$PHP_OPCACHE_REVALIDATE_FREQ
$PHP_OPCACHE_JIT
$PHP_OPCACHE_JIT_BUFFER_SIZE
+$ENABLE_CACHE
+$ENABLE_COMPRESSION
+$ENABLE_HTTP2
+$ENABLE_METRICS
+$ENABLE_HEALTH_CHECK
+$SECURITY_HEADERS
+$HEALTH_CHECK_PATH
$METRICS_PHP_FPM_PORT
+$CACHE_TTL
$XDEBUG_MODE
$XDEBUG_HOST
$XDEBUG_PORT
$XDEBUG_IDE_KEY
+$XDEBUG_VERSION
$NGINX_WORKER_PROCESSES
$NGINX_WORKER_CONNECTIONS
$NGINX_KEEPALIVE_TIMEOUT
@@ -124,7 +169,7 @@ $REDIS_TIMEOUT
'
-echo "Ensuring log directories exist and have correct permissions for validation..."
+log_info "Ensuring log directories exist and have correct permissions for validation..."
# Create log directories
mkdir -p /var/log/php /var/log/nginx /var/log/redis /var/log/supervisor /var/log/symfony /var/run/php /var/run/nginx
@@ -134,68 +179,82 @@ chown -R nginx:nginx /var/log/php /var/log/nginx /var/log/symfony /var/run/php /
chown -R redis:redis /var/log/redis || true
chown -R root:root /var/log/supervisor
-echo " ✓ Log directories created and permissions set"
+log_info " ✓ Log directories created and permissions set"
-echo "Processing configuration templates..."
+log_info "Processing configuration templates..."
# Process templates
if [ -f /etc/nginx/nginx.conf.template ]; then
envsubst "$VARS_TO_SUBSTITUTE" < /etc/nginx/nginx.conf.template > /etc/nginx/nginx.conf
- echo " ✓ Nginx main configuration processed"
+ log_info " ✓ Nginx main configuration processed"
fi
if [ -f /etc/nginx/conf.d/default.conf.template ]; then
envsubst "$VARS_TO_SUBSTITUTE" < /etc/nginx/conf.d/default.conf.template > /etc/nginx/conf.d/default.conf
- echo " ✓ Nginx site configuration processed"
+ log_info " ✓ Nginx site configuration processed"
fi
if [ -f /usr/local/etc/php/php.ini.template ]; then
envsubst "$VARS_TO_SUBSTITUTE" < /usr/local/etc/php/php.ini.template > /usr/local/etc/php/php.ini
- echo " ✓ PHP.ini configuration processed"
+ log_info " ✓ PHP.ini configuration processed"
fi
# Process PHP-FPM global configuration if template exists
if [ -f /usr/local/etc/php-fpm.conf.template ]; then
envsubst "$VARS_TO_SUBSTITUTE" < /usr/local/etc/php-fpm.conf.template > /usr/local/etc/php-fpm.conf
- echo " ✓ PHP-FPM global configuration processed"
+ log_info " ✓ PHP-FPM global configuration processed"
fi
if [ -f /usr/local/etc/php-fpm.d/www.conf.template ]; then
envsubst "$VARS_TO_SUBSTITUTE" < /usr/local/etc/php-fpm.d/www.conf.template > /usr/local/etc/php-fpm.d/www.conf
- echo " ✓ PHP-FPM pool configuration processed"
+ log_info " ✓ PHP-FPM pool configuration processed"
fi
if [ -f /usr/local/etc/php-fpm.d/monitoring.conf.template ]; then
envsubst "$VARS_TO_SUBSTITUTE" < /usr/local/etc/php-fpm.d/monitoring.conf.template > /usr/local/etc/php-fpm.d/monitoring.conf
- echo " ✓ PHP-FPM monitoring pool configuration processed"
+ log_info " ✓ PHP-FPM monitoring pool configuration processed"
fi
if [ -f /etc/redis/redis.conf.template ]; then
envsubst "$VARS_TO_SUBSTITUTE" < /etc/redis/redis.conf.template > /etc/redis/redis.conf
- echo " ✓ Redis configuration processed"
+ log_info " ✓ Redis configuration processed"
fi
-echo "Configuration templates processed successfully."
+if [ "${XDEBUG_ENABLE:-0}" = "1" ]; then
+ if [ -f /usr/local/etc/php/conf.d/xdebug.ini.template ]; then
+ envsubst "$VARS_TO_SUBSTITUTE" < /usr/local/etc/php/conf.d/xdebug.ini.template \
+ > /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini
+ log_info " ✓ Xdebug configuration processed (enabled)"
+ else
+ log_warning " ⚠ Xdebug enabled, but template not found: /usr/local/etc/php/conf.d/xdebug.ini.template"
+ fi
+else
+ rm -f /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini 2>/dev/null || true
+ log_info " ⟳ Xdebug disabled (no ini applied)"
+fi
+
+
+log_info "Configuration templates processed successfully."
# Validate configurations
-echo "Validating configurations..."
+log_info "Validating configurations..."
# Validate PHP-FPM
if php-fpm -t 2>/dev/null; then
- echo " ✓ PHP-FPM configuration is valid"
+ log_info " ✓ PHP-FPM configuration is valid"
else
- echo " ✗ PHP-FPM configuration test failed!"
+ log_error " ✗ PHP-FPM configuration test failed!"
php-fpm -t
exit 1
fi
# Validate Nginx
if nginx -t 2>/dev/null; then
- echo " ✓ Nginx configuration is valid"
+ log_info " ✓ Nginx configuration is valid"
else
- echo " ✗ Nginx configuration test failed!"
+ log_error " ✗ Nginx configuration test failed!"
nginx -t
exit 1
fi
-echo "All configurations are valid."
\ No newline at end of file
+log_info "All configurations are valid."