feat(nginx): generate 502 config for apps without web listeners

When an app has no web listeners (not yet deployed, no web process type,
or web processes not running), generate a minimal nginx config that
returns 502 Bad Gateway instead of having no config at all. This ensures
domains resolve, monitoring tools detect non-200 status codes, and SSL
certificate provisioning tools like letsencrypt can function.
This commit is contained in:
Jose Diaz-Gonzalez
2026-04-24 02:10:30 -04:00
parent 71ec612fbd
commit 2993fa800c
6 changed files with 218 additions and 11 deletions

View File

@@ -306,9 +306,19 @@ nginx_build_config() {
local PROXY_BUSY_BUFFERS_SIZE="$(fn-nginx-computed-proxy-busy-buffers-size "$APP")"
local PROXY_KEEPALIVE="$(fn-nginx-computed-proxy-keepalive "$APP")"
if [[ -z "$DOKKU_APP_LISTENERS" ]]; then
local NGINX_BIND_ADDRESS_IP4="$(fn-nginx-computed-bind-address-ipv4 "$APP")"
local NGINX_BIND_ADDRESS_IP6="$(fn-nginx-computed-bind-address-ipv6 "$APP")"
local NGINX_UNDERSCORE_IN_HEADERS="$(fn-nginx-computed-underscore-in-headers "$APP")"
local PROXY_X_FORWARDED_FOR="$(fn-nginx-computed-x-forwarded-for-value "$APP")"
local PROXY_X_FORWARDED_PORT="$(fn-nginx-computed-x-forwarded-port-value "$APP")"
local PROXY_X_FORWARDED_PROTO="$(fn-nginx-computed-x-forwarded-proto-value "$APP")"
local PROXY_X_FORWARDED_SSL="$(fn-nginx-computed-x-forwarded-ssl "$APP")"
if [[ -z "$DOKKU_APP_LISTENERS" ]] && [[ -z "$PASSED_LISTEN_IP_PORT" ]]; then
dokku_log_warn_quiet "No web listeners specified for $APP"
elif (is_deployed "$APP"); then
fi
if [[ -n "$DOKKU_APP_LISTENERS" ]] && (is_deployed "$APP"); then
if [[ "$(plugn trigger network-get-static-listeners "$APP" "web")" == "" ]]; then
local IMAGE_TAG=$(get_running_image_tag "$APP")
local IMAGE=$(get_deploying_app_image_name "$APP" "$IMAGE_TAG" 2>/dev/null)
@@ -329,14 +339,6 @@ nginx_build_config() {
local NGINX_TEMPLATE_SOURCE="app-supplied"
fi
local NGINX_BIND_ADDRESS_IP4="$(fn-nginx-computed-bind-address-ipv4 "$APP")"
local NGINX_BIND_ADDRESS_IP6="$(fn-nginx-computed-bind-address-ipv6 "$APP")"
local NGINX_UNDERSCORE_IN_HEADERS="$(fn-nginx-computed-underscore-in-headers "$APP")"
local PROXY_X_FORWARDED_FOR="$(fn-nginx-computed-x-forwarded-for-value "$APP")"
local PROXY_X_FORWARDED_PORT="$(fn-nginx-computed-x-forwarded-port-value "$APP")"
local PROXY_X_FORWARDED_PROTO="$(fn-nginx-computed-x-forwarded-proto-value "$APP")"
local PROXY_X_FORWARDED_SSL="$(fn-nginx-computed-x-forwarded-ssl "$APP")"
eval "$(config_export app "$APP")"
local SIGIL_PARAMS=(-f "$NGINX_TEMPLATE" APP="$APP" DOKKU_ROOT="$DOKKU_ROOT"
NOSSL_SERVER_NAME="$NOSSL_SERVER_NAME"
@@ -413,6 +415,65 @@ nginx_build_config() {
plugn trigger nginx-pre-reload "$APP" "$DOKKU_APP_LISTEN_PORT" "$DOKKU_APP_LISTEN_IP"
dokku_log_verbose "Reloading nginx"
validate_nginx && restart_nginx >/dev/null
elif [[ -z "$DOKKU_APP_LISTENERS" ]] && [[ -z "$PASSED_LISTEN_IP_PORT" ]]; then
local NGINX_BUILD_CONFIG_TMP_WORK_DIR=$(mktemp -d "/tmp/dokku-${DOKKU_PID}-${FUNCNAME[0]}.XXXXXX")
local NGINX_CONF=$(mktemp --tmpdir="${NGINX_BUILD_CONFIG_TMP_WORK_DIR}" "nginx.conf.XXXXXX")
trap "rm -rf '$NGINX_CONF' '$NGINX_BUILD_CONFIG_TMP_WORK_DIR' >/dev/null" RETURN INT TERM EXIT
local SIGIL_PARAMS=(-f "$NGINX_TEMPLATE" APP="$APP" DOKKU_ROOT="$DOKKU_ROOT"
NOSSL_SERVER_NAME="$NOSSL_SERVER_NAME"
DOKKU_APP_LISTENERS=""
DOKKU_APP_WEB_LISTENERS=""
DOKKU_LIB_ROOT="$DOKKU_LIB_ROOT"
PASSED_LISTEN_IP_PORT=""
SPDY_SUPPORTED="$SPDY_SUPPORTED"
TLS13_SUPPORTED="$TLS13_SUPPORTED"
HTTP2_SUPPORTED="$HTTP2_SUPPORTED"
NGINX_LOG_ROOT="$NGINX_LOG_ROOT"
NGINX_ACCESS_LOG_FORMAT="$NGINX_ACCESS_LOG_FORMAT"
NGINX_ACCESS_LOG_PATH="$NGINX_ACCESS_LOG_PATH"
NGINX_ERROR_LOG_PATH="$NGINX_ERROR_LOG_PATH"
NGINX_BIND_ADDRESS_IP4="$NGINX_BIND_ADDRESS_IP4"
NGINX_BIND_ADDRESS_IP6="$NGINX_BIND_ADDRESS_IP6"
HTTP2_DIRECTIVE_SUPPORTED="$HTTP2_DIRECTIVE_SUPPORTED"
HTTP2_PUSH_SUPPORTED="$HTTP2_PUSH_SUPPORTED"
GRPC_SUPPORTED="$GRPC_SUPPORTED"
DOKKU_APP_LISTEN_PORT="" DOKKU_APP_LISTEN_IP=""
APP_SSL_PATH="$APP_SSL_PATH" SSL_INUSE="$SSL_INUSE" SSL_SERVER_NAME="$SSL_SERVER_NAME"
CLIENT_BODY_TIMEOUT="$CLIENT_BODY_TIMEOUT"
CLIENT_HEADER_TIMEOUT="$CLIENT_HEADER_TIMEOUT"
CLIENT_MAX_BODY_SIZE="$CLIENT_MAX_BODY_SIZE"
KEEPALIVE_TIMEOUT="$KEEPALIVE_TIMEOUT"
LINGERING_TIMEOUT="$LINGERING_TIMEOUT"
PROXY_CONNECT_TIMEOUT="$PROXY_CONNECT_TIMEOUT"
PROXY_READ_TIMEOUT="$PROXY_READ_TIMEOUT"
PROXY_SEND_TIMEOUT="$PROXY_SEND_TIMEOUT"
SEND_TIMEOUT="$SEND_TIMEOUT"
PROXY_BUFFER_SIZE="$PROXY_BUFFER_SIZE"
PROXY_BUFFERING="$PROXY_BUFFERING"
PROXY_BUFFERS="$PROXY_BUFFERS"
PROXY_BUSY_BUFFERS_SIZE="$PROXY_BUSY_BUFFERS_SIZE"
PROXY_KEEPALIVE="$PROXY_KEEPALIVE"
NGINX_UNDERSCORE_IN_HEADERS="$NGINX_UNDERSCORE_IN_HEADERS"
NGINX_PORT="$PROXY_PORT" NGINX_SSL_PORT="$PROXY_SSL_PORT"
PROXY_PORT="$PROXY_PORT" PROXY_SSL_PORT="$PROXY_SSL_PORT"
PROXY_PORT_MAP="$PROXY_PORT_MAP" PROXY_UPSTREAM_PORTS="$PROXY_UPSTREAM_PORTS"
PROXY_X_FORWARDED_FOR="$PROXY_X_FORWARDED_FOR"
PROXY_X_FORWARDED_PORT="$PROXY_X_FORWARDED_PORT"
PROXY_X_FORWARDED_PROTO="$PROXY_X_FORWARDED_PROTO"
PROXY_X_FORWARDED_SSL="$PROXY_X_FORWARDED_SSL")
xargs -i echo "-----> Configuring {}...(using $NGINX_TEMPLATE_SOURCE template)" <<<"$(echo "${SSL_VHOSTS}" "${NONSSL_VHOSTS}" | tr ' ' '\n' | sort -u)"
sigil "${SIGIL_PARAMS[@]}" | cat -s >"$NGINX_CONF"
dokku_log_info1 "Creating $SCHEME nginx.conf"
mkdir -p "$DOKKU_ROOT/$APP/nginx.conf.d"
mv "$NGINX_CONF" "$DOKKU_ROOT/$APP/nginx.conf"
plugn trigger nginx-pre-reload "$APP" "$DOKKU_APP_LISTEN_PORT" "$DOKKU_APP_LISTEN_IP"
dokku_log_verbose "Reloading nginx"
validate_nginx && restart_nginx >/dev/null
fi

View File

@@ -0,0 +1,21 @@
#!/usr/bin/env bash
source "$PLUGIN_CORE_AVAILABLE_PATH/common/property-functions"
source "$PLUGIN_AVAILABLE_PATH/nginx-vhosts/functions"
set -eo pipefail
[[ $DOKKU_TRACE ]] && set -x
trigger-nginx-vhosts-post-create() {
declare desc="nginx-vhosts post-create trigger"
declare trigger="post-create"
declare APP="$1"
fn-plugin-property-setup-app "nginx" "$APP"
if [[ "$(plugn trigger proxy-type "$APP")" != "nginx" ]]; then
return
fi
nginx_build_config "$APP"
}
trigger-nginx-vhosts-post-create "$@"

View File

@@ -26,7 +26,7 @@ trigger-nginx-vhosts-proxy-build-config() {
rm -f "${DOKKU_LIB_ROOT}/data/nginx-vhosts/app-$APP/nginx.conf.sigil"
fi
else
nginx_clear_config "$APP"
nginx_build_config "$APP"
fi
}

View File

@@ -26,6 +26,7 @@ server {
}
{{ else }}
location / {
{{ if $.DOKKU_APP_WEB_LISTENERS }}
gzip on;
gzip_min_length 1100;
@@ -51,6 +52,9 @@ server {
proxy_set_header X-Forwarded-Proto {{ $.PROXY_X_FORWARDED_PROTO }};
proxy_set_header X-Request-Start $msec;
{{ if $.PROXY_X_FORWARDED_SSL }}proxy_set_header X-Forwarded-Ssl {{ $.PROXY_X_FORWARDED_SSL }};{{ end }}
{{ else }}
return 502;
{{ end }}
}
client_max_body_size {{ $.CLIENT_MAX_BODY_SIZE }};
@@ -103,6 +107,7 @@ server {
send_timeout {{ $.SEND_TIMEOUT }};
location / {
{{ if $.DOKKU_APP_WEB_LISTENERS }}
gzip on;
gzip_min_length 1100;
@@ -129,6 +134,9 @@ server {
proxy_set_header X-Forwarded-Proto {{ $.PROXY_X_FORWARDED_PROTO }};
proxy_set_header X-Request-Start $msec;
{{ if $.PROXY_X_FORWARDED_SSL }}proxy_set_header X-Forwarded-Ssl {{ $.PROXY_X_FORWARDED_SSL }};{{ end }}
{{ else }}
return 502;
{{ end }}
}
client_max_body_size {{ $.CLIENT_MAX_BODY_SIZE }};
@@ -159,6 +167,7 @@ server {
include {{ $.DOKKU_ROOT }}/{{ $.APP }}/nginx.conf.d/*.conf;
}
{{ else if eq $scheme "grpc"}}
{{ if $.DOKKU_APP_WEB_LISTENERS }}
server {
{{ if eq $.HTTP2_DIRECTIVE_SUPPORTED "true" }}
listen [{{ $.NGINX_BIND_ADDRESS_IP6 }}]:{{ $listen_port }};
@@ -179,7 +188,9 @@ server {
client_max_body_size {{ $.CLIENT_MAX_BODY_SIZE }};
include {{ $.DOKKU_ROOT }}/{{ $.APP }}/nginx.conf.d/*.conf;
}
{{ end }}
{{ else if eq $scheme "grpcs"}}
{{ if $.DOKKU_APP_WEB_LISTENERS }}
server {
{{ if eq $.HTTP2_DIRECTIVE_SUPPORTED "true" }}
listen [{{ $.NGINX_BIND_ADDRESS_IP6 }}]:{{ $listen_port }} ssl;
@@ -208,6 +219,7 @@ server {
}
{{ end }}
{{ end }}
{{ end }}
{{ if $.DOKKU_APP_WEB_LISTENERS }}
{{ range $upstream_port := $.PROXY_UPSTREAM_PORTS | split " " }}