Skip to main content

❤️ Healthcheck & Logs

Healthcheck​

The image defines a Docker healthcheck that hits /fpm-ping on the local Nginx:

HEALTHCHECK --timeout=10s CMD curl --silent --fail http://127.0.0.1:8080/fpm-ping || exit 1

How it works:

  • Nginx has a dedicated location ~ ^/(fpm-status|fpm-ping)$ block.
  • That block only allows requests from 127.0.0.1 — the rest of the world sees a 403.
  • The request is forwarded to PHP-FPM via the Unix socket /run/php-fpm.sock.
  • PHP-FPM responds with the literal string pong when it's healthy.

Why both Nginx and PHP-FPM: healthy Nginx alone is not enough — you want to detect a PHP-FPM worker pool that stopped responding. Hitting /fpm-ping proves the FastCGI path works end-to-end.

Checking healthcheck status​

docker ps # look at the STATUS column
docker inspect --format='{{json .State.Health}}' <container>

Using it from outside localhost​

It intentionally does not work from outside. Trying curl http://host:8080/fpm-ping returns 403 Forbidden — this is by design (#20). If you need an external probe, expose your own public /health endpoint in your app that checks whatever your app considers "healthy":

// /var/www/html/health.php
<?php
http_response_code(200);
header('Content-Type: text/plain');
echo "ok\n";

Logs​

All logs go to the container's standard streams so docker logs just works.

SourceStreamNotes
Nginx accessstdoutCustom main_timed format including $request_time and $upstream_response_time.
Nginx errorstderrLevel: notice.
PHP-FPMstdout / stderrWorker errors and php-fpm messages.
Your PHP appstdout / stderrVia error_log = /dev/stderr in custom.ini.
runit servicesstderrrunit writes start/stop events and crash loops.

Viewing logs​

docker compose logs -f web # follow
docker compose logs --since 10m web # recent slice
docker compose logs web | grep " 500 " # 500s from the access log

Custom log format​

The format is defined once in nginx.conf:

log_format main_timed '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for" '
'$request_time $upstream_response_time $pipe $upstream_cache_status';

That's enough to debug slow responses — $request_time is total Nginx time, $upstream_response_time is PHP-FPM time. The gap between the two is how long Nginx spent buffering and sending.

Forwarding to an aggregator​

Docker log drivers already do this for you — just configure your daemon or Compose stack:

services:
web:
image: ruseler/alpine-php-webserver
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"

Or use syslog, fluentd, awslogs, etc. Nothing special required on the image side.

Access log discipline​

Access logs are noisy, but do not disable them. They are the first thing you will need when something breaks. If volume is a concern, rotate at the Docker log driver (json-file with max-size/max-file) or ship them to an aggregator with its own retention.

Tailing with timestamps​

docker logs --timestamps adds an RFC3339 timestamp per line:

docker compose logs -f --timestamps web

This documentation is a fork of erseco/alpine-php-webserver. The upstream documentation is available here.