fix(docker): supervise self-hosted runtime processes

This commit is contained in:
4gray committed 2026-05-21 02:46:46 +03:00
1 parent 23a1330a14
commit 6afaa04a95
4 files changed
+55 -16

No files matched your search

+14 -12
View File
@@ -43,12 +43,12 @@ before it creates `PwaService`.
These variables are supported by the Docker image. The compose file sets the
safe local defaults shown below.
| Variable | Default | Purpose |
| ---------------------------------------- | ----------------------- | ------------------------------------------------------------------------------------------------------------------ |
| `BACKEND_URL` | `/api` | Browser-facing backend URL used by the PWA. Keep `/api` for the bundled nginx proxy. |
| `CLIENT_URL` | `http://localhost:4333` | Allowed browser origin for backend CORS. Use the public URL when hosting behind a reverse proxy. Multiple origins can be comma-separated. |
| `PORT` | `3000` | Internal Express backend port. nginx proxy config is rendered from the template to match it at startup. |
| `IPTVNATOR_PROXY_ALLOW_PRIVATE_NETWORKS` | `0` | Set to `1` or `true` only for trusted local/LAN deployments that intentionally proxy private network IPTV or mock endpoints. |
| Variable | Default | Purpose |
| ---------------------------------------- | ----------------------- | -------------------------------------------------------------------------------------------------------------------------------------------- |
| `BACKEND_URL` | `/api` | Browser-facing backend URL used by the PWA. Keep `/api` for the bundled nginx proxy. |
| `CLIENT_URL` | `http://localhost:4333` | Allowed browser origin for backend CORS. Use the public URL when hosting behind a reverse proxy. Multiple origins can be comma-separated. |
| `PORT` | `3000` | Internal Express backend port. nginx proxy config is rendered from the template to match it at startup. |
| `IPTVNATOR_PROXY_ALLOW_PRIVATE_NETWORKS` | `0` | Set to `1` or `true` only for trusted local/LAN deployments that intentionally proxy private network IPTV or mock endpoints. |
| `NODE_EXTRA_CA_CERTS` | unset | Optional Node.js CA bundle path for providers using private certificate authorities. Mount the CA file into the container and set this path. |
The web backend proxy accepts only `http` and `https` provider URLs. The PWA
@@ -64,18 +64,20 @@ enabled and pass the CA bundle to Node:
```yaml
services:
iptvnator:
volumes:
- ./ca.pem:/etc/ssl/private/provider-ca.pem:ro
environment:
NODE_EXTRA_CA_CERTS: /etc/ssl/private/provider-ca.pem
iptvnator:
volumes:
- ./ca.pem:/etc/ssl/private/provider-ca.pem:ro
environment:
NODE_EXTRA_CA_CERTS: /etc/ssl/private/provider-ca.pem
```
The entrypoint renders the nginx config from `docker/nginx.conf`, starts the
backend, waits for `/health`, and only then starts nginx. The nginx config
serves the PWA with SPA fallback, avoids caching `assets/app-config.js`, and
proxies `/api/*` to the internal backend. The Dockerfile and compose file both
define a health check against `/api/health`.
define a health check against `/api/health`. If nginx or the backend exits
after startup, the entrypoint exits the container so Docker Compose can apply
the `restart: unless-stopped` policy.
## Local Validation
+1
View File
@@ -5,6 +5,7 @@ services:
build:
context: ..
dockerfile: docker/Dockerfile
restart: unless-stopped
ports:
- "4333:80"
environment:
+37 -3
View File
@@ -33,13 +33,24 @@ cleanup() {
if [ -n "${NGINX_PID:-}" ]; then
kill "$NGINX_PID" 2>/dev/null || true
wait "$NGINX_PID" 2>/dev/null || true
fi
if [ -n "${BACKEND_PID:-}" ]; then
kill "$BACKEND_PID" 2>/dev/null || true
wait "$BACKEND_PID" 2>/dev/null || true
fi
}
process_is_running() {
if [ ! -r "/proc/$1/stat" ]; then
return 1
fi
state="$(awk '{ print $3 }' "/proc/$1/stat" 2>/dev/null || true)"
[ -n "$state" ] && [ "$state" != "Z" ]
}
trap cleanup INT TERM EXIT
backend_ready=0
@@ -50,9 +61,12 @@ for _ in $(seq 1 30); do
break
fi
if ! kill -0 "$BACKEND_PID" 2>/dev/null; then
if ! process_is_running "$BACKEND_PID"; then
set +e
wait "$BACKEND_PID"
exit $?
BACKEND_STATUS=$?
set -e
exit "$BACKEND_STATUS"
fi
sleep 1
@@ -66,4 +80,24 @@ fi
nginx -g 'daemon off;' &
NGINX_PID=$!
wait "$NGINX_PID"
while :; do
if ! process_is_running "$BACKEND_PID"; then
set +e
wait "$BACKEND_PID"
EXIT_STATUS=$?
set -e
echo "IPTVnator web backend exited with status ${EXIT_STATUS}."
exit "$EXIT_STATUS"
fi
if ! process_is_running "$NGINX_PID"; then
set +e
wait "$NGINX_PID"
EXIT_STATUS=$?
set -e
echo "nginx exited with status ${EXIT_STATUS}."
exit "$EXIT_STATUS"
fi
sleep 1
done
+3 -1
View File
@@ -109,7 +109,9 @@ The Docker image has two stages:
2. Runtime stage uses `node:22-alpine` with nginx installed. nginx serves
`dist/apps/web` and proxies `/api/*` to the local Express backend.
The entrypoint renders the nginx config from a `${PORT}` template, starts the
backend, waits for `/health`, and then starts nginx.
backend, waits for `/health`, and then starts nginx. If either process exits
after startup, the entrypoint exits the container so the compose restart
policy can recover the service.
Default runtime values: