harden(docker): non-root, dedicated healthcheck, opcache split, graceful shutdown
Coolify-doc-driven production hardening of the deploy stack: - run the whole stack as non-root www-data; nginx on 8080 (non-privileged), pid in /tmp, user directive dropped (Coolify routes to any port) - docker/healthcheck.sh: hit real /health route via PHP (not just port probe) - split OPcache config into docker/php/opcache.ini - graceful shutdown: supervisord stopsignal/stopwaitsecs + worker stop_grace_period - APCu intentionally not added (Symfony cache uses redis) - DEPLOY.md: 8080 port, non-root, resource-limit guidance Verified on linux/amd64: non-root uid=82, /health 200, migrations run, worker process healthcheck OK. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,6 @@
|
||||
#!/bin/sh
|
||||
# Container healthcheck — hits the real Symfony /health route through nginx+fpm,
|
||||
# so a green status means the app actually boots and serves (not just that the
|
||||
# port is open). Uses PHP (always present in the image) to avoid depending on
|
||||
# curl/wget. nginx listens on 8080 (non-root). Exit 0 = healthy, 1 = unhealthy.
|
||||
exec php -r '$h=@get_headers("http://127.0.0.1:8080/health"); exit($h && strpos($h[0]," 200")!==false ? 0 : 1);'
|
||||
@@ -1,5 +1,8 @@
|
||||
server {
|
||||
listen 80 default_server;
|
||||
# Non-privileged port so the whole stack can run as www-data (non-root).
|
||||
# Coolify/Traefik routes to whatever port the service exposes — assign 8080
|
||||
# as the service port in Coolify.
|
||||
listen 8080 default_server;
|
||||
server_name _;
|
||||
root /app/public;
|
||||
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
; OPcache — production tuning for ClinicPro.
|
||||
; validate_timestamps=0: code never changes at runtime in an immutable image, so
|
||||
; skip stat() checks for max throughput. (A new deploy = a new image.)
|
||||
; preload: warms the Symfony container/classes into shared memory at FPM start;
|
||||
; runs as www-data, so app files must be readable by www-data (see Dockerfile).
|
||||
opcache.enable = 1
|
||||
opcache.enable_cli = 0
|
||||
opcache.memory_consumption = 256
|
||||
opcache.max_accelerated_files = 20000
|
||||
opcache.validate_timestamps = 0
|
||||
opcache.interned_strings_buffer = 16
|
||||
opcache.preload = /app/config/preload.php
|
||||
opcache.preload_user = www-data
|
||||
@@ -6,16 +6,6 @@ max_execution_time = 60
|
||||
expose_php = Off
|
||||
date.timezone = Asia/Tehran
|
||||
|
||||
; OPcache (production)
|
||||
opcache.enable = 1
|
||||
opcache.enable_cli = 0
|
||||
opcache.memory_consumption = 256
|
||||
opcache.max_accelerated_files = 20000
|
||||
opcache.validate_timestamps = 0
|
||||
opcache.interned_strings_buffer = 16
|
||||
opcache.preload = /app/config/preload.php
|
||||
opcache.preload_user = www-data
|
||||
|
||||
; Realpath cache (perf)
|
||||
realpath_cache_size = 4096k
|
||||
realpath_cache_ttl = 600
|
||||
|
||||
+18
-2
@@ -1,14 +1,28 @@
|
||||
[supervisord]
|
||||
nodaemon=true
|
||||
user=root
|
||||
# Run the whole stack as the non-root www-data user (security hardening).
|
||||
# nginx listens on 8080 (non-privileged) so root is not needed to bind the port.
|
||||
user=www-data
|
||||
logfile=/dev/stdout
|
||||
logfile_maxbytes=0
|
||||
pidfile=/run/supervisord.pid
|
||||
pidfile=/tmp/supervisord.pid
|
||||
|
||||
[unix_http_server]
|
||||
file=/tmp/supervisor.sock
|
||||
|
||||
[supervisorctl]
|
||||
serverurl=unix:///tmp/supervisor.sock
|
||||
|
||||
[rpcinterface:supervisor]
|
||||
supervisor.rpcinterface_factory=supervisor.rpcinterface:make_main_rpcinterface
|
||||
|
||||
[program:php-fpm]
|
||||
command=php-fpm -F
|
||||
autorestart=true
|
||||
priority=10
|
||||
# Forward SIGTERM (graceful) and give workers time to finish in-flight requests.
|
||||
stopsignal=TERM
|
||||
stopwaitsecs=15
|
||||
stdout_logfile=/dev/stdout
|
||||
stdout_logfile_maxbytes=0
|
||||
stderr_logfile=/dev/stderr
|
||||
@@ -18,6 +32,8 @@ stderr_logfile_maxbytes=0
|
||||
command=nginx -g 'daemon off;'
|
||||
autorestart=true
|
||||
priority=20
|
||||
stopsignal=QUIT
|
||||
stopwaitsecs=15
|
||||
stdout_logfile=/dev/stdout
|
||||
stdout_logfile_maxbytes=0
|
||||
stderr_logfile=/dev/stderr
|
||||
|
||||
Reference in New Issue
Block a user