- Add docker volume prune to deploy.sh to prevent stale code issues - Add automatic migrations and cache warmup to staging entrypoint - Fix nginx race condition by waiting for PHP-FPM before starting - Improve PHP healthcheck to use php-fpm-healthcheck - Add curl to production nginx Dockerfile for healthchecks - Add ensureSeedsTable() to SeedRepository for automatic table creation - Update SeedCommand to ensure seeds table exists before operations This prevents 502 Bad Gateway errors during deployment and ensures fresh code is deployed without volume cache issues.
523 lines
20 KiB
YAML
523 lines
20 KiB
YAML
# Staging Environment Override
|
||
# Usage: docker-compose -f docker-compose.base.yml -f docker-compose.staging.yml up
|
||
#
|
||
# This file overrides base configuration with staging-specific settings:
|
||
# - Container names with "staging-" prefix
|
||
# - Traefik integration for staging.michaelschiemer.de
|
||
# - Git clone functionality for staging branch
|
||
# - Staging-specific networks (traefik-public, staging-internal)
|
||
# - Staging-specific volumes
|
||
|
||
services:
|
||
# PHP-FPM Application Runtime
|
||
php:
|
||
image: localhost:5000/framework:latest
|
||
container_name: php
|
||
restart: unless-stopped
|
||
networks:
|
||
- app-backend
|
||
- app-internal
|
||
environment:
|
||
- TZ=Europe/Berlin
|
||
- APP_ENV=staging
|
||
- APP_DEBUG=false
|
||
- APP_URL=https://staging.michaelschiemer.de
|
||
- APP_KEY=${APP_KEY:-}
|
||
# Git Repository - clones staging branch
|
||
- GIT_REPOSITORY_URL=${GIT_REPOSITORY_URL:-}
|
||
- GIT_BRANCH=staging
|
||
- GIT_TOKEN=${GIT_TOKEN:-}
|
||
- GIT_USERNAME=${GIT_USERNAME:-}
|
||
- GIT_PASSWORD=${GIT_PASSWORD:-}
|
||
# Database - using separate staging database
|
||
- DB_HOST=postgres
|
||
- DB_PORT=5432
|
||
- DB_DATABASE=${DB_DATABASE:-michaelschiemer_staging}
|
||
- DB_USERNAME=${DB_USERNAME}
|
||
- DB_PASSWORD=${DB_PASSWORD}
|
||
# Redis
|
||
- REDIS_HOST=redis
|
||
- REDIS_PORT=6379
|
||
# Cache
|
||
- CACHE_DRIVER=redis
|
||
- CACHE_PREFIX=${CACHE_PREFIX:-staging}
|
||
# Session
|
||
- SESSION_DRIVER=redis
|
||
- SESSION_LIFETIME=${SESSION_LIFETIME:-120}
|
||
# Queue
|
||
- QUEUE_DRIVER=redis
|
||
- QUEUE_CONNECTION=default
|
||
# Use Docker Secrets via *_FILE pattern (Framework supports this automatically)
|
||
# Note: These paths will be set by the entrypoint script after copying secrets
|
||
# to /var/www/html/storage/secrets/ for www-data access
|
||
# The entrypoint script will copy secrets and set these paths
|
||
- DB_PASSWORD_FILE=/var/www/html/storage/secrets/db_user_password
|
||
- REDIS_PASSWORD_FILE=/var/www/html/storage/secrets/redis_password
|
||
- APP_KEY_FILE=/var/www/html/storage/secrets/app_key
|
||
volumes:
|
||
- app-code:/var/www/html
|
||
- app-storage:/var/www/html/storage
|
||
- app-logs:/var/www/html/storage/logs
|
||
- /etc/timezone:/etc/timezone:ro
|
||
- /etc/localtime:/etc/localtime:ro
|
||
secrets:
|
||
- db_user_password
|
||
- redis_password
|
||
- app_key
|
||
# Override entrypoint to only start PHP-FPM (not nginx) + fix git ownership
|
||
entrypoint: ["/bin/sh", "-c"]
|
||
command:
|
||
- |
|
||
|
||
# Copy Docker Secrets to readable location for www-data
|
||
# Docker Secrets are only readable by root, but PHP (www-data) needs to read them.
|
||
# We copy them here as root to a location where www-data can read them.
|
||
# Note: Use $$ to escape shell variables in docker-compose YAML
|
||
echo "🔐 Setting up Docker Secrets for PHP access..."
|
||
SECRETS_DIR="/var/www/html/storage/secrets"
|
||
# Ensure we're in the right directory
|
||
cd /var/www/html || exit 1
|
||
# Create secrets directory if it doesn't exist
|
||
mkdir -p "$$SECRETS_DIR"
|
||
chmod 750 "$$SECRETS_DIR"
|
||
chown www-data:www-data "$$SECRETS_DIR"
|
||
|
||
if [ -f /run/secrets/redis_password ]; then
|
||
cp /run/secrets/redis_password "$$SECRETS_DIR/redis_password" 2>/dev/null || true
|
||
chmod 640 "$$SECRETS_DIR/redis_password"
|
||
chown www-data:www-data "$$SECRETS_DIR/redis_password"
|
||
export REDIS_PASSWORD_FILE="$$SECRETS_DIR/redis_password"
|
||
echo "✅ Copied redis_password to $$SECRETS_DIR/redis_password"
|
||
else
|
||
echo "⚠️ Warning: /run/secrets/redis_password not found"
|
||
fi
|
||
|
||
if [ -f /run/secrets/db_user_password ]; then
|
||
cp /run/secrets/db_user_password "$$SECRETS_DIR/db_user_password" 2>/dev/null || true
|
||
chmod 640 "$$SECRETS_DIR/db_user_password"
|
||
chown www-data:www-data "$$SECRETS_DIR/db_user_password"
|
||
export DB_PASSWORD_FILE="$$SECRETS_DIR/db_user_password"
|
||
echo "✅ Copied db_user_password to $$SECRETS_DIR/db_user_password"
|
||
else
|
||
echo "⚠️ Warning: /run/secrets/db_user_password not found"
|
||
fi
|
||
|
||
if [ -f /run/secrets/app_key ]; then
|
||
cp /run/secrets/app_key "$$SECRETS_DIR/app_key" 2>/dev/null || true
|
||
chmod 640 "$$SECRETS_DIR/app_key"
|
||
chown www-data:www-data "$$SECRETS_DIR/app_key"
|
||
export APP_KEY_FILE="$$SECRETS_DIR/app_key"
|
||
echo "✅ Copied app_key to $$SECRETS_DIR/app_key"
|
||
else
|
||
echo "⚠️ Warning: /run/secrets/app_key not found"
|
||
fi
|
||
|
||
|
||
# Fix Git ownership issue
|
||
# Ensure Git treats the mounted repository as safe regardless of owner
|
||
git config --global --add safe.directory /var/www/html 2>/dev/null || true
|
||
git config --system --add safe.directory /var/www/html 2>/dev/null || true
|
||
|
||
# Git Clone/Pull functionality
|
||
if [ -n "$GIT_REPOSITORY_URL" ]; then
|
||
echo ""
|
||
echo "📥 Cloning/Pulling code from Git repository..."
|
||
|
||
GIT_BRANCH="${GIT_BRANCH:-main}"
|
||
GIT_TARGET_DIR="/var/www/html"
|
||
|
||
# Setup Git credentials
|
||
if [ -n "$GIT_TOKEN" ]; then
|
||
GIT_URL_WITH_AUTH=$(echo "$GIT_REPOSITORY_URL" | sed "s|https://|https://${GIT_TOKEN}@|")
|
||
elif [ -n "$GIT_USERNAME" ] && [ -n "$GIT_PASSWORD" ]; then
|
||
GIT_URL_WITH_AUTH=$(echo "$GIT_REPOSITORY_URL" | sed "s|https://|https://${GIT_USERNAME}:${GIT_PASSWORD}@|")
|
||
else
|
||
GIT_URL_WITH_AUTH="$GIT_REPOSITORY_URL"
|
||
fi
|
||
|
||
# Clone or pull
|
||
if [ ! -d "$GIT_TARGET_DIR/.git" ]; then
|
||
echo "📥 Cloning repository from $GIT_REPOSITORY_URL (branch: $GIT_BRANCH)..."
|
||
if [ "$(ls -A $GIT_TARGET_DIR 2>/dev/null)" ]; then
|
||
find "$GIT_TARGET_DIR" -mindepth 1 -maxdepth 1 ! -name "storage" -exec rm -rf {} \; 2>/dev/null || true
|
||
fi
|
||
TEMP_CLONE="${GIT_TARGET_DIR}.tmp"
|
||
rm -rf "$TEMP_CLONE" 2>/dev/null || true
|
||
if git -c safe.directory=/var/www/html clone --branch "$GIT_BRANCH" --depth 1 "$GIT_URL_WITH_AUTH" "$TEMP_CLONE"; then
|
||
find "$GIT_TARGET_DIR" -mindepth 1 -maxdepth 1 ! -name "storage" -exec rm -rf {} \; 2>/dev/null || true
|
||
find "$TEMP_CLONE" -mindepth 1 -maxdepth 1 ! -name "." ! -name ".." -exec mv {} "$GIT_TARGET_DIR/" \; 2>/dev/null || true
|
||
rm -rf "$TEMP_CLONE" 2>/dev/null || true
|
||
echo "✅ Repository cloned successfully"
|
||
fi
|
||
else
|
||
echo "🔄 Pulling latest changes from $GIT_BRANCH..."
|
||
cd "$GIT_TARGET_DIR"
|
||
git -c safe.directory=/var/www/html fetch origin "$GIT_BRANCH" || echo "⚠️ Git fetch failed"
|
||
git -c safe.directory=/var/www/html reset --hard "origin/$GIT_BRANCH" || echo "⚠️ Git reset failed"
|
||
git -c safe.directory=/var/www/html clean -fd || true
|
||
fi
|
||
|
||
# Install dependencies
|
||
if [ -f "$GIT_TARGET_DIR/composer.json" ]; then
|
||
echo "📦 Installing/updating Composer dependencies..."
|
||
cd "$GIT_TARGET_DIR"
|
||
composer install --no-dev --optimize-autoloader --no-interaction --no-scripts || echo "⚠️ Composer install failed"
|
||
composer dump-autoload --optimize --classmap-authoritative || true
|
||
fi
|
||
|
||
echo "✅ Git sync completed"
|
||
else
|
||
echo ""
|
||
echo "ℹ️ GIT_REPOSITORY_URL not set, using code from image"
|
||
fi
|
||
|
||
echo ""
|
||
echo "📊 Environment variables:"
|
||
env | grep -E "DB_|APP_" | grep -v "PASSWORD|KEY|SECRET" || true
|
||
|
||
# Run database migrations
|
||
if [ -f /var/www/html/console.php ]; then
|
||
echo ""
|
||
echo "🗄️ Running database migrations..."
|
||
cd /var/www/html
|
||
php console.php db:migrate --force || echo "⚠️ Migration warning (may be OK if already migrated)"
|
||
fi
|
||
|
||
# Warm up caches
|
||
echo ""
|
||
echo "🔥 Warming up caches..."
|
||
cd /var/www/html
|
||
php console.php cache:warm 2>/dev/null || echo "ℹ️ Cache warmup skipped (command may not exist)"
|
||
|
||
echo ""
|
||
echo "🛠️ Adjusting filesystem permissions..."
|
||
chown -R www-data:www-data /var/www/html/storage /var/www/html/bootstrap/cache 2>/dev/null || true
|
||
find /var/www/html/storage /var/www/html/bootstrap/cache -type d -exec chmod 775 {} \; 2>/dev/null || true
|
||
find /var/www/html/storage /var/www/html/bootstrap/cache -type f -exec chmod 664 {} \; 2>/dev/null || true
|
||
|
||
# Keep PHP-FPM secure with clear_env = yes (default)
|
||
# The *_FILE environment variables are passed explicitly via docker-compose environment section
|
||
# PHP's DockerSecretsResolver will read the secrets from the files specified in *_FILE vars
|
||
|
||
# Start PHP-FPM only (no nginx)
|
||
echo ""
|
||
echo "🚀 Starting PHP-FPM..."
|
||
echo "REDIS_PASSWORD_FILE: ${REDIS_PASSWORD_FILE:-NOT SET}"
|
||
exec php-fpm
|
||
healthcheck:
|
||
# Use HTTP liveness check via php-fpm (not via nginx)
|
||
# This checks if the PHP application is actually responding
|
||
test: ["CMD-SHELL", "php-fpm-healthcheck || exit 1"]
|
||
interval: 15s
|
||
timeout: 5s
|
||
retries: 3
|
||
start_period: 60s
|
||
depends_on:
|
||
redis:
|
||
condition: service_started
|
||
# Nginx Web Server
|
||
nginx:
|
||
image: localhost:5000/framework:latest
|
||
container_name: nginx
|
||
restart: unless-stopped
|
||
networks:
|
||
- traefik-public
|
||
- app-backend
|
||
environment:
|
||
- TZ=Europe/Berlin
|
||
- APP_ENV=staging
|
||
- APP_DEBUG=false
|
||
# Git Repository - clones staging branch
|
||
- GIT_REPOSITORY_URL=${GIT_REPOSITORY_URL:-}
|
||
- GIT_BRANCH=staging
|
||
- GIT_TOKEN=${GIT_TOKEN:-}
|
||
- GIT_USERNAME=${GIT_USERNAME:-}
|
||
- GIT_PASSWORD=${GIT_PASSWORD:-}
|
||
volumes:
|
||
- ./deployment/stacks/staging/nginx/conf.d:/etc/nginx/conf.d:ro
|
||
- app-code:/var/www/html:ro
|
||
- app-storage:/var/www/html/storage:ro
|
||
- /etc/timezone:/etc/timezone:ro
|
||
- /etc/localtime:/etc/localtime:ro
|
||
# Wait for code to be available (cloned by php container) then start nginx
|
||
entrypoint: ["/bin/sh", "-c"]
|
||
command:
|
||
- |
|
||
# Wait for code to be available in shared volume (php container clones it)
|
||
GIT_TARGET_DIR="/var/www/html"
|
||
echo "⏳ [staging-nginx] Waiting for code to be available in shared volume..."
|
||
for i in 1 2 3 4 5 6 7 8 9 10; do
|
||
if [ -d "$$GIT_TARGET_DIR/public" ]; then
|
||
echo "✅ [staging-nginx] Code found in shared volume"
|
||
break
|
||
fi
|
||
echo " [staging-nginx] Waiting... ($$i/10)"
|
||
sleep 2
|
||
done
|
||
|
||
# If code still not available after wait, try to copy from image as fallback
|
||
if [ ! -d "$$GIT_TARGET_DIR/public" ] && [ -d "/var/www/html.orig" ]; then
|
||
echo "⚠️ [staging-nginx] Code not found in shared volume, copying from image..."
|
||
find /var/www/html.orig -mindepth 1 -maxdepth 1 ! -name "storage" -exec cp -r {} "$$GIT_TARGET_DIR/" \; 2>/dev/null || true
|
||
fi
|
||
|
||
# Fix nginx upstream configuration - sites-enabled/default is a symlink to sites-available/default
|
||
# This is critical: nginx config uses production-php:9000 but staging uses php container
|
||
for NGINX_CONF in /etc/nginx/sites-enabled/default /etc/nginx/sites-available/default; do
|
||
if [ -f "$$NGINX_CONF" ]; then
|
||
echo "🔧 [staging-nginx] Fixing PHP-FPM upstream in $$NGINX_CONF..."
|
||
# Replace production-php with staging php container name
|
||
sed -i 's|server production-php:9000;|server php:9000;|g' "$$NGINX_CONF" || true
|
||
# Replace localhost/127.0.0.1 references
|
||
sed -i '/upstream php-upstream {/,/}/s|server 127.0.0.1:9000;|server php:9000;|g' "$$NGINX_CONF" || true
|
||
sed -i '/upstream php-upstream {/,/}/s|server localhost:9000;|server php:9000;|g' "$$NGINX_CONF" || true
|
||
# Replace any auto-generated container names (like 5aad84af7c9e_php)
|
||
sed -i 's|server [a-zA-Z0-9_-]*php:9000;|server php:9000;|g' "$$NGINX_CONF" || true
|
||
# Replace any direct fastcgi_pass references too
|
||
sed -i 's|fastcgi_pass 127.0.0.1:9000;|fastcgi_pass php-upstream;|g' "$$NGINX_CONF" || true
|
||
sed -i 's|fastcgi_pass localhost:9000;|fastcgi_pass php-upstream;|g' "$$NGINX_CONF" || true
|
||
sed -i 's|fastcgi_pass production-php:9000;|fastcgi_pass php-upstream;|g' "$$NGINX_CONF" || true
|
||
echo "✅ [staging-nginx] PHP-FPM upstream fixed in $$NGINX_CONF"
|
||
fi
|
||
done
|
||
|
||
# Wait for PHP-FPM to be ready before starting nginx
|
||
# This prevents 502 Bad Gateway errors during startup
|
||
echo "⏳ [staging-nginx] Waiting for PHP-FPM to be ready..."
|
||
MAX_WAIT=30
|
||
WAITED=0
|
||
while [ $$WAITED -lt $$MAX_WAIT ]; do
|
||
# Check if PHP-FPM is accepting connections on port 9000
|
||
if nc -z php 9000 2>/dev/null; then
|
||
echo "✅ [staging-nginx] PHP-FPM is ready on php:9000"
|
||
break
|
||
fi
|
||
echo " [staging-nginx] PHP-FPM not ready yet... ($$WAITED/$$MAX_WAIT)"
|
||
sleep 1
|
||
WAITED=$$((WAITED + 1))
|
||
done
|
||
|
||
if [ $$WAITED -ge $$MAX_WAIT ]; then
|
||
echo "⚠️ [staging-nginx] PHP-FPM did not become ready within $$MAX_WAIT seconds, starting anyway..."
|
||
fi
|
||
|
||
# Start nginx only (no PHP-FPM, no Git clone - php container handles that)
|
||
echo "🚀 [staging-nginx] Starting nginx..."
|
||
exec nginx -g "daemon off;"
|
||
labels:
|
||
- "traefik.enable=true"
|
||
# HTTP Router for staging subdomain
|
||
- "traefik.http.routers.staging.rule=Host(`staging.michaelschiemer.de`)"
|
||
- "traefik.http.routers.staging.entrypoints=websecure"
|
||
- "traefik.http.routers.staging.tls=true"
|
||
- "traefik.http.routers.staging.tls.certresolver=letsencrypt"
|
||
# Service
|
||
- "traefik.http.services.staging.loadbalancer.server.port=80"
|
||
# Network
|
||
- "traefik.docker.network=traefik-public"
|
||
healthcheck:
|
||
# Use /health/live endpoint for lightweight liveness check
|
||
test: ["CMD-SHELL", "curl -sf http://127.0.0.1/health/live || exit 1"]
|
||
interval: 15s
|
||
timeout: 5s
|
||
retries: 3
|
||
start_period: 30s
|
||
depends_on:
|
||
php:
|
||
condition: service_started
|
||
# Remove base service dependencies and build
|
||
ports: []
|
||
|
||
# Redis Cache/Session/Queue Backend (separate from production)
|
||
redis:
|
||
image: redis:7-alpine
|
||
container_name: redis
|
||
restart: unless-stopped
|
||
networks:
|
||
- app-backend
|
||
environment:
|
||
- TZ=Europe/Berlin
|
||
- REDIS_PASSWORD_FILE=/run/secrets/redis_password
|
||
secrets:
|
||
- redis_password
|
||
# Use entrypoint script to inject password from Docker Secret into config
|
||
# Note: Script runs as root to read Docker Secrets, then starts Redis
|
||
entrypoint: ["/bin/sh", "-c"]
|
||
command:
|
||
- |
|
||
# Read password from Docker Secret (as root)
|
||
REDIS_PASSWORD=$$(cat /run/secrets/redis_password 2>/dev/null || echo '')
|
||
# Start Redis with all settings as command line arguments (no config file to avoid conflicts)
|
||
if [ -n "$$REDIS_PASSWORD" ]; then
|
||
exec redis-server \
|
||
--bind 0.0.0.0 \
|
||
--dir /data \
|
||
--maxmemory 256mb \
|
||
--maxmemory-policy allkeys-lru \
|
||
--save 900 1 \
|
||
--save 300 10 \
|
||
--save 60 10000 \
|
||
--appendonly yes \
|
||
--appendfsync everysec \
|
||
--requirepass "$$REDIS_PASSWORD"
|
||
else
|
||
exec redis-server \
|
||
--bind 0.0.0.0 \
|
||
--dir /data \
|
||
--maxmemory 256mb \
|
||
--maxmemory-policy allkeys-lru \
|
||
--save 900 1 \
|
||
--save 300 10 \
|
||
--save 60 10000 \
|
||
--appendonly yes \
|
||
--appendfsync everysec
|
||
fi
|
||
volumes:
|
||
- redis-data:/data
|
||
- /etc/timezone:/etc/timezone:ro
|
||
- /etc/localtime:/etc/localtime:ro
|
||
|
||
# Queue Worker (Background Jobs)
|
||
queue-worker:
|
||
image: localhost:5000/framework:latest
|
||
container_name: queue-worker
|
||
restart: unless-stopped
|
||
networks:
|
||
- app-backend
|
||
- app-internal
|
||
environment:
|
||
- TZ=Europe/Berlin
|
||
- APP_ENV=staging
|
||
- APP_DEBUG=false
|
||
# Database - using separate staging database
|
||
- DB_HOST=postgres
|
||
- DB_PORT=5432
|
||
- DB_DATABASE=${DB_DATABASE:-michaelschiemer_staging}
|
||
- DB_USERNAME=${DB_USERNAME}
|
||
- DB_PASSWORD=${DB_PASSWORD}
|
||
# Use Docker Secrets via *_FILE pattern (Framework supports this automatically)
|
||
- DB_PASSWORD_FILE=/run/secrets/db_user_password
|
||
# Redis
|
||
- REDIS_HOST=redis
|
||
- REDIS_PORT=6379
|
||
- REDIS_PASSWORD_FILE=/run/secrets/redis_password
|
||
# Queue
|
||
- QUEUE_DRIVER=redis
|
||
- QUEUE_CONNECTION=default
|
||
- QUEUE_WORKER_SLEEP=${QUEUE_WORKER_SLEEP:-3}
|
||
- QUEUE_WORKER_TRIES=${QUEUE_WORKER_TRIES:-3}
|
||
- QUEUE_WORKER_TIMEOUT=${QUEUE_WORKER_TIMEOUT:-60}
|
||
volumes:
|
||
- app-code:/var/www/html
|
||
- app-storage:/var/www/html/storage
|
||
- app-logs:/var/www/html/storage/logs
|
||
- /etc/timezone:/etc/timezone:ro
|
||
- /etc/localtime:/etc/localtime:ro
|
||
command: php console.php queue:work --queue=default --timeout=${QUEUE_WORKER_TIMEOUT:-60}
|
||
healthcheck:
|
||
test: ["CMD-SHELL", "php -r 'exit(0);' && test -f /var/www/html/console.php || exit 1"]
|
||
interval: 60s
|
||
timeout: 10s
|
||
retries: 3
|
||
start_period: 30s
|
||
depends_on:
|
||
php:
|
||
condition: service_started
|
||
redis:
|
||
condition: service_started
|
||
entrypoint: ""
|
||
stop_grace_period: 30s
|
||
secrets:
|
||
- db_user_password
|
||
- redis_password
|
||
- app_key
|
||
|
||
# Scheduler (Cron Jobs)
|
||
scheduler:
|
||
image: localhost:5000/framework:latest
|
||
container_name: scheduler
|
||
restart: unless-stopped
|
||
networks:
|
||
- app-backend
|
||
- app-internal
|
||
environment:
|
||
- TZ=Europe/Berlin
|
||
- APP_ENV=staging
|
||
- APP_DEBUG=false
|
||
# Database - using separate staging database
|
||
- DB_HOST=postgres
|
||
- DB_PORT=5432
|
||
- DB_DATABASE=${DB_DATABASE:-michaelschiemer_staging}
|
||
- DB_USERNAME=${DB_USERNAME}
|
||
- DB_PASSWORD=${DB_PASSWORD}
|
||
# Use Docker Secrets via *_FILE pattern (Framework supports this automatically)
|
||
- DB_PASSWORD_FILE=/run/secrets/db_user_password
|
||
# Redis
|
||
- REDIS_HOST=redis
|
||
- REDIS_PORT=6379
|
||
- REDIS_PASSWORD_FILE=/run/secrets/redis_password
|
||
volumes:
|
||
- app-code:/var/www/html
|
||
- app-storage:/var/www/html/storage
|
||
- app-logs:/var/www/html/storage/logs
|
||
- /etc/timezone:/etc/timezone:ro
|
||
- /etc/localtime:/etc/localtime:ro
|
||
command: php console.php scheduler:run
|
||
healthcheck:
|
||
test: ["CMD-SHELL", "php -r 'exit(0);' && test -f /var/www/html/console.php || exit 1"]
|
||
interval: 60s
|
||
timeout: 10s
|
||
retries: 3
|
||
start_period: 30s
|
||
depends_on:
|
||
php:
|
||
condition: service_started
|
||
redis:
|
||
condition: service_started
|
||
entrypoint: ""
|
||
stop_grace_period: 30s
|
||
secrets:
|
||
- db_user_password
|
||
- redis_password
|
||
- app_key
|
||
|
||
# Disable base services (override from docker-compose.base.yml)
|
||
web:
|
||
profiles: [never]
|
||
minio:
|
||
profiles: [never]
|
||
|
||
networks:
|
||
traefik-public:
|
||
external: true
|
||
app-backend:
|
||
driver: bridge
|
||
app-internal:
|
||
external: true
|
||
name: app-internal
|
||
|
||
volumes:
|
||
app-code:
|
||
name: staging-code
|
||
app-storage:
|
||
name: staging-storage
|
||
app-logs:
|
||
name: staging-logs
|
||
redis-data:
|
||
name: staging-redis-data
|
||
|
||
# Docker Secrets Configuration
|
||
# Secrets are inherited from docker-compose.base.yml
|
||
# But we need to explicitly define them here to ensure they're available
|
||
secrets:
|
||
db_user_password:
|
||
file: ./deployment/secrets/staging/db_password.txt
|
||
external: false
|
||
redis_password:
|
||
file: ./deployment/secrets/staging/redis_password.txt
|
||
external: false
|
||
app_key:
|
||
file: ./deployment/secrets/staging/app_key.txt
|
||
external: false
|
||
|