Skip to content

Health Checks

Health Checks

Cbox PHP Base Images include comprehensive health checking for Docker and Kubernetes environments.

Built-in Health Check

All Cbox images include a deep health validation script at /usr/local/bin/healthcheck.sh.

What It Checks

Check Description Failure Impact
PHP-FPM Process Verifies php-fpm master process is running Container unhealthy
Nginx Process Verifies nginx master process is running Container unhealthy
Port 80 Tests TCP connection to port 80 Container unhealthy
Port 9000 Tests PHP-FPM FastCGI port Container unhealthy
OPcache Status Verifies OPcache is functioning Warning only
Critical Extensions Checks pdo_mysql, redis are loaded Warning only
Memory Usage Reports PHP-FPM memory consumption Informational

Running Manually

# Execute health check
docker exec myapp /usr/local/bin/healthcheck.sh

# Expected output (healthy):
# ✓ PHP-FPM running
# ✓ Nginx running
# ✓ Port 80 responding
# ✓ Port 9000 responding
# ✓ OPcache enabled
# ✓ Critical extensions loaded
# Memory: 45MB / 512MB

Docker Health Check

Default Configuration

Cbox images include a Docker HEALTHCHECK instruction:

HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
    CMD /usr/local/bin/healthcheck.sh || exit 1
Parameter Value Description
interval 30s Time between health checks
timeout 5s Maximum time for check to complete
start-period 10s Grace period during container startup
retries 3 Failures before marking unhealthy

Checking Health Status

# View container health
docker inspect --format='{{.State.Health.Status}}' myapp
# Output: healthy | unhealthy | starting

# Detailed health history
docker inspect --format='{{json .State.Health}}' myapp | jq

# Watch health checks in real-time
docker events --filter container=myapp --filter event=health_status

Customizing Health Check

Override in docker-compose.yml:

services:
  app:
    image: ghcr.io/cboxdk/php-baseimages/php-fpm-nginx:8.4-bookworm
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost/healthz"]
      interval: 15s
      timeout: 5s
      retries: 5
      start_period: 30s

Or in Dockerfile:

FROM ghcr.io/cboxdk/php-baseimages/php-fpm-nginx:8.4-bookworm

# Custom health check
HEALTHCHECK --interval=15s --timeout=10s --retries=5 \
    CMD curl -f http://localhost/healthz || exit 1

Disabling Health Check

For CI/CD or testing scenarios:

services:
  app:
    image: ghcr.io/cboxdk/php-baseimages/php-fpm-nginx:8.4-bookworm
    healthcheck:
      disable: true

Kubernetes Health Probes

Liveness Probe

Determines if container should be restarted:

apiVersion: v1
kind: Pod
spec:
  containers:
  - name: app
    image: ghcr.io/cboxdk/php-baseimages/php-fpm-nginx:8.4-bookworm
    livenessProbe:
      httpGet:
        path: /livez
        port: 9091
      initialDelaySeconds: 10
      periodSeconds: 30
      timeoutSeconds: 5
      failureThreshold: 3

Readiness Probe

Determines if container should receive traffic:

apiVersion: v1
kind: Pod
spec:
  containers:
  - name: app
    image: ghcr.io/cboxdk/php-baseimages/php-fpm-nginx:8.4-bookworm
    readinessProbe:
      httpGet:
        path: /readyz
        port: 9091
      initialDelaySeconds: 5
      periodSeconds: 10
      timeoutSeconds: 3
      successThreshold: 1
      failureThreshold: 3

Startup Probe

For slow-starting applications:

apiVersion: v1
kind: Pod
spec:
  containers:
  - name: app
    image: ghcr.io/cboxdk/php-baseimages/php-fpm-nginx:8.4-bookworm
    startupProbe:
      httpGet:
        path: /readyz
        port: 9091
      initialDelaySeconds: 0
      periodSeconds: 5
      timeoutSeconds: 3
      failureThreshold: 30  # 30 * 5s = 150s max startup

Complete Kubernetes Configuration

apiVersion: apps/v1
kind: Deployment
metadata:
  name: cbox-app
spec:
  replicas: 3
  selector:
    matchLabels:
      app: cbox
  template:
    metadata:
      labels:
        app: cbox
    spec:
      containers:
      - name: app
        image: ghcr.io/cboxdk/php-baseimages/php-fpm-nginx:8.4-bookworm
        ports:
        - containerPort: 80

        # Restart if completely stuck
        livenessProbe:
          httpGet:
            path: /livez
            port: 9091
          initialDelaySeconds: 30
          periodSeconds: 30
          timeoutSeconds: 5
          failureThreshold: 3

        # Remove from service if not ready
        readinessProbe:
          httpGet:
            path: /readyz
            port: 9091
          initialDelaySeconds: 5
          periodSeconds: 10
          timeoutSeconds: 3
          failureThreshold: 3

        # Allow slow startup (migrations, cache warming)
        startupProbe:
          httpGet:
            path: /readyz
            port: 9091
          periodSeconds: 5
          failureThreshold: 60  # 5 minutes max

Application-Level Health Check

Laravel Health Endpoint

Create a dedicated health route:

// routes/web.php
Route::get('/health', function () {
    try {
        // Check database
        DB::connection()->getPdo();

        // Check Redis
        Redis::ping();

        // Check storage
        Storage::disk('local')->exists('.gitignore');

        return response()->json([
            'status' => 'healthy',
            'timestamp' => now()->toIso8601String(),
        ]);
    } catch (\Exception $e) {
        return response()->json([
            'status' => 'unhealthy',
            'error' => $e->getMessage(),
        ], 503);
    }
});

Symfony Health Endpoint

// src/Controller/HealthController.php
#[Route('/health')]
public function health(
    EntityManagerInterface $em,
    CacheInterface $cache
): JsonResponse {
    try {
        // Check database
        $em->getConnection()->executeQuery('SELECT 1');

        // Check cache
        $cache->get('health_check', fn() => true);

        return new JsonResponse(['status' => 'healthy']);
    } catch (\Exception $e) {
        return new JsonResponse(
            ['status' => 'unhealthy', 'error' => $e->getMessage()],
            Response::HTTP_SERVICE_UNAVAILABLE
        );
    }
}

The three health endpoints

Cbox images expose three distinct health surfaces — don't confuse them:

Endpoint Port Reachable from Purpose
nginx /healthz 80 (8080 rootless) localhost only (allow 127.0.0.1; deny all) The container HEALTHCHECK and cbox-init's internal nginx probe. Returns 200 healthy, no PHP. Not reachable off-host, so not usable for a Kubernetes httpGet probe.
cbox-init /readyz + /livez 9091 anywhere (bound 0.0.0.0) What Kubernetes probes. /readyz200 only when every supervised process (nginx + php-fpm + workers) is ready; /livez200 while the supervisor is responsive. cbox-init also writes /tmp/cbox-ready for an exec probe.
/health, /up 80/8080 public (via your app) Owned by your application. nginx passes them through to PHP — the base image does not serve /health itself. Use these for app-level health (spatie/laravel-health, Laravel's /up).

Enable the cbox-init HTTP endpoints with global.readiness.http_port: 9091 (shipped in the default cbox-init.yaml). Kubernetes probes should target 9091 (/readyz, /livez) — see the Deployment example above — not nginx /health.

Earlier image versions served a public nginx /health; that was removed so it no longer shadows the application's own /health//up routes.

Cbox Init Health Checks

Cbox Process Manager includes built-in health checks for all managed processes:

Process Health Checks

environment:
  # Health check configuration
  CBOX_INIT_PROCESS_PHP_FPM_HEALTH_CHECK_TYPE: tcp
  CBOX_INIT_PROCESS_PHP_FPM_HEALTH_CHECK_TARGET: "127.0.0.1:9000"
  CBOX_INIT_PROCESS_PHP_FPM_HEALTH_CHECK_INTERVAL: 10

  CBOX_INIT_PROCESS_NGINX_HEALTH_CHECK_TYPE: http
  CBOX_INIT_PROCESS_NGINX_HEALTH_CHECK_TARGET: "http://127.0.0.1/health"
  CBOX_INIT_PROCESS_NGINX_HEALTH_CHECK_INTERVAL: 10

Health Check Types

Type Target Format Use Case
tcp host:port Process listening check
http http://host/path HTTP endpoint check
exec /path/to/script Custom script check

Prometheus Metrics

Cbox Init exposes health metrics on port 9090:

# Get health metrics
curl http://localhost:9090/metrics | grep health

# Example output:
# cbox_init_health_check_status{process="php-fpm",check_type="tcp"} 1
# cbox_init_health_check_status{process="nginx",check_type="http"} 1

Troubleshooting

Health Check Failing

# Check what's happening
docker exec myapp /usr/local/bin/healthcheck.sh

# Check process status
docker exec myapp ps aux

# Check logs
docker logs myapp

# Check if ports are listening
docker exec myapp netstat -tlnp

Common Issues

PHP-FPM not starting:

# Check PHP-FPM config
docker exec myapp php-fpm -t

# Check PHP-FPM logs
docker exec myapp cat /var/log/php-fpm/error.log

Nginx not starting:

# Check Nginx config
docker exec myapp nginx -t

# Check Nginx logs
docker exec myapp cat /var/log/nginx/error.log

Port not responding:

# Check if processes are listening
docker exec myapp ss -tlnp

# Test port internally
docker exec myapp curl -v http://127.0.0.1/health

Health Check Timeouts

If health checks timeout during startup:

# Increase start period for slow apps
healthcheck:
  start_period: 60s  # Allow 60s for startup
  interval: 30s
  timeout: 10s
  retries: 3

Need more help? Common Issues | Cbox Init Integration